View Javadoc

1   /**
2    * Copyright 2005-2011 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;
17  
18  import org.apache.commons.lang.ClassUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.kuali.rice.core.api.uif.DataType;
22  import org.kuali.rice.core.api.util.ClassLoaderUtils;
23  import org.kuali.rice.core.web.format.Formatter;
24  import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
25  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
26  import org.kuali.rice.krad.datadictionary.exception.ClassValidationException;
27  import org.kuali.rice.krad.datadictionary.validation.ValidationPattern;
28  import org.kuali.rice.krad.datadictionary.validation.capability.CaseConstrainable;
29  import org.kuali.rice.krad.datadictionary.validation.capability.Formatable;
30  import org.kuali.rice.krad.datadictionary.validation.capability.HierarchicallyConstrainable;
31  import org.kuali.rice.krad.datadictionary.validation.capability.LengthConstrainable;
32  import org.kuali.rice.krad.datadictionary.validation.capability.MustOccurConstrainable;
33  import org.kuali.rice.krad.datadictionary.validation.capability.PrerequisiteConstrainable;
34  import org.kuali.rice.krad.datadictionary.validation.capability.RangeConstrainable;
35  import org.kuali.rice.krad.datadictionary.validation.capability.ValidCharactersConstrainable;
36  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
37  import org.kuali.rice.krad.datadictionary.validation.constraint.LookupConstraint;
38  import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
39  import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
40  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
41  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
42  import org.kuali.rice.krad.uif.control.Control;
43  import org.kuali.rice.krad.util.ObjectUtils;
44  
45  import java.util.List;
46  
47  /**
48   * A single attribute definition in the DataDictionary, which contains
49   * information relating to the display, validation, and general maintenance of a
50   * specific attribute of an entry.
51   * 
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  public class AttributeDefinition extends AttributeDefinitionBase implements CaseConstrainable, PrerequisiteConstrainable, Formatable, HierarchicallyConstrainable, MustOccurConstrainable, LengthConstrainable, RangeConstrainable, ValidCharactersConstrainable {
55  	private static final long serialVersionUID = -2490613377818442742L;
56  
57  	protected Boolean forceUppercase = Boolean.FALSE;
58  	
59  	protected DataType dataType;
60  	
61  	protected Integer minLength;
62  	protected Integer maxLength;
63  	protected Boolean unique;
64  
65  	protected String exclusiveMin;
66  	protected String inclusiveMax;
67  
68  	@Deprecated 
69  	protected ValidationPattern validationPattern;
70  
71  	protected ControlDefinition control;
72  
73  	// TODO: rename to control once ControlDefinition is removed
74  	protected Control controlField;
75  
76  	protected String formatterClass;
77  
78  	protected AttributeSecurity attributeSecurity;
79  	
80  	protected Boolean dynamic;
81  	
82  	// KS-style constraints 
83  	protected String customValidatorClass;
84  	protected ValidCharactersConstraint validCharactersConstraint;	
85      protected CaseConstraint caseConstraint;
86      protected List<PrerequisiteConstraint> dependencyConstraints;
87  	protected List<MustOccurConstraint> mustOccurConstraints;
88  	protected LookupConstraint lookupDefinition;// If the user wants to match
89  		// against two searches, that search must be defined as  well
90  	protected String lookupContextPath;
91  	
92  	//TODO: This may not be required since we now use ComplexAttributeDefinition
93  	protected String childEntryName;
94  	
95  	private KeyValuesFinder optionsFinder;
96  
97  	protected String alternateDisplayAttributeName;
98      protected String additionalDisplayAttributeName;
99      
100 
101 	public AttributeDefinition() {
102 		// Empty
103 	}
104 
105 	/**
106 	 * forceUppercase = convert user entry to uppercase and always display
107 	 * database value as uppercase.
108 	 */
109 	public void setForceUppercase(Boolean forceUppercase) {
110 		this.forceUppercase = forceUppercase;
111 	}
112 
113 	public Boolean getForceUppercase() {
114 		return this.forceUppercase;
115 	}
116 
117 	@Override
118 	public Integer getMaxLength() {
119 		return maxLength;
120 	}
121 
122 	/**
123 	 * The maxLength element determines the maximum size of the field for data
124 	 * entry edit purposes and for display purposes.
125 	 */
126 	public void setMaxLength(Integer maxLength) {
127 		this.maxLength = maxLength;
128 	}
129 
130 	@Override
131 	public String getExclusiveMin() {
132 		return exclusiveMin;
133 	}
134 
135 	/**
136 	 * The exclusiveMin element determines the minimum allowable value for data
137 	 * entry editing purposes. Value can be an integer or decimal value such as
138 	 * -.001 or 99.
139 	 */
140 	public void setExclusiveMin(String exclusiveMin) {
141 		this.exclusiveMin = exclusiveMin;
142 	}
143 
144 	/**
145 	 * The inclusiveMax element determines the maximum allowable value for data
146 	 * entry editing purposes. Value can be an integer or decimal value such as
147 	 * -.001 or 99.
148 	 * 
149 	 * JSTL: This field is mapped into the field named "exclusiveMax".
150 	 */
151 	@Override
152 	public String getInclusiveMax() {
153 		return inclusiveMax;
154 	}
155 
156 	/**
157 	 * The inclusiveMax element determines the maximum allowable value for data
158 	 * entry editing purposes. Value can be an integer or decimal value such as
159 	 * -.001 or 99.
160 	 * 
161 	 * JSTL: This field is mapped into the field named "exclusiveMax".
162 	 */
163 	public void setInclusiveMax(String inclusiveMax) {
164 		this.inclusiveMax = inclusiveMax;
165 	}
166 
167 	/**
168 	 * @return true if a validationPattern has been set
169 	 */
170 	public boolean hasValidationPattern() {
171 		return (validationPattern != null);
172 	}
173 
174 	public ValidationPattern getValidationPattern() {
175 		return this.validationPattern;
176 	}
177 
178 	/**
179 	 * The validationPattern element defines the allowable character-level or
180 	 * field-level values for an attribute.
181 	 * 
182 	 * JSTL: validationPattern is a Map which is accessed using a key of
183 	 * "validationPattern". Each entry may contain some of the keys listed
184 	 * below. The keys that may be present for a given attribute are dependent
185 	 * upon the type of validationPattern.
186 	 * 
187 	 * maxLength (String) exactLength type allowWhitespace allowUnderscore
188 	 * allowPeriod validChars precision scale allowNegative
189 	 * 
190 	 * The allowable keys (in addition to type) for each type are: Type****
191 	 * ***Keys*** alphanumeric exactLength maxLength allowWhitespace
192 	 * allowUnderscore allowPeriod
193 	 * 
194 	 * alpha exactLength maxLength allowWhitespace
195 	 * 
196 	 * anyCharacter exactLength maxLength allowWhitespace
197 	 * 
198 	 * charset validChars
199 	 * 
200 	 * numeric exactLength maxLength
201 	 * 
202 	 * fixedPoint allowNegative precision scale
203 	 * 
204 	 * floatingPoint allowNegative
205 	 * 
206 	 * date n/a emailAddress n/a javaClass n/a month n/a phoneNumber n/a
207 	 * timestamp n/a year n/a zipcode n/a
208 	 * 
209 	 * Note: maxLength and exactLength are mutually exclusive. If one is
210 	 * entered, the other may not be entered.
211 	 * 
212 	 * Note: See ApplicationResources.properties for exact regex patterns. e.g.
213 	 * validationPatternRegex.date for regex used in date validation.
214 	 */
215 	public void setValidationPattern(ValidationPattern validationPattern) {
216 		this.validationPattern = validationPattern;
217 		
218 		// FIXME: JLR - need to recreate this functionality using the ValidCharsConstraint logic
219 	}
220 
221 
222 	/**
223 	 * @return control
224 	 */
225 	public ControlDefinition getControl() {
226 		return control;
227 	}
228 
229 	/**
230 	 * The control element defines the manner in which an attribute is displayed
231 	 * and the manner in which the attribute value is entered.
232 	 * 
233 	 * JSTL: control is a Map representing an HTML control. It is accessed using
234 	 * a key of "control". The table below shows the types of entries associated
235 	 * with each type of control.
236 	 * 
237 	 ** Control Type** **Key** **Value** checkbox checkbox boolean String
238 	 * 
239 	 * hidden hidden boolean String
240 	 * 
241 	 * radio radio boolean String valuesFinder valuesFinder class name
242 	 * dataObjectClass String keyAttribute String labelAttribute String
243 	 * includeKeyInLabel boolean String
244 	 * 
245 	 * select select boolean String valuesFinder valuesFinder class name
246 	 * dataObjectClass String keyAttribute String labelAttribute String
247 	 * includeBlankRow boolean String includeKeyInLabel boolean String
248 	 * 
249 	 * apcSelect apcSelect boolean String paramNamespace String
250 	 * parameterDetailType String parameterName String
251 	 * 
252 	 * text text boolean String size String
253 	 * 
254 	 * textarea textarea boolean String rows cols
255 	 * 
256 	 * currency currency boolean String size String formattedMaxLength String
257 	 * 
258 	 * kualiUser kualiUser boolean String universalIdAttributeName String
259 	 * userIdAttributeName String personNameAttributeName String
260 	 * 
261 	 * lookupHidden lookupHidden boolean String
262 	 * 
263 	 * lookupReadonly lookupReadonly boolean String
264 	 * 
265 	 * @param control
266 	 * @throws IllegalArgumentException
267 	 *             if the given control is null
268 	 */
269 	public void setControl(ControlDefinition control) {
270 		if (control == null) {
271 			throw new IllegalArgumentException("invalid (null) control");
272 		}
273 		this.control = control;
274 	}
275 
276 	public boolean hasFormatterClass() {
277 		return (formatterClass != null);
278 	}
279 
280 	@Override
281 	public String getFormatterClass() {
282 		return formatterClass;
283 	}
284 
285 	/**
286 	 * The formatterClass element is used when custom formatting is required for
287 	 * display of the field value. This field specifies the name of the java
288 	 * class to be used for the formatting. About 15 different classes are
289 	 * available including BooleanFormatter, CurrencyFormatter, DateFormatter,
290 	 * etc.
291 	 */
292 	public void setFormatterClass(String formatterClass) {
293 		if (formatterClass == null) {
294 			throw new IllegalArgumentException("invalid (null) formatterClass");
295 		}
296 		this.formatterClass = formatterClass;
297 	}
298 
299 	/**
300 	 * Directly validate simple fields, call completeValidation on Definition
301 	 * fields.
302 	 * 
303 	 * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
304 	 */
305 	@Override
306 	public void completeValidation(Class<?> rootObjectClass, Class<?> otherObjectClass) {
307 		try {
308 			if (!DataDictionary.isPropertyOf(rootObjectClass, getName())) {
309 				throw new AttributeValidationException("property '" + getName() + "' is not a property of class '"
310 						+ rootObjectClass.getName() + "' (" + "" + ")");
311 			}
312 
313 			//TODO currently requiring a control or controlField, but this should not be case (AttrField should probably do the check)
314 			if (getControl() == null && getControlField() == null) {
315 				throw new AttributeValidationException("property '" + getName() + "' in class '"
316 						+ rootObjectClass.getName() + " does not have a control defined");
317 			}
318 			
319 			if(getControl() != null) {
320 			    getControl().completeValidation(rootObjectClass, otherObjectClass);
321 			}
322 
323 			if (attributeSecurity != null) {
324 				attributeSecurity.completeValidation(rootObjectClass, otherObjectClass);
325 			}
326 
327 			if (validationPattern != null) {
328 				validationPattern.completeValidation();
329 			}
330 
331 			if (formatterClass != null) {
332 				try {
333 					Class formatterClassObject = ClassUtils.getClass(ClassLoaderUtils.getDefaultClassLoader(),
334 							getFormatterClass());
335 					if (!Formatter.class.isAssignableFrom(formatterClassObject)) {
336 						throw new ClassValidationException("formatterClass is not a valid instance of "
337 								+ Formatter.class.getName() + " instead was: " + formatterClassObject.getName());
338 					}
339 				}
340 				catch (ClassNotFoundException e) {
341 					throw new ClassValidationException("formatterClass could not be found: " + getFormatterClass(), e);
342 				}
343 			}
344 		}
345 		catch (RuntimeException ex) {
346 			Logger.getLogger(getClass()).error(
347 					"Unable to validate attribute " + rootObjectClass + "." + getName() + ": " + ex.getMessage(), ex);
348 			throw ex;
349 		}
350 	}
351 
352 	/**
353 	 * @see java.lang.Object#toString()
354 	 */
355 	@Override
356 	public String toString() {
357 		return "AttributeDefinition for attribute " + getName();
358 	}
359 
360 	/**
361 	 * @return the attributeSecurity
362 	 */
363 	public AttributeSecurity getAttributeSecurity() {
364 		return this.attributeSecurity;
365 	}
366 
367 	/**
368 	 * @param attributeSecurity
369 	 *            the attributeSecurity to set
370 	 */
371 	public void setAttributeSecurity(AttributeSecurity attributeSecurity) {
372 		this.attributeSecurity = attributeSecurity;
373 	}
374 
375 	public boolean hasAttributeSecurity() {
376 		return (attributeSecurity != null);
377 	}
378 
379 	/**
380 	 * This overridden method ...
381 	 * 
382 	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
383 	 */
384 	@Override
385 	public void afterPropertiesSet() throws Exception {
386 		if (StringUtils.isEmpty(name)) {
387 			throw new RuntimeException("blank name for bean: " + id);
388 		}
389 	}
390 
391 	/**
392 	 * @return the unique
393 	 */
394 	public Boolean getUnique() {
395 		return this.unique;
396 	}
397 
398 	/**
399 	 * @param unique
400 	 *            the unique to set
401 	 */
402 	public void setUnique(Boolean unique) {
403 		this.unique = unique;
404 	}
405 
406 	/**
407 	 * Default <code>Control</code> to use when the attribute is to be rendered
408 	 * for the UI. Used by the UIF when a control is not defined for an
409 	 * <code>InputField</code>
410 	 * 
411 	 * @return Control instance
412 	 */
413 	public Control getControlField() {
414 		return this.controlField;
415 	}
416 
417 	/**
418 	 * Setter for the default control
419 	 * 
420 	 * @param controlField
421 	 */
422 	public void setControlField(Control controlField) {
423 		this.controlField = controlField;
424 	}
425 
426 	/**
427 	 * @return the minLength
428 	 */
429 	public Integer getMinLength() {
430 		return this.minLength;
431 	}
432 
433 	/**
434 	 * @param minLength the minLength to set
435 	 */
436 	public void setMinLength(Integer minLength) {
437 		this.minLength = minLength;
438 	}
439 
440 	/**
441 	 * @return the dataType
442 	 */
443 	@Override
444 	public DataType getDataType() {
445 		return this.dataType;
446 	}
447 
448 	/**
449 	 * @param dataType the dataType to set
450 	 */
451 	public void setDataType(DataType dataType) {
452 		this.dataType = dataType;
453 	}
454 	
455 	public void setDataType(String dataType) {
456 		this.dataType = DataType.valueOf(dataType);
457 	}
458 
459 	/**
460 	 * @return the customValidatorClass
461 	 */
462 	public String getCustomValidatorClass() {
463 		return this.customValidatorClass;
464 	}
465 
466 	/**
467 	 * @param customValidatorClass the customValidatorClass to set
468 	 */
469 	public void setCustomValidatorClass(String customValidatorClass) {
470 		this.customValidatorClass = customValidatorClass;
471 	}
472 
473 	/**
474 	 * @return the validChars
475 	 */
476 	@Override
477 	public ValidCharactersConstraint getValidCharactersConstraint() {
478 		return this.validCharactersConstraint;
479 	}
480 
481 	/**
482 	 * @param validCharactersConstraint the validChars to set
483 	 */
484 	public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
485 		this.validCharactersConstraint = validCharactersConstraint;
486 	}
487 
488 	/**
489 	 * @return the caseConstraint
490 	 */
491 	@Override
492 	public CaseConstraint getCaseConstraint() {
493 		return this.caseConstraint;
494 	}
495 
496 	/**
497 	 * @param caseConstraint the caseConstraint to set
498 	 */
499 	public void setCaseConstraint(CaseConstraint caseConstraint) {
500 		this.caseConstraint = caseConstraint;
501 	}
502 
503 	/**
504 	 * @return the requireConstraint
505 	 */
506 	@Override
507 	public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
508 		return this.dependencyConstraints;
509 	}
510 
511 	/**
512 	 * @param dependencyConstraints the requireConstraint to set
513 	 */
514 	public void setPrerequisiteConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
515 		this.dependencyConstraints = dependencyConstraints;
516 	}
517 
518 	/**
519 	 * @return the occursConstraint
520 	 */
521 	@Override
522 	public List<MustOccurConstraint> getMustOccurConstraints() {
523 		return this.mustOccurConstraints;
524 	}
525 
526 	/**
527 	 * @param mustOccurConstraints the occursConstraint to set
528 	 */
529 	public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
530 		this.mustOccurConstraints = mustOccurConstraints;
531 	}
532 
533 	/**
534 	 * @return the lookupDefinition
535 	 */
536 	public LookupConstraint getLookupDefinition() {
537 		return this.lookupDefinition;
538 	}
539 
540 	/**
541 	 * @param lookupDefinition the lookupDefinition to set
542 	 */
543 	public void setLookupDefinition(LookupConstraint lookupDefinition) {
544 		this.lookupDefinition = lookupDefinition;
545 	}
546 
547 	/**
548 	 * @return the lookupContextPath
549 	 */
550 	public String getLookupContextPath() {
551 		return this.lookupContextPath;
552 	}
553 
554 	/**
555 	 * @param lookupContextPath the lookupContextPath to set
556 	 */
557 	public void setLookupContextPath(String lookupContextPath) {
558 		this.lookupContextPath = lookupContextPath;
559 	}
560 
561 	/**
562 	 * @return the childEntryName
563 	 */
564 	public String getChildEntryName() {
565 		return this.childEntryName;
566 	}
567 
568 	/**
569 	 * @param childEntryName the childEntryName to set
570 	 */
571 	public void setChildEntryName(String childEntryName) {
572 		this.childEntryName = childEntryName;
573 	}
574 
575 
576 	
577     /**
578      * Instance of <code>KeyValluesFinder</code> that should be invoked to
579      * provide a List of values the field can have. Generally used to provide
580      * the options for a multi-value control or to validate the submitted field
581      * value
582      * 
583      * @return KeyValuesFinder instance
584      */
585     public KeyValuesFinder getOptionsFinder() {
586         return this.optionsFinder;
587     }
588 
589     /**
590      * Setter for the field's KeyValuesFinder instance
591      * 
592      * @param optionsFinder
593      */
594     public void setOptionsFinder(KeyValuesFinder optionsFinder) {
595         this.optionsFinder = optionsFinder;
596     }
597     
598     /**
599      * Setter that takes in the class name for the options finder and creates a
600      * new instance to use as the finder for the attribute field
601      * 
602      * @param optionsFinderClass
603      */
604     public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
605         this.optionsFinder = ObjectUtils.newInstance(optionsFinderClass);
606     }
607 	
608 	public void setAdditionalDisplayAttributeName(String additionalDisplayAttributeName) {
609 		this.additionalDisplayAttributeName = additionalDisplayAttributeName;
610 	}
611 	
612 	public String getAdditionalDisplayAttributeName() {
613 		return this.additionalDisplayAttributeName;
614 	}
615 
616 	public void setAlternateDisplayAttributeName(String alternateDisplayAttributeName) {
617 		this.alternateDisplayAttributeName = alternateDisplayAttributeName;
618 	}
619 	
620 	public String getAlternateDisplayAttributeName() {
621 		return this.alternateDisplayAttributeName;
622 	}
623 
624 }