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;
17  
18  import org.apache.commons.lang.ClassUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.core.api.uif.DataType;
21  import org.kuali.rice.core.api.util.ClassLoaderUtils;
22  import org.kuali.rice.core.web.format.Formatter;
23  import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
24  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
25  import org.kuali.rice.krad.datadictionary.exception.ClassValidationException;
26  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
27  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
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.MustOccurConstrainable;
33  import org.kuali.rice.krad.datadictionary.validation.capability.PrerequisiteConstrainable;
34  import org.kuali.rice.krad.datadictionary.validation.capability.ValidCharactersConstrainable;
35  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
36  import org.kuali.rice.krad.datadictionary.validation.constraint.LookupConstraint;
37  import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
38  import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
39  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
40  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
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.beans.PropertyEditor;
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  @BeanTag(name = "attributeDefinition")
56  public class AttributeDefinition extends AttributeDefinitionBase implements CaseConstrainable, PrerequisiteConstrainable, Formatable, HierarchicallyConstrainable, MustOccurConstrainable, ValidCharactersConstrainable {
57      private static final long serialVersionUID = -2490613377818442742L;
58  
59      protected Boolean forceUppercase = Boolean.FALSE;
60  
61      protected DataType dataType;
62  
63      protected Boolean unique;
64  
65      //These are deprecated DO NOT USE with new KRAD implementations
66      @Deprecated
67      protected ValidationPattern validationPattern;
68  
69      protected ControlDefinition control;
70  
71      // TODO: rename to control once ControlDefinition is removed
72      protected Control controlField;
73  
74      protected String formatterClass;
75      protected PropertyEditor propertyEditor;
76  
77      protected AttributeSecurity attributeSecurity;
78  
79      protected Boolean dynamic;
80  
81      // KRAD constraints
82      protected String customValidatorClass;
83      protected ValidCharactersConstraint validCharactersConstraint;
84      protected CaseConstraint caseConstraint;
85      protected List<PrerequisiteConstraint> dependencyConstraints;
86      protected List<MustOccurConstraint> mustOccurConstraints;
87      // if the user wants to match against two searches, that search must be defined as well
88      protected LookupConstraint lookupDefinition;
89      protected String lookupContextPath;
90  
91      //TODO: This may not be required since we now use ComplexAttributeDefinition
92      protected String childEntryName;
93  
94      private KeyValuesFinder optionsFinder;
95  
96      protected String alternateDisplayAttributeName;
97      protected String additionalDisplayAttributeName;
98  
99      public AttributeDefinition() {
100         super();
101     }
102 
103     /**
104      * Setter for force upper case
105      *
106      * @param forceUppercase
107      */
108     public void setForceUppercase(Boolean forceUppercase) {
109         this.forceUppercase = forceUppercase;
110     }
111 
112     /**
113      * Indicates whether user entry should be converted to upper case
114      *
115      * <p>
116      * If set all user input will be changed to uppercase. Values from the database will also be forced to display
117      * as upper case and thus be persisted as upper case.
118      * </p>
119      *
120      * @return boolean true if force upper case is set
121      */
122     @BeanTagAttribute(name = "forceUppercase")
123     public Boolean getForceUppercase() {
124         return this.forceUppercase;
125     }
126 
127     /**
128      * @see org.kuali.rice.krad.datadictionary.validation.constraint.LengthConstraint#getMaxLength()
129      */
130     @BeanTagAttribute(name = "maxLength")
131     public Integer getMaxLength() {
132         return this.getSimpleConstraint().getMaxLength();
133     }
134 
135     /**
136      * Setter for maximum length
137      *
138      * @param maxLength
139      */
140     public void setMaxLength(Integer maxLength) {
141         this.getSimpleConstraint().setMaxLength(maxLength);
142     }
143 
144     /**
145      * @see org.kuali.rice.krad.datadictionary.validation.constraint.RangeConstraint#getExclusiveMin()
146      */
147     @BeanTagAttribute(name = "exclusiveMin")
148     public String getExclusiveMin() {
149         return this.getSimpleConstraint().getExclusiveMin();
150     }
151 
152     /**
153      * Setter for minimum value
154      *
155      * @param exclusiveMin - minimum allowed value
156      */
157     public void setExclusiveMin(String exclusiveMin) {
158         this.getSimpleConstraint().setExclusiveMin(exclusiveMin);
159     }
160 
161     /**
162      * @see org.kuali.rice.krad.datadictionary.validation.constraint.RangeConstraint#getInclusiveMax()
163      */
164     @BeanTagAttribute(name = "inclusiveMax")
165     public String getInclusiveMax() {
166         return this.getSimpleConstraint().getInclusiveMax();
167     }
168 
169     /**
170      * Setter for maximum value
171      *
172      * @param inclusiveMax - max allowed value
173      */
174     public void setInclusiveMax(String inclusiveMax) {
175         this.getSimpleConstraint().setInclusiveMax(inclusiveMax);
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 
219     /**
220      * Defines the allowable character-level or
221      * field-level values for an attribute
222      *
223      * <p>
224      * ValidationPattern is a Map which is accessed using a key of "validationPattern". Each entry may contain
225      * some of the keys listed below. The keys that may be present for a given attribute are dependent
226      * upon the type of validationPattern.
227      *
228      * maxLength (String) exactLength type allowWhitespace allowUnderscore
229      * allowPeriod validChars precision scale allowNegative
230      *
231      * The allowable keys (in addition to type) for each type are: Type****
232      * ***Keys*** alphanumeric exactLength maxLength allowWhitespace
233      * allowUnderscore allowPeriod
234      *
235      * alpha exactLength maxLength allowWhitespace
236      *
237      * anyCharacter exactLength maxLength allowWhitespace
238      *
239      * charset validChars
240      *
241      * numeric exactLength maxLength
242      *
243      * fixedPoint allowNegative precision scale
244      *
245      * floatingPoint allowNegative
246      *
247      * date n/a emailAddress n/a javaClass n/a month n/a phoneNumber n/a
248      * timestamp n/a year n/a zipcode n/a
249      *
250      * Note: maxLength and exactLength are mutually exclusive. If one is
251      * entered, the other may not be entered.
252      *
253      * Note: See ApplicationResources.properties for exact regex patterns. e.g.
254      * validationPatternRegex.date for regex used in date validation.
255      * </p>
256      *
257      * @return ValidationPattern
258      */
259     public ValidationPattern getValidationPattern() {
260         return this.validationPattern;
261     }
262 
263     /**
264      * @return control
265      */
266     @BeanTagAttribute(name = "oldControl", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
267     public ControlDefinition getControl() {
268         return control;
269     }
270 
271     /**
272      * The control element defines the manner in which an attribute is displayed
273      * and the manner in which the attribute value is entered.
274      *
275      * JSTL: control is a Map representing an HTML control. It is accessed using
276      * a key of "control". The table below shows the types of entries associated
277      * with each type of control.
278      *
279      * * Control Type** **Key** **Value** checkbox checkbox boolean String
280      *
281      * hidden hidden boolean String
282      *
283      * radio radio boolean String valuesFinder valuesFinder class name
284      * dataObjectClass String keyAttribute String labelAttribute String
285      * includeKeyInLabel boolean String
286      *
287      * select select boolean String valuesFinder valuesFinder class name
288      * dataObjectClass String keyAttribute String labelAttribute String
289      * includeBlankRow boolean String includeKeyInLabel boolean String
290      *
291      * apcSelect apcSelect boolean String paramNamespace String
292      * parameterDetailType String parameterName String
293      *
294      * text text boolean String size String
295      *
296      * textarea textarea boolean String rows cols
297      *
298      * currency currency boolean String size String formattedMaxLength String
299      *
300      * kualiUser kualiUser boolean String universalIdAttributeName String
301      * userIdAttributeName String personNameAttributeName String
302      *
303      * lookupHidden lookupHidden boolean String
304      *
305      * lookupReadonly lookupReadonly boolean String
306      *
307      * @param control
308      * @throws IllegalArgumentException if the given control is null
309      */
310     public void setControl(ControlDefinition control) {
311         if (control == null) {
312             throw new IllegalArgumentException("invalid (null) control");
313         }
314         this.control = control;
315     }
316 
317     public boolean hasFormatterClass() {
318         return (formatterClass != null);
319     }
320 
321     @Override
322     @BeanTagAttribute(name = "formatterClass")
323     public String getFormatterClass() {
324         return formatterClass;
325     }
326 
327     /**
328      * The formatterClass element is used when custom formatting is required for
329      * display of the field value. This field specifies the name of the java
330      * class to be used for the formatting. About 15 different classes are
331      * available including BooleanFormatter, CurrencyFormatter, DateFormatter,
332      * etc.
333      */
334     public void setFormatterClass(String formatterClass) {
335         if (formatterClass == null) {
336             throw new IllegalArgumentException("invalid (null) formatterClass");
337         }
338         this.formatterClass = formatterClass;
339     }
340 
341     /**
342      * Performs formatting of the field value for display and then converting the value back to its
343      * expected type from a string
344      *
345      * <p>
346      * Note property editors exist and are already registered for the basic Java types and the
347      * common Kuali types such as [@link KualiDecimal}. Registration with this property is only
348      * needed for custom property editors
349      * </p>
350      *
351      * @return PropertyEditor property editor instance to use for this field
352      */
353     @BeanTagAttribute(name = "propertyEditor", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
354     public PropertyEditor getPropertyEditor() {
355         return propertyEditor;
356     }
357 
358     /**
359      * Setter for the custom property editor to use for the field
360      *
361      * @param propertyEditor
362      */
363     public void setPropertyEditor(PropertyEditor propertyEditor) {
364         this.propertyEditor = propertyEditor;
365     }
366 
367     /**
368      * Convenience setter for configuring a property editor by class
369      *
370      * @param propertyEditorClass
371      */
372     public void setPropertyEditorClass(Class<? extends PropertyEditor> propertyEditorClass) {
373         this.propertyEditor = ObjectUtils.newInstance(propertyEditorClass);
374     }
375 
376     /**
377      * Directly validate simple fields, call completeValidation on Definition
378      * fields.
379      *
380      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
381      */
382     @Override
383     @Deprecated
384     public void completeValidation(Class<?> rootObjectClass, Class<?> otherObjectClass) {
385         try {
386             if (!DataDictionary.isPropertyOf(rootObjectClass, getName())) {
387                 throw new AttributeValidationException("property '"
388                         + getName()
389                         + "' is not a property of class '"
390                         + rootObjectClass.getName()
391                         + "' ("
392                         + ""
393                         + ")");
394             }
395 
396             //TODO currently requiring a control or controlField, but this should not be case (AttrField should probably do the check)
397             if (getControl() == null && getControlField() == null) {
398                 throw new AttributeValidationException("property '"
399                         + getName()
400                         + "' in class '"
401                         + rootObjectClass.getName()
402                         + " does not have a control defined");
403             }
404 
405             if (getControl() != null) {
406                 getControl().completeValidation(rootObjectClass, otherObjectClass);
407             }
408 
409             if (attributeSecurity != null) {
410                 attributeSecurity.completeValidation(rootObjectClass, otherObjectClass);
411             }
412 
413             if (validationPattern != null) {
414                 validationPattern.completeValidation();
415             }
416 
417             if (formatterClass != null) {
418                 try {
419                     Class formatterClassObject = ClassUtils.getClass(ClassLoaderUtils.getDefaultClassLoader(),
420                             getFormatterClass());
421                     if (!Formatter.class.isAssignableFrom(formatterClassObject)) {
422                         throw new ClassValidationException("formatterClass is not a valid instance of "
423                                 + Formatter.class.getName()
424                                 + " instead was: "
425                                 + formatterClassObject.getName());
426                     }
427                 } catch (ClassNotFoundException e) {
428                     throw new ClassValidationException("formatterClass could not be found: " + getFormatterClass(), e);
429                 }
430             }
431         } catch (RuntimeException ex) {
432             Logger.getLogger(getClass()).error(
433                     "Unable to validate attribute " + rootObjectClass + "." + getName() + ": " + ex.getMessage(), ex);
434             throw ex;
435         }
436     }
437 
438     /**
439      * Directly validate simple fields, call completeValidation on Definition
440      * fields.
441      *
442      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation(org.kuali.rice.krad.datadictionary.validator.ValidationTrace)
443      */
444     public void completeValidation(Class rootObjectClass, Class otherObjectClass, ValidationTrace tracer) {
445         tracer.addBean(this.getClass().getSimpleName(), "Attribute: " + getName());
446         try {
447             if (!DataDictionary.isPropertyOf(rootObjectClass, getName())) {
448                 String currentValues[] = {"property = " + getName(), "class = " + rootObjectClass.getName()};
449                 tracer.createError("Property is not found in class", currentValues);
450             }
451 
452             //TODO currently requiring a control or controlField, but this should not be case (AttrField should probably do the check)
453             if (getControl() == null && getControlField() == null) {
454                 String currentValues[] = {"property = " + getName(), "class = " + rootObjectClass.getName()};
455                 tracer.createError("Property does not have a control defined in the class", currentValues);
456             }
457 
458             if (getControl() != null) {
459                 //getControl().completeValidation(rootObjectClass, otherObjectClass, tracer.getCopy());
460             }
461 
462             if (attributeSecurity != null) {
463                 attributeSecurity.completeValidation(rootObjectClass, otherObjectClass, tracer.getCopy());
464             }
465 
466             if (validationPattern != null) {
467                 // validationPattern.completeValidation(tracer.getCopy());
468             }
469 
470             if (formatterClass != null) {
471                 try {
472                     Class formatterClassObject = ClassUtils.getClass(ClassLoaderUtils.getDefaultClassLoader(),
473                             getFormatterClass());
474                     if (!Formatter.class.isAssignableFrom(formatterClassObject)) {
475                         String currentValues[] = {"formatterClassObject = " + formatterClassObject.getName()};
476                         tracer.createError("FormatterClass is not a valid instance", currentValues);
477                     }
478                 } catch (ClassNotFoundException e) {
479                     String currentValues[] = {"class = " + getFormatterClass()};
480                     tracer.createError("FormatterClass could not be found", currentValues);
481                 }
482             }
483         } catch (RuntimeException ex) {
484             String currentValues[] =
485                     {"attribute = " + rootObjectClass + "." + getName(), "Exception = " + ex.getMessage()};
486             tracer.createError("Unable to validate attribute", currentValues);
487         }
488     }
489 
490     /**
491      * @see java.lang.Object#toString()
492      */
493     @Override
494     public String toString() {
495         return "AttributeDefinition for attribute " + getName();
496     }
497 
498     /**
499      * @return the attributeSecurity
500      */
501     @BeanTagAttribute(name = "attributeSecurity", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
502     public AttributeSecurity getAttributeSecurity() {
503         return this.attributeSecurity;
504     }
505 
506     /**
507      * @param attributeSecurity the attributeSecurity to set
508      */
509     public void setAttributeSecurity(AttributeSecurity attributeSecurity) {
510         this.attributeSecurity = attributeSecurity;
511     }
512 
513     /**
514      * @return the unique
515      */
516     public Boolean getUnique() {
517         return this.unique;
518     }
519 
520     /**
521      * @param unique the unique to set
522      */
523     public void setUnique(Boolean unique) {
524         this.unique = unique;
525     }
526 
527     /**
528      * Default {@code Control} to use when the attribute is to be rendered
529      * for the UI. Used by the UIF when a control is not defined for an
530      * {@code InputField}
531      *
532      * @return Control instance
533      */
534     @BeanTagAttribute(name = "control", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
535     public Control getControlField() {
536         return this.controlField;
537     }
538 
539     /**
540      * Setter for the default control
541      *
542      * @param controlField
543      */
544     public void setControlField(Control controlField) {
545         this.controlField = controlField;
546     }
547 
548     /**
549      * @see org.kuali.rice.krad.datadictionary.validation.constraint.LengthConstraint#getMinLength()
550      */
551     @BeanTagAttribute(name = "minLength")
552     public Integer getMinLength() {
553         return this.getSimpleConstraint().getMinLength();
554     }
555 
556     /**
557      * Setter for minumum length
558      *
559      * @param minLength
560      */
561     public void setMinLength(Integer minLength) {
562         this.getSimpleConstraint().setMinLength(minLength);
563     }
564 
565     /**
566      * @return the dataType
567      */
568     @BeanTagAttribute(name = "dataType", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
569     public DataType getDataType() {
570         return simpleConstraint.getDataType();
571     }
572 
573     /**
574      * @param dataType the dataType to set
575      */
576     public void setDataType(DataType dataType) {
577         simpleConstraint.setDataType(dataType);
578     }
579 
580     public void setDataType(String dataType) {
581         simpleConstraint.setDataType(DataType.valueOf(dataType));
582     }
583 
584     /**
585      * @return the customValidatorClass
586      */
587     @BeanTagAttribute(name = "customValidatorClass")
588     public String getCustomValidatorClass() {
589         return this.customValidatorClass;
590     }
591 
592     /**
593      * @param customValidatorClass the customValidatorClass to set
594      */
595     public void setCustomValidatorClass(String customValidatorClass) {
596         this.customValidatorClass = customValidatorClass;
597     }
598 
599     /**
600      * @return the validChars
601      */
602     @Override
603     @BeanTagAttribute(name = "validChractersConstraint", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
604     public ValidCharactersConstraint getValidCharactersConstraint() {
605         return this.validCharactersConstraint;
606     }
607 
608     /**
609      * @param validCharactersConstraint the validChars to set
610      */
611     public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
612         this.validCharactersConstraint = validCharactersConstraint;
613     }
614 
615     /**
616      * @return the caseConstraint
617      */
618     @Override
619     @BeanTagAttribute(name = "caseConstraint", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
620     public CaseConstraint getCaseConstraint() {
621         return this.caseConstraint;
622     }
623 
624     /**
625      * @param caseConstraint the caseConstraint to set
626      */
627     public void setCaseConstraint(CaseConstraint caseConstraint) {
628         this.caseConstraint = caseConstraint;
629     }
630 
631     /**
632      * @return the requireConstraint
633      */
634     @Override
635     @BeanTagAttribute(name = "prerequisteConstraint", type = BeanTagAttribute.AttributeType.LISTBEAN)
636     public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
637         return this.dependencyConstraints;
638     }
639 
640     /**
641      * @param dependencyConstraints the requireConstraint to set
642      */
643     public void setPrerequisiteConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
644         this.dependencyConstraints = dependencyConstraints;
645     }
646 
647     /**
648      * @return the occursConstraint
649      */
650     @Override
651     @BeanTagAttribute(name = "mustOccurConstraints", type = BeanTagAttribute.AttributeType.LISTBEAN)
652     public List<MustOccurConstraint> getMustOccurConstraints() {
653         return this.mustOccurConstraints;
654     }
655 
656     /**
657      * @param mustOccurConstraints the occursConstraint to set
658      */
659     public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
660         this.mustOccurConstraints = mustOccurConstraints;
661     }
662 
663     /**
664      * @return the lookupDefinition
665      */
666     public LookupConstraint getLookupDefinition() {
667         return this.lookupDefinition;
668     }
669 
670     /**
671      * @param lookupDefinition the lookupDefinition to set
672      */
673     public void setLookupDefinition(LookupConstraint lookupDefinition) {
674         this.lookupDefinition = lookupDefinition;
675     }
676 
677     /**
678      * @return the lookupContextPath
679      */
680     public String getLookupContextPath() {
681         return this.lookupContextPath;
682     }
683 
684     /**
685      * @param lookupContextPath the lookupContextPath to set
686      */
687     public void setLookupContextPath(String lookupContextPath) {
688         this.lookupContextPath = lookupContextPath;
689     }
690 
691     /**
692      * @return the childEntryName
693      */
694     @BeanTagAttribute(name = "childEntryName")
695     public String getChildEntryName() {
696         return this.childEntryName;
697     }
698 
699     /**
700      * @param childEntryName the childEntryName to set
701      */
702     public void setChildEntryName(String childEntryName) {
703         this.childEntryName = childEntryName;
704     }
705 
706     /**
707      * Instance of {@code KeyValluesFinder} that should be invoked to
708      * provide a List of values the field can have. Generally used to provide
709      * the options for a multi-value control or to validate the submitted field
710      * value
711      *
712      * @return KeyValuesFinder instance
713      */
714     @BeanTagAttribute(name = "optionFinder", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
715     public KeyValuesFinder getOptionsFinder() {
716         return this.optionsFinder;
717     }
718 
719     /**
720      * Setter for the field's KeyValuesFinder instance
721      *
722      * @param optionsFinder
723      */
724     public void setOptionsFinder(KeyValuesFinder optionsFinder) {
725         this.optionsFinder = optionsFinder;
726     }
727 
728     /**
729      * Setter that takes in the class name for the options finder and creates a
730      * new instance to use as the finder for the attribute field
731      *
732      * @param optionsFinderClass
733      */
734     public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
735         this.optionsFinder = ObjectUtils.newInstance(optionsFinderClass);
736     }
737 
738     public void setAdditionalDisplayAttributeName(String additionalDisplayAttributeName) {
739         this.additionalDisplayAttributeName = additionalDisplayAttributeName;
740     }
741 
742     @BeanTagAttribute(name = "additionalDisplayAttributeName")
743     public String getAdditionalDisplayAttributeName() {
744         return this.additionalDisplayAttributeName;
745     }
746 
747     public void setAlternateDisplayAttributeName(String alternateDisplayAttributeName) {
748         this.alternateDisplayAttributeName = alternateDisplayAttributeName;
749     }
750 
751     @BeanTagAttribute(name = "alternateDisplayAttributeName")
752     public String getAlternateDisplayAttributeName() {
753         return this.alternateDisplayAttributeName;
754     }
755 
756     /**
757      * Gets dependency constraints for this AttributeDefinition.  Same as getPrerequisiteConstraints.
758      *
759      * @return dependency constraints
760      */
761     public List<PrerequisiteConstraint> getDependencyConstraints() {
762         return dependencyConstraints;
763     }
764 
765     /**
766      * Sets dependency constraints for this AttributeDefinition.  Same as setPrerequisiteConstraints.
767      *
768      * @param dependencyConstraints dependency constraints
769      */
770     public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
771         this.dependencyConstraints = dependencyConstraints;
772     }
773 
774 }