001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.datadictionary.validation.constraint; 017 018import org.kuali.rice.krad.datadictionary.DictionaryBeanBase; 019import org.kuali.rice.krad.datadictionary.parse.BeanTag; 020import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 021import org.kuali.rice.krad.datadictionary.validator.ErrorReport; 022import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 023 024import java.util.ArrayList; 025import java.util.List; 026 027/** 028 * A class that implements the required accessor for label keys. This provides a convenient base class 029 * from which other constraints can be derived. 030 * 031 * Only BaseConstraints can have state validation. 032 * 033 * This class is a direct copy of one that was in Kuali Student. 034 * 035 * @author Kuali Rice Team (rice.collab@kuali.org) 036 * @since 1.1 037 */ 038@BeanTag(name = "constraint") 039public class BaseConstraint extends DictionaryBeanBase implements Constraint { 040 private static final long serialVersionUID = -2891712660500311114L; 041 042 protected String messageNamespaceCode; 043 protected String messageComponentCode; 044 protected String messageKey; 045 046 protected Boolean applyClientSide; 047 048 protected List<String> validationMessageParams; 049 protected List<String> states; 050 protected List<? extends BaseConstraint> constraintStateOverrides; 051 052 public BaseConstraint() { 053 applyClientSide = Boolean.valueOf(true); 054 } 055 056 /** 057 * Namespace code (often an application or module code) the constraint failure message is associated with 058 * 059 * <p> 060 * Used with the component code and error key for retrieving the constraint. If null, 061 * the default namespace code will be used 062 * </p> 063 * 064 * @return String constraint message namespace code 065 */ 066 @BeanTagAttribute(name = "messageNamespaceCode") 067 public String getMessageNamespaceCode() { 068 return messageNamespaceCode; 069 } 070 071 /** 072 * Setter for the constraint message associated namespace code 073 * 074 * @param messageNamespaceCode 075 */ 076 public void setMessageNamespaceCode(String messageNamespaceCode) { 077 this.messageNamespaceCode = messageNamespaceCode; 078 } 079 080 /** 081 * A code within the namespace that identifies a component or group the constraint message is associated with 082 * 083 * <p> 084 * Used with the namespace and error key for retrieving the constraint text. If null, 085 * the default component code will be used 086 * </p> 087 * 088 * @return String message component code 089 */ 090 @BeanTagAttribute(name = "messageComponentCode") 091 public String getMessageComponentCode() { 092 return messageComponentCode; 093 } 094 095 /** 096 * Setter for the constraint message associated component code 097 * 098 * @param messageComponentCode 099 */ 100 public void setMessageComponentCode(String messageComponentCode) { 101 this.messageComponentCode = messageComponentCode; 102 } 103 104 /** 105 * A key that is used to retrieve the constraint message text (used with the namespace and component 106 * code if specified) 107 * 108 * @return String message key 109 */ 110 @BeanTagAttribute(name = "messageKey") 111 public String getMessageKey() { 112 return messageKey; 113 } 114 115 /** 116 * Setter for the constraint message key 117 * 118 * @param messageKey 119 */ 120 public void setMessageKey(String messageKey) { 121 this.messageKey = messageKey; 122 } 123 124 /** 125 * If this is true, the constraint should be applied on the client side when the user interacts with 126 * a field - if this constraint can be interpreted for client side use. Default is true. 127 * 128 * @return the applyClientSide 129 */ 130 @BeanTagAttribute(name = "applyClientSide") 131 public Boolean getApplyClientSide() { 132 return this.applyClientSide; 133 } 134 135 /** 136 * @param applyClientSide the applyClientSide to set 137 */ 138 public void setApplyClientSide(Boolean applyClientSide) { 139 this.applyClientSide = applyClientSide; 140 } 141 142 /** 143 * Parameters to be used in the string retrieved by this constraint's messageKey, ordered by number of 144 * the param 145 * 146 * @return the validationMessageParams 147 */ 148 @BeanTagAttribute(name = "validationMessageParams", type = BeanTagAttribute.AttributeType.LISTVALUE) 149 public List<String> getValidationMessageParams() { 150 return this.validationMessageParams; 151 } 152 153 /** 154 * Parameters to be used in the string retrieved by this constraint's messageKey, ordered by number of 155 * the param 156 * 157 * @return the validationMessageParams 158 */ 159 public String[] getValidationMessageParamsArray() { 160 if (this.getValidationMessageParams() != null) { 161 return this.getValidationMessageParams().toArray(new String[this.getValidationMessageParams().size()]); 162 } else { 163 return null; 164 } 165 166 } 167 168 /** 169 * @param validationMessageParams the validationMessageParams to set 170 */ 171 public void setValidationMessageParams(List<String> validationMessageParams) { 172 this.validationMessageParams = validationMessageParams; 173 } 174 175 /** 176 * A list of states to apply this constraint for, this will effect when a constraint 177 * is applied. 178 * 179 * <p>Each state this constraint is applied for needs to be declared with few additional options: 180 * <ul> 181 * <li>if NO states are defined for this constraint, this constraint is applied for ALL states</li> 182 * <li>if a state is defined with a + symbol, example "state+", then this constraint will be applied for that state 183 * and ALL following states</li> 184 * <li>if a state is defined as a range with ">", example "state1>state6", then this constraint will be applied for 185 * all 186 * states from state1 to state6 </li> 187 * </ul> 188 * These can be mixed and matched, as appropriate, though states using a + symbol should always be the last 189 * item of a list (as they imply this state and everything else after).</p> 190 * 191 * <p>Example state list may be: ["state1", "state3>state5", "state6+"]. In this example, note that this 192 * constraint 193 * is never applied to "state2" (assuming these example states represent a state order by number)</p> 194 * 195 * @return the states to apply the constraint on, an empty list if the constraint is applied for all states 196 */ 197 @BeanTagAttribute(name = "states", type = BeanTagAttribute.AttributeType.LISTVALUE) 198 public List<String> getStates() { 199 if (states == null) { 200 states = new ArrayList<String>(); 201 } 202 return states; 203 } 204 205 /** 206 * Set the states for this contraint to be applied on 207 * 208 * @param states 209 */ 210 public void setStates(List<String> states) { 211 this.states = states; 212 } 213 214 /** 215 * Get the list of constraintStateOverrides which represent constraints that will replace THIS constraint 216 * when their state is matched during validation. 217 * Because of this, constraints added to this list MUST have their states defined. 218 * 219 * <p>ConstraintStateOverrides always take precedence over this 220 * constraint if they apply to the state being evaluated during validation. These settings have no effect if 221 * there is no stateMapping represented on the entry/view being evaluated. 222 * </p> 223 * 224 * @return List of constraint overrides for this constraint 225 */ 226 @BeanTagAttribute(name = "constraintStateOverrides", type = BeanTagAttribute.AttributeType.LISTBEAN) 227 public List<? extends BaseConstraint> getConstraintStateOverrides() { 228 return constraintStateOverrides; 229 } 230 231 /** 232 * Set the constraintStateOverrides to be used when a state is matched during validation 233 * 234 * @param constraintStateOverrides 235 */ 236 public void setConstraintStateOverrides(List<? extends BaseConstraint> constraintStateOverrides) { 237 if (constraintStateOverrides != null) { 238 for (BaseConstraint bc : constraintStateOverrides) { 239 if (!bc.getClass().equals(this.getClass())) { 240 List<Class<?>> superClasses = new ArrayList<Class<?>>(); 241 Class<?> o = bc.getClass(); 242 while (o != null && !o.equals(BaseConstraint.class)) { 243 superClasses.add(o); 244 o = o.getSuperclass(); 245 } 246 247 List<Class<?>> thisSuperClasses = new ArrayList<Class<?>>(); 248 o = this.getClass(); 249 while (o != null && !o.equals(BaseConstraint.class)) { 250 thisSuperClasses.add(o); 251 o = o.getSuperclass(); 252 } 253 superClasses.retainAll(thisSuperClasses); 254 255 if (superClasses.isEmpty()) { 256 throw new RuntimeException("Constraint State Override is not a correct type, type should be " + 257 this.getClass().toString() + " (or child/parent of that constraint type)"); 258 } 259 } 260 if (bc.getStates().isEmpty()) { 261 throw new RuntimeException( 262 "Constraint State Overrides MUST declare the states they apply to. No states" 263 + "were declared."); 264 } 265 } 266 } 267 this.constraintStateOverrides = constraintStateOverrides; 268 } 269 270 271 272 /** 273 * Validates different requirements of component compiling a series of reports detailing information on errors 274 * found in the component. Used by the RiceDictionaryValidator. 275 * 276 * @param tracer Record of component's location 277 */ 278 public void completeValidation(ValidationTrace tracer) { 279 tracer.addBean("BaseConstraint", getMessageKey()); 280 281 if (getConstraintStateOverrides() != null) { 282 for (int i = 0; i < constraintStateOverrides.size(); i++) { 283 if (constraintStateOverrides.get(i).getStates() == null) { 284 String currentValues[] = 285 {"constraintStateOverrides(" + i + ").messageKey =" + constraintStateOverrides.get(i) 286 .getMessageKey()}; 287 tracer.createError("Constraints set in State Overrides must have there states property set", 288 currentValues); 289 } 290 constraintStateOverrides.get(i).completeValidation(tracer.getCopy()); 291 } 292 } 293 294 if (getMessageKey() == null) { 295 String currentValues[] = {"messageKey =" + getMessageKey()}; 296 tracer.createWarning("Message key is not set", currentValues); 297 ErrorReport error = new ErrorReport(ErrorReport.WARNING); 298 } 299 } 300}