001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krad.datadictionary;
017
018import java.beans.PropertyEditor;
019import java.util.List;
020
021import org.apache.commons.lang.ClassUtils;
022import org.apache.commons.lang.StringUtils;
023import org.kuali.rice.core.api.data.DataType;
024import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
025import org.kuali.rice.core.api.util.ClassLoaderUtils;
026import org.kuali.rice.core.web.format.Formatter;
027import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
028import org.kuali.rice.krad.datadictionary.mask.MaskFormatterLiteral;
029import org.kuali.rice.krad.datadictionary.parse.BeanTag;
030import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
031import org.kuali.rice.krad.datadictionary.validation.ValidationPattern;
032import org.kuali.rice.krad.datadictionary.validation.capability.CaseConstrainable;
033import org.kuali.rice.krad.datadictionary.validation.capability.Formatable;
034import org.kuali.rice.krad.datadictionary.validation.capability.HierarchicallyConstrainable;
035import org.kuali.rice.krad.datadictionary.validation.capability.MustOccurConstrainable;
036import org.kuali.rice.krad.datadictionary.validation.capability.PrerequisiteConstrainable;
037import org.kuali.rice.krad.datadictionary.validation.capability.ValidCharactersConstrainable;
038import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
039import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
040import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
041import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
042import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
043import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
044import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
045import org.kuali.rice.krad.uif.control.Control;
046import org.kuali.rice.krad.util.KRADUtils;
047
048/**
049 * A single attribute definition in the DataDictionary, which contains
050 * information relating to the display, validation, and general maintenance of a
051 * specific attribute of an entry.
052 *
053 * @author Kuali Rice Team (rice.collab@kuali.org)
054 */
055@BeanTag(name = "attributeDefinition")
056public class AttributeDefinition extends AttributeDefinitionBase implements CaseConstrainable, PrerequisiteConstrainable, Formatable, HierarchicallyConstrainable, MustOccurConstrainable, ValidCharactersConstrainable {
057    private static final long serialVersionUID = -2490613377818442742L;
058    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AttributeDefinition.class);
059
060    protected Boolean forceUppercase;
061
062    protected Boolean unique;
063
064    //These are deprecated DO NOT USE with new KRAD implementations
065    @Deprecated
066    protected ValidationPattern validationPattern;
067
068    @Deprecated
069    protected ControlDefinition control;
070
071    // TODO: rename to control once ControlDefinition is removed
072    protected Control controlField;
073    protected Control cachedDerivedControl = null;
074
075    @Deprecated
076    protected String formatterClass;
077    protected PropertyEditor propertyEditor;
078
079    protected AttributeSecurity attributeSecurity;
080
081    protected Boolean dynamic;
082
083    // KRAD constraints
084    protected String customValidatorClass;
085    protected ValidCharactersConstraint validCharactersConstraint;
086    protected CaseConstraint caseConstraint;
087    protected List<PrerequisiteConstraint> dependencyConstraints;
088    protected List<MustOccurConstraint> mustOccurConstraints;
089
090    //TODO: This may not be required since we now use ComplexAttributeDefinition
091    protected String childEntryName;
092
093    private KeyValuesFinder optionsFinder;
094
095    protected String alternateDisplayAttributeName;
096    protected String additionalDisplayAttributeName;
097
098    public AttributeDefinition() {
099        super();
100    }
101
102    /**
103     * Setter for force upper case
104     *
105     * @param forceUppercase
106     */
107    public void setForceUppercase(Boolean forceUppercase) {
108        this.forceUppercase = forceUppercase;
109    }
110
111    /**
112     * Indicates whether user entry should be converted to upper case
113     *
114     * <p>
115     * If set all user input will be changed to uppercase. Values from the database will also be forced to display
116     * as upper case and thus be persisted as upper case.
117     * </p>
118     *
119     * If not set and embedded metadata is present, the ForceUppercase value will be read from the linked metadata.
120     *
121     * @return boolean true if force upper case is set
122     */
123    @BeanTagAttribute
124    public Boolean getForceUppercase() {
125        if ( forceUppercase != null ) {
126            return forceUppercase;
127        }
128        if ( getDataObjectAttribute() != null ) {
129            return getDataObjectAttribute().isForceUppercase();
130        }
131        return Boolean.FALSE;
132    }
133
134    /**
135     * Returns the maximum length for this field, if set.  If not set, it attempts to pull from
136     * the embedded metadata, if any.
137     *z
138     * @see org.kuali.rice.krad.datadictionary.validation.constraint.LengthConstraint#getMaxLength()
139     */
140    @BeanTagAttribute
141    public Integer getMaxLength() {
142        if ( getSimpleConstraint().getMaxLength() != null ) {
143            return getSimpleConstraint().getMaxLength();
144        }
145        if ( getDataObjectAttribute() != null ) {
146            if ( getDataObjectAttribute().getMaxLength() != null ) {
147                return new Integer( getDataObjectAttribute().getMaxLength().intValue() );
148            }
149        }
150        return null;
151    }
152
153    /**
154     * Setter for maximum length
155     *
156     * @param maxLength
157     */
158    public void setMaxLength(Integer maxLength) {
159        this.getSimpleConstraint().setMaxLength(maxLength);
160    }
161
162    /**
163     * @see org.kuali.rice.krad.datadictionary.validation.constraint.RangeConstraint#getExclusiveMin()
164     */
165    @BeanTagAttribute
166    public String getExclusiveMin() {
167        return this.getSimpleConstraint().getExclusiveMin();
168    }
169
170    /**
171     * Setter for minimum value
172     *
173     * @param exclusiveMin - minimum allowed value
174     */
175    public void setExclusiveMin(String exclusiveMin) {
176        this.getSimpleConstraint().setExclusiveMin(exclusiveMin);
177    }
178
179    /**
180     * @see org.kuali.rice.krad.datadictionary.validation.constraint.RangeConstraint#getInclusiveMax()
181     */
182    @BeanTagAttribute
183    public String getInclusiveMax() {
184        return this.getSimpleConstraint().getInclusiveMax();
185    }
186
187    /**
188     * Setter for maximum value
189     *
190     * @param inclusiveMax - max allowed value
191     */
192    public void setInclusiveMax(String inclusiveMax) {
193        this.getSimpleConstraint().setInclusiveMax(inclusiveMax);
194    }
195
196    /**
197     * The validationPattern element defines the allowable character-level or
198     * field-level values for an attribute.
199     *
200     * JSTL: validationPattern is a Map which is accessed using a key of
201     * "validationPattern". Each entry may contain some of the keys listed
202     * below. The keys that may be present for a given attribute are dependent
203     * upon the type of validationPattern.
204     *
205     * maxLength (String) exactLength type allowWhitespace allowUnderscore
206     * allowPeriod validChars precision scale allowNegative
207     *
208     * The allowable keys (in addition to type) for each type are: Type****
209     * ***Keys*** alphanumeric exactLength maxLength allowWhitespace
210     * allowUnderscore allowPeriod
211     *
212     * alpha exactLength maxLength allowWhitespace
213     *
214     * anyCharacter exactLength maxLength allowWhitespace
215     *
216     * charset validChars
217     *
218     * numeric exactLength maxLength
219     *
220     * fixedPoint allowNegative precision scale
221     *
222     * floatingPoint allowNegative
223     *
224     * date n/a emailAddress n/a javaClass n/a month n/a phoneNumber n/a
225     * timestamp n/a year n/a zipcode n/a
226     *
227     * Note: maxLength and exactLength are mutually exclusive. If one is
228     * entered, the other may not be entered.
229     *
230     * Note: See ApplicationResources.properties for exact regex patterns. e.g.
231     * validationPatternRegex.date for regex used in date validation.
232     */
233    @Deprecated
234    public void setValidationPattern(ValidationPattern validationPattern) {
235        this.validationPattern = validationPattern;
236    }
237
238    /**
239     * Indicates whether a validation pattern has been set
240     * @return boolean
241     */
242    @Deprecated
243    public boolean hasValidationPattern() {
244        return (validationPattern != null);
245    }
246
247    /**
248     * Defines the allowable character-level or
249     * field-level values for an attribute
250     *
251     * <p>
252     * ValidationPattern is a Map which is accessed using a key of "validationPattern". Each entry may contain
253     * some of the keys listed below. The keys that may be present for a given attribute are dependent
254     * upon the type of validationPattern.
255     *
256     * maxLength (String) exactLength type allowWhitespace allowUnderscore
257     * allowPeriod validChars precision scale allowNegative
258     *
259     * The allowable keys (in addition to type) for each type are: Type****
260     * ***Keys*** alphanumeric exactLength maxLength allowWhitespace
261     * allowUnderscore allowPeriod
262     *
263     * alpha exactLength maxLength allowWhitespace
264     *
265     * anyCharacter exactLength maxLength allowWhitespace
266     *
267     * charset validChars
268     *
269     * numeric exactLength maxLength
270     *
271     * fixedPoint allowNegative precision scale
272     *
273     * floatingPoint allowNegative
274     *
275     * date n/a emailAddress n/a javaClass n/a month n/a phoneNumber n/a
276     * timestamp n/a year n/a zipcode n/a
277     *
278     * Note: maxLength and exactLength are mutually exclusive. If one is
279     * entered, the other may not be entered.
280     *
281     * Note: See ApplicationResources.properties for exact regex patterns. e.g.
282     * validationPatternRegex.date for regex used in date validation.
283     * </p>
284     *
285     * @return ValidationPattern
286     */
287    @Deprecated
288    public ValidationPattern getValidationPattern() {
289        return this.validationPattern;
290    }
291
292    /**
293     * @return control
294     */
295    @BeanTagAttribute(name = "oldControl", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
296    @Deprecated
297    public ControlDefinition getControl() {
298        return control;
299    }
300
301    /**
302     * The control element defines the manner in which an attribute is displayed
303     * and the manner in which the attribute value is entered.
304     *
305     * JSTL: control is a Map representing an HTML control. It is accessed using
306     * a key of "control". The table below shows the types of entries associated
307     * with each type of control.
308     *
309     * * Control Type** **Key** **Value** checkbox checkbox boolean String
310     *
311     * hidden hidden boolean String
312     *
313     * radio radio boolean String valuesFinder valuesFinder class name
314     * dataObjectClass String keyAttribute String labelAttribute String
315     * includeKeyInLabel boolean String
316     *
317     * select select boolean String valuesFinder valuesFinder class name
318     * dataObjectClass String keyAttribute String labelAttribute String
319     * includeBlankRow boolean String includeKeyInLabel boolean String
320     *
321     * apcSelect apcSelect boolean String paramNamespace String
322     * parameterDetailType String parameterName String
323     *
324     * text text boolean String size String
325     *
326     * textarea textarea boolean String rows cols
327     *
328     * currency currency boolean String size String formattedMaxLength String
329     *
330     * kualiUser kualiUser boolean String universalIdAttributeName String
331     * userIdAttributeName String personNameAttributeName String
332     *
333     * lookupHidden lookupHidden boolean String
334     *
335     * lookupReadonly lookupReadonly boolean String
336     *
337     * @param control
338     * @throws IllegalArgumentException if the given control is null
339     */
340    @Deprecated
341    public void setControl(ControlDefinition control) {
342        if (control == null) {
343            throw new IllegalArgumentException("invalid (null) control");
344        }
345        this.control = control;
346    }
347
348    @Deprecated
349    public boolean hasFormatterClass() {
350        return (formatterClass != null);
351    }
352
353    @Override
354    @BeanTagAttribute
355    @Deprecated
356    public String getFormatterClass() {
357        return formatterClass;
358    }
359
360    /**
361     * The formatterClass element is used when custom formatting is required for
362     * display of the field value. This field specifies the name of the java
363     * class to be used for the formatting. About 15 different classes are
364     * available including BooleanFormatter, CurrencyFormatter, DateFormatter,
365     * etc.
366     */
367    @Deprecated
368    public void setFormatterClass(String formatterClass) {
369        if (formatterClass == null) {
370            throw new IllegalArgumentException("invalid (null) formatterClass");
371        }
372        this.formatterClass = formatterClass;
373    }
374
375    /**
376     * Performs formatting of the field value for display and then converting the value back to its
377     * expected type from a string.
378     *
379     * If not set in the AttributeDefinition, it attempts to pull from the embedded metadata, if any.
380     *
381     * <p>
382     * Note property editors exist and are already registered for the basic Java types and the
383     * common Kuali types such as [@link KualiDecimal}. Registration with this property is only
384     * needed for custom property editors
385     * </p>
386     *
387     * @return PropertyEditor property editor instance to use for this field
388     */
389    @BeanTagAttribute
390    public PropertyEditor getPropertyEditor() {
391        if ( propertyEditor != null ) {
392            return propertyEditor;
393        }
394        if ( getDataObjectAttribute() != null ) {
395            return getDataObjectAttribute().getPropertyEditor();
396        }
397        return null;
398    }
399
400    /**
401     * Setter for the custom property editor to use for the field
402     *
403     * @param propertyEditor
404     */
405    public void setPropertyEditor(PropertyEditor propertyEditor) {
406        this.propertyEditor = propertyEditor;
407    }
408
409    /**
410     * Convenience setter for configuring a property editor by class
411     *
412     * @param propertyEditorClass
413     */
414    public void setPropertyEditorClass(Class<? extends PropertyEditor> propertyEditorClass) {
415        this.propertyEditor = KRADUtils.createNewObjectFromClass(propertyEditorClass);
416    }
417
418    /**
419     * This overridden method ...
420     *
421     * @see org.kuali.rice.krad.datadictionary.DictionaryBeanBase#dataDictionaryPostProcessing()
422     */
423    @Override
424    public void dataDictionaryPostProcessing() {
425        super.dataDictionaryPostProcessing();
426        if ( getAttributeSecurity() != null ) {
427            getAttributeSecurity().dataDictionaryPostProcessing();
428        }
429    }
430
431    /**
432     * Directly validate simple fields, call completeValidation on Definition
433     * fields.
434     *
435     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
436     */
437    @Override
438    @Deprecated
439    public void completeValidation(Class<?> rootObjectClass, Class<?> otherObjectClass) {
440        completeValidation(rootObjectClass, otherObjectClass, new ValidationTrace());
441    }
442
443    /**
444     * Directly validate simple fields, call completeValidation on Definition
445     * fields.
446     *
447     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation(org.kuali.rice.krad.datadictionary.validator.ValidationTrace)
448     */
449    @Override
450    public void completeValidation(Class rootObjectClass, Class otherObjectClass, ValidationTrace tracer) {
451        tracer.addBean(this.getClass().getSimpleName(), "id: " + getId());
452        try {
453            if (StringUtils.isBlank(getName())) {
454                String currentValues[] = {"id = " + getId(), "class = " + rootObjectClass.getName()};
455                tracer.createError("AttributeDefinition missing name", currentValues);
456            }
457            if (!DataDictionary.isPropertyOf(rootObjectClass, getName())) {
458                String currentValues[] = {"property = " + getName(), "class = " + rootObjectClass.getName()};
459                tracer.createError("Property is not found in class. Ensure that the property is defined on the class and that there is at least a public 'getter' for it.", currentValues);
460            }
461
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 (getAttributeSecurity() != null) {
468                getAttributeSecurity().completeValidation(rootObjectClass, otherObjectClass, tracer.getCopy());
469            }
470
471            // KNS Controls - do not use KRAD Validation style
472            if (getControl() != null) {
473                getControl().completeValidation(rootObjectClass, otherObjectClass);
474            }
475            if (validationPattern != null) {
476                 validationPattern.completeValidation();
477            }
478
479            if (getFormatterClass() != 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            LOG.error("Exception while validating AttributeDefinition: " + getId(), ex );
497        }
498    }
499
500    @BeanTagAttribute(name = "attributeSecurity", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
501    public AttributeSecurity getAttributeSecurity() {
502        if ( attributeSecurity != null ) {
503            return attributeSecurity;
504        }
505        
506        // If we have an embedded attribute definition and this attribute is
507        // listed as "sensitive", then set the field to be masked by default on the UI
508        if ( getDataObjectAttribute() != null ) {
509            if ( getDataObjectAttribute().isSensitive() ) {
510                AttributeSecurity attrSec = new AttributeSecurity();
511                attrSec.setMask(true);
512                attrSec.setMaskFormatter(new MaskFormatterLiteral());
513                attributeSecurity = attrSec;
514            }
515        }
516        
517        return attributeSecurity;
518    }
519
520    public void setAttributeSecurity(AttributeSecurity attributeSecurity) {
521        this.attributeSecurity = attributeSecurity;
522    }
523
524    public boolean hasAttributeSecurity() {
525        return (getAttributeSecurity() != null);
526    }
527
528    /**
529     * @return the unique
530     */
531    public Boolean getUnique() {
532        return this.unique;
533    }
534
535    /**
536     * @param unique the unique to set
537     */
538    public void setUnique(Boolean unique) {
539        this.unique = unique;
540    }
541
542    /**
543     * Default {@code Control} to use when the attribute is to be rendered
544     * for the UI. Used by the UIF when a control is not defined for an
545     * {@code InputField}
546     *
547     * If not set in the AttributeDefinition, a default will be generated from the metadata for this field.
548     *
549     * @return Control instance
550     */
551    @BeanTagAttribute(name = "control", type = BeanTagAttribute.AttributeType.BYTYPE)
552    public Control getControlField() {
553        if ( controlField != null ) {
554            return controlField;
555        }
556        if ( cachedDerivedControl == null ) {
557            if ( GlobalResourceLoader.isInitialized() ) {
558                cachedDerivedControl = KRADServiceLocatorWeb.getUifDefaultingService().deriveControlAttributeFromMetadata(this);
559            }
560        }
561        return cachedDerivedControl;
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
577    public Integer getMinLength() {
578        if ( getSimpleConstraint().getMinLength() != null ) {
579            return getSimpleConstraint().getMinLength();
580        }
581        if ( getDataObjectAttribute() != null ) {
582            if ( getDataObjectAttribute().getMinLength() != null ) {
583                return new Integer( getDataObjectAttribute().getMinLength().intValue() );
584            }
585        }
586        return null;
587    }
588
589    /**
590     * Setter for minumum length
591     *
592     * @param minLength
593     */
594    public void setMinLength(Integer minLength) {
595        this.getSimpleConstraint().setMinLength(minLength);
596    }
597
598    /**
599     * Returns the Kuali datatype for this field.  See {@link DataType} for the defined types.
600     *
601     * If not defined in the AttributeDefinition, it will be retrieved from the embedded metadata, if defined.
602     *
603     * If not defined by either, will return {@link DataType#STRING}.
604     */
605    @BeanTagAttribute(type = BeanTagAttribute.AttributeType.SINGLEBEAN)
606    public DataType getDataType() {
607        if ( simpleConstraint.getDataType() != null ) {
608            return simpleConstraint.getDataType();
609        }
610        if ( getDataObjectAttribute() != null ) {
611            return getDataObjectAttribute().getDataType();
612        }
613        return DataType.STRING;
614    }
615
616    /**
617     * @param dataType the dataType to set
618     */
619    public void setDataType(DataType dataType) {
620        simpleConstraint.setDataType(dataType);
621    }
622
623    public void setDataType(String dataType) {
624        simpleConstraint.setDataType(DataType.valueOf(dataType));
625    }
626
627    /**
628     * @return the customValidatorClass
629     */
630    @BeanTagAttribute
631    public String getCustomValidatorClass() {
632        return this.customValidatorClass;
633    }
634
635    /**
636     * @param customValidatorClass the customValidatorClass to set
637     */
638    public void setCustomValidatorClass(String customValidatorClass) {
639        this.customValidatorClass = customValidatorClass;
640    }
641
642    /**
643     * @return the validChars
644     */
645    @Override
646    @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
647    public ValidCharactersConstraint getValidCharactersConstraint() {
648        if ( validCharactersConstraint == null ) {
649            // If there is no constraint set, attempt to derive one
650            if ( GlobalResourceLoader.isInitialized() ) {
651                // We don't set a default validation pattern if the field is hidden
652                if ( getControlField() != null && !getControlField().isHidden() && !Boolean.TRUE.equals(getControlField().getReadOnly()) ) {
653                    validCharactersConstraint = KRADServiceLocatorWeb.getUifDefaultingService().deriveValidCharactersConstraint( this );
654                }
655            }
656        }
657        return validCharactersConstraint;
658    }
659
660    /**
661     * @param validCharactersConstraint the validChars to set
662     */
663    public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
664        this.validCharactersConstraint = validCharactersConstraint;
665    }
666
667    /**
668     * @return the caseConstraint
669     */
670    @Override
671    @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
672    public CaseConstraint getCaseConstraint() {
673        return this.caseConstraint;
674    }
675
676    /**
677     * @param caseConstraint the caseConstraint to set
678     */
679    public void setCaseConstraint(CaseConstraint caseConstraint) {
680        this.caseConstraint = caseConstraint;
681    }
682
683    /**
684     * @return the requireConstraint
685     */
686    @Override
687    @BeanTagAttribute
688    public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
689        return this.dependencyConstraints;
690    }
691
692    /**
693     * @param dependencyConstraints the requireConstraint to set
694     */
695    public void setPrerequisiteConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
696        this.dependencyConstraints = dependencyConstraints;
697    }
698
699    /**
700     * @return the occursConstraint
701     */
702    @Override
703    @BeanTagAttribute
704    public List<MustOccurConstraint> getMustOccurConstraints() {
705        return this.mustOccurConstraints;
706    }
707
708    /**
709     * @param mustOccurConstraints the occursConstraint to set
710     */
711    public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
712        this.mustOccurConstraints = mustOccurConstraints;
713    }
714
715    /**
716     * @return the childEntryName
717     */
718    @Override
719    @BeanTagAttribute
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
740    public KeyValuesFinder getOptionsFinder() {
741        if ( optionsFinder == null ) {
742            if ( getDataObjectAttribute() != null && getDataObjectAttribute().getValidValues() != null ) {
743                return getDataObjectAttribute().getValidValues();
744            }
745        }
746        return optionsFinder;
747    }
748
749    /**
750     * Setter for the field's KeyValuesFinder instance
751     *
752     * @param optionsFinder
753     */
754    public void setOptionsFinder(KeyValuesFinder optionsFinder) {
755        this.optionsFinder = optionsFinder;
756    }
757
758    /**
759     * Setter that takes in the class name for the options finder and creates a
760     * new instance to use as the finder for the attribute field
761     *
762     * @param optionsFinderClass
763     */
764    public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
765        this.optionsFinder = KRADUtils.createNewObjectFromClass(optionsFinderClass);
766    }
767
768    public void setAdditionalDisplayAttributeName(String additionalDisplayAttributeName) {
769        this.additionalDisplayAttributeName = additionalDisplayAttributeName;
770    }
771
772    @BeanTagAttribute
773    public String getAdditionalDisplayAttributeName() {
774        return this.additionalDisplayAttributeName;
775    }
776
777    public void setAlternateDisplayAttributeName(String alternateDisplayAttributeName) {
778        this.alternateDisplayAttributeName = alternateDisplayAttributeName;
779    }
780
781    @BeanTagAttribute
782    public String getAlternateDisplayAttributeName() {
783        return this.alternateDisplayAttributeName;
784    }
785
786    /**
787     * Gets dependency constraints for this AttributeDefinition.  Same as getPrerequisiteConstraints.
788     *
789     * @return dependency constraints
790     */
791    public List<PrerequisiteConstraint> getDependencyConstraints() {
792        return dependencyConstraints;
793    }
794
795    /**
796     * Sets dependency constraints for this AttributeDefinition.  Same as setPrerequisiteConstraints.
797     *
798     * @param dependencyConstraints dependency constraints
799     */
800    public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
801        this.dependencyConstraints = dependencyConstraints;
802    }
803
804}