View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.datadictionary.validation.constraint;
17  
18  import org.kuali.rice.krad.datadictionary.DictionaryBeanBase;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21  import org.kuali.rice.krad.datadictionary.validator.ErrorReport;
22  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
23  
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  /**
28   * A class that implements the required accessor for label keys. This provides a convenient base class
29   * from which other constraints can be derived.
30   *
31   * Only BaseConstraints can have state validation.
32   *
33   * This class is a direct copy of one that was in Kuali Student.
34   *
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   * @since 1.1
37   */
38  @BeanTag(name = "constraint")
39  public class BaseConstraint extends DictionaryBeanBase implements Constraint {
40      private static final long serialVersionUID = -2891712660500311114L;
41  
42      protected String messageNamespaceCode;
43      protected String messageComponentCode;
44      protected String messageKey;
45  
46      protected Boolean applyClientSide;
47  
48      protected List<String> validationMessageParams;
49      protected List<String> states;
50      protected List<? extends BaseConstraint> constraintStateOverrides;
51  
52      public BaseConstraint() {
53          applyClientSide = Boolean.valueOf(true);
54      }
55  
56      /**
57       * Namespace code (often an application or module code) the constraint failure message is associated with
58       *
59       * <p>
60       * Used with the component code and error key for retrieving the constraint. If null,
61       * the default namespace code will be used
62       * </p>
63       *
64       * @return String constraint message namespace code
65       */
66      @BeanTagAttribute(name = "messageNamespaceCode")
67      public String getMessageNamespaceCode() {
68          return messageNamespaceCode;
69      }
70  
71      /**
72       * Setter for the constraint message associated namespace code
73       *
74       * @param messageNamespaceCode
75       */
76      public void setMessageNamespaceCode(String messageNamespaceCode) {
77          this.messageNamespaceCode = messageNamespaceCode;
78      }
79  
80      /**
81       * A code within the namespace that identifies a component or group the constraint message is associated with
82       *
83       * <p>
84       * Used with the namespace and error key for retrieving the constraint text. If null,
85       * the default component code will be used
86       * </p>
87       *
88       * @return String message component code
89       */
90      @BeanTagAttribute(name = "messageComponentCode")
91      public String getMessageComponentCode() {
92          return messageComponentCode;
93      }
94  
95      /**
96       * Setter for the constraint message associated component code
97       *
98       * @param messageComponentCode
99       */
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 }