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