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.data.metadata.impl;
017
018import java.beans.PropertyEditor;
019import java.util.Collections;
020import java.util.Set;
021
022import org.apache.commons.lang.StringUtils;
023import org.kuali.rice.core.api.data.DataType;
024import org.kuali.rice.krad.data.metadata.DataObjectAttribute;
025import org.kuali.rice.krad.data.provider.annotation.UifDisplayHint;
026import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
027
028import com.google.common.annotations.Beta;
029
030/**
031 * Base implementation class for attribute metadata for data object classes.
032 *
033 * <p>
034 * This implementation supports "chaining" for most attributes. That is, if the value for a property is defined locally,
035 * it will me used. If unset (null) it will, if there is an {@link #embeddedAttribute}, request it from that
036 * DataObjectAttribute. (This could be a recursive operation if multiple metadata providers are chained.)
037 * </p>
038 * <p>
039 * If the value is unset and there is no embedded attribute, most methods will return a non-null default value.
040 * </p>
041 *
042 * @author Kuali Rice Team (rice.collab@kuali.org)
043 */
044public class DataObjectAttributeImpl extends MetadataCommonBase implements DataObjectAttributeInternal {
045        private static final long serialVersionUID = -5241499559388935579L;
046        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataObjectAttributeImpl.class);
047
048        protected DataObjectAttribute embeddedAttribute;
049
050        protected Class<?> owningType;
051
052        // These are temporary placeholders for the source property from which a property was inherited when
053        // it "lives" on a related object. E.g., accountType.codeAndDescription
054        // After all metadata has been imported, this information will be used to "embed" the parent
055        // DataObjectAttribute so that properties (E.g., label) are inherited from there
056        protected Class<?> inheritedFromType;
057        protected String inheritedFromAttributeName;
058        protected String inheritedFromParentAttributeName;
059        protected String displayAttributeName;
060        protected Boolean caseInsensitive;
061        protected Boolean forceUppercase;
062        protected Boolean required;
063        protected Boolean persisted;
064        protected Boolean sensitive;
065        protected Long maxLength;
066        protected Long minLength;
067        protected String validCharactersConstraintBeanName;
068
069        protected PropertyEditor propertyEditor;
070        protected KeyValuesFinder validValues;
071        protected DataType dataType;
072        protected Class<?> type;
073        
074        protected Set<UifDisplayHint> displayHints;
075
076    /**
077     * {@inheritDoc}
078     */
079        @Override
080        public String getDisplayAttributeName() {
081                if (displayAttributeName != null) {
082                        return displayAttributeName;
083                }
084
085                if (embeddedAttribute != null) {
086                        return embeddedAttribute.getDisplayAttributeName();
087                }
088
089                return getName();
090        }
091
092    /**
093    * Sets the attribute display name.
094    *
095    * @param displayAttributeName the attribute name.
096    */
097        public void setDisplayAttributeName(String displayAttributeName) {
098                if (StringUtils.isBlank(displayAttributeName)) {
099                        displayAttributeName = null;
100                }
101
102                this.displayAttributeName = displayAttributeName;
103        }
104
105    /**
106     * {@inheritDoc}
107     */
108        @Override
109        public boolean isCaseInsensitive() {
110                if (caseInsensitive != null) {
111                        return caseInsensitive;
112                }
113
114                if (embeddedAttribute != null) {
115                        return embeddedAttribute.isCaseInsensitive();
116                }
117
118                return false;
119        }
120
121    /**
122    * Sets value that determines whether attribute is case insensitive.
123    *
124    * @param caseInsensitive whether attribute is case insensitive.
125    */
126        public void setCaseInsensitive(boolean caseInsensitive) {
127                this.caseInsensitive = caseInsensitive;
128        }
129
130    /**
131     * {@inheritDoc}
132     */
133        @Override
134        public boolean isForceUppercase() {
135                if (forceUppercase != null) {
136                        return forceUppercase;
137                }
138
139                if (embeddedAttribute != null) {
140                        return embeddedAttribute.isForceUppercase();
141                }
142
143                return false;
144        }
145
146
147    /**
148    * Determines if attribute should be forced to upper case.
149    *
150    * @param forceUppercase whether attribute should be forced to upper.
151    */
152        public void setForceUppercase(boolean forceUppercase) {
153                this.forceUppercase = forceUppercase;
154        }
155
156    /**
157     * {@inheritDoc}
158     */
159        @Override
160        public PropertyEditor getPropertyEditor() {
161                if (propertyEditor != null) {
162                        return propertyEditor;
163                }
164
165                if (embeddedAttribute != null) {
166                        return embeddedAttribute.getPropertyEditor();
167                }
168
169                return null;
170        }
171
172    /**
173     * Sets the property editor used when loading data.
174     *
175     * @param propertyEditor determines formats when loading data.
176     */
177        public void setPropertyEditor(PropertyEditor propertyEditor) {
178                this.propertyEditor = propertyEditor;
179        }
180
181    /**
182     * {@inheritDoc}
183     */
184        @Override
185        public KeyValuesFinder getValidValues() {
186                if (validValues != null) {
187                        return validValues;
188                }
189
190                if (embeddedAttribute != null) {
191                        return embeddedAttribute.getValidValues();
192                }
193
194                return null;
195        }
196
197    /**
198    * Sets keyValueFinder used for dropdown.
199    *
200    * @param validValues dropdown keyValueFinder.
201    */
202        public void setValidValues(KeyValuesFinder validValues) {
203                this.validValues = validValues;
204        }
205
206    /**
207     * {@inheritDoc}
208     */
209        @Override
210        public DataType getDataType() {
211                if (dataType != null) {
212                        return dataType;
213                }
214
215                if (embeddedAttribute != null) {
216                        return embeddedAttribute.getDataType();
217                }
218
219                return DataType.STRING;
220        }
221
222    /**
223    * Sets KRAD data type.
224    *
225    * @param dataType KRAD derived data type.
226    */
227        public void setDataType(DataType dataType) {
228                this.dataType = dataType;
229        }
230
231    /**
232     * {@inheritDoc}
233     */
234        @Override
235        public String toString() {
236                StringBuilder builder = new StringBuilder();
237                builder.append("DataObjectAttribute [");
238                builder.append("name=").append(name);
239                if (label != null) {
240                        builder.append(", ").append("label=").append(label);
241                }
242                if (backingObjectName != null) {
243                        builder.append(", ").append("backingObjectName=").append(backingObjectName);
244                }
245                if (dataType != null) {
246                        builder.append(", ").append("dataType=").append(dataType);
247                }
248                if (type != null) {
249                        builder.append(", ").append("type=").append(type.getName());
250                }
251                if (caseInsensitive != null) {
252                        builder.append(", ").append("caseInsensitive=").append(caseInsensitive);
253                }
254                if (propertyEditor != null) {
255                        builder.append(", ").append("propertyEditor=").append(propertyEditor);
256                }
257                if (sensitive != null && sensitive) {
258                        builder.append(", ").append("sensitive=").append(sensitive);
259                }
260                if (validValues != null) {
261                        builder.append(", ").append("validValues=").append(validValues);
262                }
263                if (inheritedFromType != null) {
264                        builder.append(", ").append("inheritedFromType=").append(inheritedFromType);
265                }
266                if (inheritedFromAttributeName != null) {
267                        builder.append(", ").append("inheritedFromAttributeName=").append(inheritedFromAttributeName);
268                }
269                builder.append(", ").append("mergeAction=").append(mergeAction);
270                builder.append("]");
271                return builder.toString();
272        }
273
274    /**
275     * {@inheritDoc}
276     */
277        @Override
278        public Long getMaxLength() {
279                if (maxLength != null) {
280                        return maxLength;
281                }
282
283                if (embeddedAttribute != null) {
284                        return embeddedAttribute.getMaxLength();
285                }
286
287                return null;
288        }
289
290    /**
291    * Sets max length of attribute.
292    *
293    * @param maxLength attribute max length.
294    */
295        public void setMaxLength(Long maxLength) {
296                this.maxLength = maxLength;
297        }
298
299    /**
300     * {@inheritDoc}
301     */
302        @Override
303        public DataObjectAttribute getEmbeddedAttribute() {
304                return embeddedAttribute;
305        }
306
307    /**
308     * {@inheritDoc}
309     */
310        @Override
311        public void setEmbeddedAttribute(DataObjectAttribute embeddedAttribute) {
312                // protect against embedding itself
313                if (embeddedAttribute == this) {
314                        LOG.warn(
315                                        "ERROR!!!!  Attempt to embed a DataObjectAttribute into itself.  You must really want a stack overflow!  Trace: ",
316                                        new Throwable("Throw-away Throwable for tracing purposes."));
317                        return;
318                }
319
320                this.embeddedAttribute = embeddedAttribute;
321                setEmbeddedCommonMetadata(embeddedAttribute);
322        }
323
324    /**
325     * {@inheritDoc}
326     */
327        @Override
328        public boolean isRequired() {
329                if (required != null) {
330                        return required;
331                }
332
333                if (embeddedAttribute != null) {
334                        return embeddedAttribute.isRequired();
335                }
336
337                return false;
338        }
339
340    /**
341    * Set whether attribute is required.
342    *
343    * @param required attribute required flag.
344    */
345        public void setRequired(boolean required) {
346                this.required = required;
347        }
348
349    /**
350     * {@inheritDoc}
351     */
352        @Override
353    @Beta
354        public String getValidCharactersConstraintBeanName() {
355                if (validCharactersConstraintBeanName != null) {
356                        return validCharactersConstraintBeanName;
357                }
358
359                if (embeddedAttribute != null) {
360                        return embeddedAttribute.getValidCharactersConstraintBeanName();
361                }
362
363                return validCharactersConstraintBeanName;
364        }
365
366    /**
367     * BETA: Sets valid character constraint bean name.
368     *
369     * @param validCharactersConstraintBeanName character constraint bean name.
370     */
371    @Beta
372        public void setValidCharactersConstraintBeanName(String validCharactersConstraintBeanName) {
373                this.validCharactersConstraintBeanName = validCharactersConstraintBeanName;
374        }
375
376    /**
377     * {@inheritDoc}
378     */
379        @Override
380        public Class<?> getOwningType() {
381                if (owningType != null) {
382                        return owningType;
383                }
384
385                if (embeddedAttribute != null) {
386                        return embeddedAttribute.getOwningType();
387                }
388
389                return null;
390        }
391
392    /**
393    * Sets the data object type to which this attribute belongs.
394    *
395    * @param owningType data object type to which this attribute belongs.
396    */
397        public void setOwningType(Class<?> owningType) {
398                this.owningType = owningType;
399        }
400
401    /**
402     * {@inheritDoc}
403     */
404        @Override
405        public boolean isPersisted() {
406                if (persisted != null) {
407                        return persisted;
408                }
409
410                if (embeddedAttribute != null) {
411                        return embeddedAttribute.isPersisted();
412                }
413
414                return true;
415        }
416
417    /**
418    * Sets flag whether object is persisted.
419    *
420    * @param persisted flag whether object is persisted.
421    */
422        public void setPersisted(boolean persisted) {
423                this.persisted = persisted;
424        }
425
426    /**
427    * Determines type of class.
428    *
429    */
430        public Class<?> getType() {
431                if (type != null) {
432            return type;
433        }
434
435                return String.class;
436        }
437
438    /**
439    * Sets unknown class in order to determine type.
440    *
441    * @param javaType unknown class.
442    */
443        public void setType(Class<?> javaType) {
444                this.type = javaType;
445        }
446
447    /**
448     * {@inheritDoc}
449     */
450        @Override
451        public Class<?> getInheritedFromType() {
452                if (inheritedFromType != null) {
453                        return inheritedFromType;
454                }
455
456                if (embeddedAttribute != null) {
457                        return embeddedAttribute.getInheritedFromType();
458                }
459
460                return null;
461        }
462
463    /**
464    * Sets unknown class to determine if inherited.
465    *
466    * @param inheritedFromType unknown class.
467    */
468        public void setInheritedFromType(Class<?> inheritedFromType) {
469                this.inheritedFromType = inheritedFromType;
470        }
471
472    /**
473     * {@inheritDoc}
474     */
475        @Override
476        public String getInheritedFromAttributeName() {
477                if (inheritedFromAttributeName != null) {
478                        return inheritedFromAttributeName;
479                }
480
481                if (embeddedAttribute != null) {
482                        return embeddedAttribute.getInheritedFromAttributeName();
483                }
484
485                return null;
486        }
487
488    /**
489    * Sets data object name to determine if inherited.
490    *
491    * @param inheritedFromAttributeName name of attribute.
492    */
493        public void setInheritedFromAttributeName(String inheritedFromAttributeName) {
494                this.inheritedFromAttributeName = inheritedFromAttributeName;
495        }
496
497    /**
498     * {@inheritDoc}
499     */
500        @Override
501        public String getInheritedFromParentAttributeName() {
502                if (inheritedFromParentAttributeName != null) {
503                        return inheritedFromParentAttributeName;
504                }
505
506                if (embeddedAttribute != null) {
507                        return embeddedAttribute.getInheritedFromParentAttributeName();
508                }
509
510                return null;
511        }
512
513    /**
514    * Sets parent data object name to determine if inherited.
515    *
516    * @param inheritedFromParentAttributeName name of attribute.
517    */
518        public void setInheritedFromParentAttributeName(String inheritedFromParentAttributeName) {
519                this.inheritedFromParentAttributeName = inheritedFromParentAttributeName;
520        }
521
522    /**
523     * {@inheritDoc}
524     */
525        @Override
526        public boolean isInherited() {
527                return getInheritedFromAttributeName() != null;
528        }
529
530    /**
531     * {@inheritDoc}
532     */
533        @Override
534        public DataObjectAttribute getOriginalDataObjectAttribute() {
535                if (embeddedAttribute == null) {
536                        return this;
537                }
538                return embeddedAttribute.getOriginalDataObjectAttribute();
539        }
540
541    /**
542     * {@inheritDoc}
543     */
544        @Override
545        public Long getMinLength() {
546                if (minLength != null) {
547                        return minLength;
548                }
549                if (embeddedAttribute != null) {
550                        return embeddedAttribute.getMinLength();
551                }
552                return null;
553        }
554
555    /**
556    * Sets minimum length of attribute.
557    *
558    * @param minLength minimum length value.
559    */
560        public void setMinLength(Long minLength) {
561                this.minLength = minLength;
562        }
563
564    /**
565     * {@inheritDoc}
566     */
567        @Override
568        public boolean isSensitive() {
569                if (sensitive != null) {
570                        return sensitive;
571                }
572
573                if (embeddedAttribute != null) {
574                        return embeddedAttribute.isSensitive();
575                }
576
577                return false;
578        }
579
580    /**
581    * Sets whether sensitive.
582    *
583    * @param sensitive whether attribute is sensitive.
584    */
585        public void setSensitive(boolean sensitive) {
586                this.sensitive = sensitive;
587        }
588
589    /**
590     * {@inheritDoc}
591     */
592    @Override
593    @Beta
594        public Set<UifDisplayHint> getDisplayHints() {
595                if (displayHints != null) {
596                        return displayHints;
597                }
598
599                if (embeddedAttribute != null) {
600                        return embeddedAttribute.getDisplayHints();
601                }
602
603                return Collections.emptySet();
604        }
605
606    /**
607    * BETA: Sets UIF display hints.
608    *
609    * @param displayHints UIF display hints.
610    */
611    @Beta
612        public void setDisplayHints(Set<UifDisplayHint> displayHints) {
613                this.displayHints = displayHints;
614        }
615}