View Javadoc

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