View Javadoc

1   /**
2    * Copyright 2005-2012 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.parse.BeanTag;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
20  import org.kuali.rice.krad.datadictionary.validator.ErrorReport;
21  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  /**
27   * A class that implements the required accessor for label keys. This provides a convenient base class
28   * from which other constraints can be derived.
29   *
30   * Only BaseConstraints can have state validation.
31   *
32   * This class is a direct copy of one that was in Kuali Student.
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   * @since 1.1
36   */
37  @BeanTag(name = "baseConstraint")
38  public class BaseConstraint implements Constraint {
39      protected String messageNamespaceCode;
40      protected String messageComponentCode;
41      protected String messageKey;
42  
43      protected Boolean applyClientSide;
44  
45      protected List<String> validationMessageParams;
46      protected List<String> states;
47      protected List<? extends BaseConstraint> constraintStateOverrides;
48  
49      public BaseConstraint() {
50          applyClientSide = Boolean.valueOf(true);
51      }
52  
53      /**
54       * Namespace code (often an application or module code) the constraint failure message is associated with
55       *
56       * <p>
57       * Used with the component code and error key for retrieving the constraint. If null,
58       * the default namespace code will be used
59       * </p>
60       *
61       * @return String constraint message namespace code
62       */
63      @BeanTagAttribute(name = "messageNamespaceCode")
64      public String getMessageNamespaceCode() {
65          return messageNamespaceCode;
66      }
67  
68      /**
69       * Setter for the constraint message associated namespace code
70       *
71       * @param messageNamespaceCode
72       */
73      public void setMessageNamespaceCode(String messageNamespaceCode) {
74          this.messageNamespaceCode = messageNamespaceCode;
75      }
76  
77      /**
78       * A code within the namespace that identifies a component or group the constraint message is associated with
79       *
80       * <p>
81       * Used with the namespace and error key for retrieving the constraint text. If null,
82       * the default component code will be used
83       * </p>
84       *
85       * @return String message component code
86       */
87      @BeanTagAttribute(name = "messageComponentCode")
88      public String getMessageComponentCode() {
89          return messageComponentCode;
90      }
91  
92      /**
93       * Setter for the constraint message associated component code
94       *
95       * @param messageComponentCode
96       */
97      public void setMessageComponentCode(String messageComponentCode) {
98          this.messageComponentCode = messageComponentCode;
99      }
100 
101     /**
102      * A key that is used to retrieve the constraint message text (used with the namespace and component
103      * code if specified)
104      *
105      * @return String message key
106      */
107     @BeanTagAttribute(name = "messageKey")
108     public String getMessageKey() {
109         return messageKey;
110     }
111 
112     /**
113      * Setter for the constraint message key
114      *
115      * @param messageKey
116      */
117     public void setMessageKey(String messageKey) {
118         this.messageKey = messageKey;
119     }
120 
121     /**
122      * If this is true, the constraint should be applied on the client side when the user interacts with
123      * a field - if this constraint can be interpreted for client side use. Default is true.
124      *
125      * @return the applyClientSide
126      */
127     @BeanTagAttribute(name = "applyClientSide")
128     public Boolean getApplyClientSide() {
129         return this.applyClientSide;
130     }
131 
132     /**
133      * @param applyClientSide the applyClientSide to set
134      */
135     public void setApplyClientSide(Boolean applyClientSide) {
136         this.applyClientSide = applyClientSide;
137     }
138 
139     /**
140      * Parameters to be used in the string retrieved by this constraint's messageKey, ordered by number of
141      * the param
142      *
143      * @return the validationMessageParams
144      */
145     @BeanTagAttribute(name = "validationMessageParams", type = BeanTagAttribute.AttributeType.LISTVALUE)
146     public List<String> getValidationMessageParams() {
147         return this.validationMessageParams;
148     }
149 
150     /**
151      * Parameters to be used in the string retrieved by this constraint's messageKey, ordered by number of
152      * the param
153      *
154      * @return the validationMessageParams
155      */
156     public String[] getValidationMessageParamsArray() {
157         if (this.getValidationMessageParams() != null) {
158             return this.getValidationMessageParams().toArray(new String[this.getValidationMessageParams().size()]);
159         } else {
160             return null;
161         }
162 
163     }
164 
165     /**
166      * @param validationMessageParams the validationMessageParams to set
167      */
168     public void setValidationMessageParams(List<String> validationMessageParams) {
169         this.validationMessageParams = validationMessageParams;
170     }
171 
172     /**
173      * A list of states to apply this constraint for, this will effect when a constraint
174      * is applied.
175      *
176      * <p>Each state this constraint is applied for needs to be declared with few additional options:
177      * <ul>
178      * <li>if NO states are defined for this constraint, this constraint is applied for ALL states</li>
179      * <li>if a state is defined with a + symbol, example "state+", then this constraint will be applied for that state
180      * and ALL following states</li>
181      * <li>if a state is defined as a range with ">", example "state1>state6", then this constraint will be applied for
182      * all
183      * states from state1 to state6 </li>
184      * </ul>
185      * These can be mixed and matched, as appropriate, though states using a + symbol should always be the last
186      * item of a list (as they imply this state and everything else after).</p>
187      *
188      * <p>Example state list may be: ["state1", "state3>state5", "state6+"].  In this example, note that this
189      * constraint
190      * is never applied to "state2" (assuming these example states represent a state order by number)</p>
191      *
192      * @return the states to apply the constraint on, an empty list if the constraint is applied for all states
193      */
194     @BeanTagAttribute(name = "states", type = BeanTagAttribute.AttributeType.LISTVALUE)
195     public List<String> getStates() {
196         if (states == null) {
197             states = new ArrayList<String>();
198         }
199         return states;
200     }
201 
202     /**
203      * Set the states for this contraint to be applied on
204      *
205      * @param states
206      */
207     public void setStates(List<String> states) {
208         this.states = states;
209     }
210 
211     /**
212      * Get the list of constraintStateOverrides which represent constraints that will replace THIS constraint
213      * when their state is matched during validation.
214      * Because of this, constraints added to this list MUST have their states defined.
215      *
216      * <p>ConstraintStateOverrides always take precedence over this
217      * constraint if they apply to the state being evaluated during validation.  These settings have no effect if
218      * there is no stateMapping represented on the entry/view being evaluated.
219      * </p>
220      *
221      * @return List of constraint overrides for this constraint
222      */
223     @BeanTagAttribute(name = "constraintStateOverrides", type = BeanTagAttribute.AttributeType.LISTBEAN)
224     public List<? extends BaseConstraint> getConstraintStateOverrides() {
225         return constraintStateOverrides;
226     }
227 
228     /**
229      * Set the constraintStateOverrides to be used when a state is matched during validation
230      *
231      * @param constraintStateOverrides
232      */
233     public void setConstraintStateOverrides(List<? extends BaseConstraint> constraintStateOverrides) {
234         if (constraintStateOverrides != null) {
235             for (BaseConstraint bc : constraintStateOverrides) {
236                 if (!bc.getClass().equals(this.getClass())) {
237                     List<Class<?>> superClasses = new ArrayList<Class<?>>();
238                     Class<?> o = bc.getClass();
239                     while (o != null && !o.equals(BaseConstraint.class)) {
240                         superClasses.add(o);
241                         o = o.getSuperclass();
242                     }
243 
244                     List<Class<?>> thisSuperClasses = new ArrayList<Class<?>>();
245                     o = this.getClass();
246                     while (o != null && !o.equals(BaseConstraint.class)) {
247                         thisSuperClasses.add(o);
248                         o = o.getSuperclass();
249                     }
250                     superClasses.retainAll(thisSuperClasses);
251 
252                     if (superClasses.isEmpty()) {
253                         throw new RuntimeException("Constraint State Override is not a correct type, type should be " +
254                                 this.getClass().toString() + " (or child/parent of that constraint type)");
255                     }
256                 }
257                 if (bc.getStates().isEmpty()) {
258                     throw new RuntimeException(
259                             "Constraint State Overrides MUST declare the states they apply to.  No states"
260                                     + "were declared.");
261                 }
262             }
263         }
264         this.constraintStateOverrides = constraintStateOverrides;
265     }
266 
267     /**
268      * Validates different requirements of component compiling a series of reports detailing information on errors
269      * found in the component.  Used by the RiceDictionaryValidator.
270      *
271      * @param tracer Record of component's location
272      */
273     public void completeValidation(ValidationTrace tracer) {
274         tracer.addBean("BaseConstraint", getMessageKey());
275 
276         if (getConstraintStateOverrides() != null) {
277             for (int i = 0; i < constraintStateOverrides.size(); i++) {
278                 if (constraintStateOverrides.get(i).getStates() == null) {
279                     String currentValues[] =
280                             {"constraintStateOverrides(" + i + ").messageKey =" + constraintStateOverrides.get(i)
281                                     .getMessageKey()};
282                     tracer.createError("Constraints set in State Overrides must have there states property set",
283                             currentValues);
284                 }
285                 constraintStateOverrides.get(i).completeValidation(tracer.getCopy());
286             }
287         }
288 
289         if (getMessageKey() == null) {
290             String currentValues[] = {"messageKey =" + getMessageKey()};
291             tracer.createWarning("Message key is not set", currentValues);
292             ErrorReport error = new ErrorReport(ErrorReport.WARNING);
293         }
294     }
295 }