View Javadoc
1   /**
2    * Copyright 2005-2014 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 java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
24  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
25  
26  /**
27   * A single Relationship definition in the DataDictionary, which contains information concerning which primitive
28   * attributes of this
29   * class can be used to retrieve an instance of some related Object instance
30   *
31   * The relationship element defines how primitive attributes of this
32   * class can be used to retrieve an instance of some related Object instance
33   * DD: See RelationshipDefinition.java.
34   *
35   * JSTL: relationship is a Map which is accessed using a key which is the
36   * objectAttributeName of a relationship.  The map contains a single entry
37   * with a key of "primitiveAttributes" and value which is an attributesMap ExportMap.
38   *
39   * The attributesMap ExportMap contains the following keys:
40   * 0   (for first primitiveAttribute)
41   * 1   (for second primitiveAttribute)
42   * etc.
43   * The corresponding value for each entry is an primitiveAttribute ExportMap
44   * which contains the following keys:
45   * "sourceName"
46   * "targetName"
47   */
48  @BeanTag(name = "relationshipDefinition")
49  public class RelationshipDefinition extends DataDictionaryDefinitionBase {
50      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RelationshipDefinition.class);
51      private static final long serialVersionUID = 2946722646095412576L;
52  
53      protected String objectAttributeName; //Same as parentAttributeName of DataObjectRelationship
54      protected Class<?> sourceClass; //parentClass
55  
56      /**
57       * For 1:1 relationships, this class represents the type of the reference class.  For 1:n references, this class
58       * represents the type of the element
59       * of the collection
60       */
61      protected Class<?> targetClass; //relatedClass
62  
63      protected List<PrimitiveAttributeDefinition> primitiveAttributes = new ArrayList<PrimitiveAttributeDefinition>();
64      //parentToChildReferences
65      protected List<SupportAttributeDefinition> supportAttributes = new ArrayList<SupportAttributeDefinition>();
66      //parentToChildReferences
67  
68      public RelationshipDefinition() {}
69  
70      @BeanTagAttribute(name = "objectAttributeName")
71      public String getObjectAttributeName() {
72          return objectAttributeName;
73      }
74  
75      @BeanTagAttribute(name = "sourceClass")
76      public Class<?> getSourceClass() {
77          return sourceClass;
78      }
79  
80      /**
81       * Returns the {@link #targetClass}
82       */
83      @BeanTagAttribute(name = "targetClass")
84      public Class<?> getTargetClass() {
85          return targetClass;
86      }
87  
88      /**
89       * Sets the {@link #targetClass}
90       *
91       * @param targetClass
92       */
93      public void setTargetClass(Class<?> targetClass) {
94          this.targetClass = targetClass;
95      }
96  
97      /**
98       * Name of the business object property on the containing business object that is linked
99       * by the contained PrimitiveAttributeDefinition objects.
100      */
101     public void setObjectAttributeName(String objectAttributeName) {
102         if (StringUtils.isBlank(objectAttributeName)) {
103             throw new IllegalArgumentException("invalid (blank) objectAttributeName");
104         }
105 
106         this.objectAttributeName = objectAttributeName;
107     }
108 
109     @BeanTagAttribute(name = "primitiveAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN)
110     public List<PrimitiveAttributeDefinition> getPrimitiveAttributes() {
111         return primitiveAttributes;
112     }
113 
114     @BeanTagAttribute(name = "supportAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN)
115     public List<SupportAttributeDefinition> getSupportAttributes() {
116         return supportAttributes;
117     }
118 
119     public boolean hasIdentifier() {
120         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
121             if (supportAttributeDefinition.isIdentifier()) {
122                 return true;
123             }
124         }
125         return false;
126     }
127 
128     public SupportAttributeDefinition getIdentifier() {
129         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
130             if (supportAttributeDefinition.isIdentifier()) {
131                 return supportAttributeDefinition;
132             }
133         }
134         return null;
135     }
136 
137     /**
138      * This overridden method ...
139      *
140      * @see org.kuali.rice.krad.datadictionary.DictionaryBeanBase#dataDictionaryPostProcessing()
141      */
142     @Override
143     public void dataDictionaryPostProcessing() {
144         super.dataDictionaryPostProcessing();
145         if (targetClass == null) {
146             Class<?> propertyClass = DataDictionary.getAttributeClass(sourceClass, objectAttributeName);
147             if (propertyClass != null) {
148                 targetClass = propertyClass;
149             }
150         }
151         for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) {
152             primitiveAttributeDefinition.dataDictionaryPostProcessing();
153         }
154         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
155             supportAttributeDefinition.dataDictionaryPostProcessing();
156         }
157     }
158 
159     /**
160      * Directly validate simple fields, call completeValidation on Definition fields.
161      *
162      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
163      */
164     @Override
165     @Deprecated
166     public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass) {
167         completeValidation(rootBusinessObjectClass, otherBusinessObjectClass, new ValidationTrace());
168     }
169 
170     /**
171      * Directly validate simple fields
172      *
173      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation(org.kuali.rice.krad.datadictionary.validator.ValidationTrace)
174      */
175     @Override
176     public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass,
177             ValidationTrace tracer) {
178         tracer.addBean(this.getClass().getSimpleName(), "Attribute: " + getObjectAttributeName());
179         try {
180             if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getObjectAttributeName())) {
181                 String currentValues[] =
182                         {"property = " + getObjectAttributeName(), "Class =" + rootBusinessObjectClass};
183                 tracer.createError("Property is not an attribute of the class", currentValues);
184             }
185         } catch (RuntimeException ex) {
186             String currentValues[] = {"attribute = " + getObjectAttributeName(), "Exception = " + ex.getMessage()};
187             tracer.createError("Unable to validate attribute", currentValues);
188             LOG.error( "Exception while validating attribute: " + getObjectAttributeName(), ex );
189         }
190 
191         if (targetClass == null) {
192             String currentValues[] =
193                     {"property = " + getObjectAttributeName(), "sourceClass = " + getSourceClass()};
194             tracer.createError("Cannot get valid class for property", currentValues);
195         } else {
196 
197             for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) {
198                 primitiveAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy());
199             }
200             for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
201                 supportAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy());
202             }
203         }
204     }
205 
206     /**
207      * The primitiveAttribute element identifies one pair of
208      * corresponding fields in the primary business object and
209      * the related business object.
210      *
211      * JSTL: primitiveAttribute is a Map which is accessed by the
212      * sequential key of "0", "1", etc.  Each entry contains the following
213      * keys:
214      * sourceName (String)
215      * targetName (String)
216      * The value corresponding to the sourceName key is the attribute name defined
217      * for the primary business object.
218      * The value corresponding to the targetName key is the attribute name for
219      * the object being referenced by objectAttributeName.
220      */
221     public void setPrimitiveAttributes(List<PrimitiveAttributeDefinition> primitiveAttributes) {
222         this.primitiveAttributes = primitiveAttributes;
223     }
224 
225     /**
226      * Support attributes define additional attributes that can be used to generate
227      * lookup field conversions and lookup parameters.
228      *
229      * Field conversions and lookup parameters are normally generated using foreign key relationships
230      * defined within OJB and the DD.  Because Person objects are linked in a special way (i.e. they may
231      * come from an external data source and not from the DB, such as LDAP), it is often necessary to define
232      * extra fields that are related to each other, sort of like a supplemental foreign key.
233      *
234      * sourceName is the name of the POJO property of the business object
235      * targetName is the name of attribute that corresponds to the sourceName in the looked up BO
236      * identifier when true, only the field marked as an identifier will be passed in as a lookup parameter
237      * at most one supportAttribute for each relationship should be defined as identifier="true"
238      */
239     public void setSupportAttributes(List<SupportAttributeDefinition> supportAttributes) {
240         this.supportAttributes = supportAttributes;
241     }
242 
243     /**
244      * @param sourceClass the sourceClass to set
245      */
246     public void setSourceClass(Class<?> sourceClass) {
247         this.sourceClass = sourceClass;
248     }
249 
250     @Override
251     public String toString() {
252         StringBuilder builder = new StringBuilder();
253         builder.append("RelationshipDefinition [objectAttributeName=").append(this.objectAttributeName)
254                 .append(", sourceClass=").append(this.sourceClass).append(", targetClass=").append(this.targetClass)
255                 .append(", primitiveAttributes=").append(this.primitiveAttributes).append(", supportAttributes=")
256                 .append(this.supportAttributes).append("]");
257         return builder.toString();
258     }
259 }
260