View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.datadictionary;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
23  
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  /**
28   * A single Relationship definition in the DataDictionary, which contains information concerning which primitive
29   * attributes of this
30   * class can be used to retrieve an instance of some related Object instance
31   *
32   * The relationship element defines how primitive attributes of this
33   * class can be used to retrieve an instance of some related Object instance
34   * DD: See RelationshipDefinition.java.
35   *
36   * JSTL: relationship is a Map which is accessed using a key which is the
37   * objectAttributeName of a relationship.  The map contains a single entry
38   * with a key of "primitiveAttributes" and value which is an attributesMap ExportMap.
39   *
40   * The attributesMap ExportMap contains the following keys:
41   * 0   (for first primitiveAttribute)
42   * 1   (for second primitiveAttribute)
43   * etc.
44   * The corresponding value for each entry is an primitiveAttribute ExportMap
45   * which contains the following keys:
46   * "sourceName"
47   * "targetName"
48   */
49  @BeanTag(name = "relationshipDefinition-bean")
50  public class RelationshipDefinition extends DataDictionaryDefinitionBase {
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          if (targetClass == null) {
86              Class propertyClass = DataDictionary.getAttributeClass(sourceClass, objectAttributeName);
87              if (propertyClass == null) {
88                  throw new AttributeValidationException("cannot get valid class for property '"
89                          + objectAttributeName
90                          + "' as an attribute of '"
91                          + sourceClass
92                          + "'");
93              }
94  
95              targetClass = propertyClass;
96          }
97          return targetClass;
98      }
99  
100     /**
101      * Sets the {@link #targetClass}
102      *
103      * @param targetClass
104      */
105     public void setTargetClass(Class<?> targetClass) {
106         this.targetClass = targetClass;
107     }
108 
109     /**
110      * Name of the business object property on the containing business object that is linked
111      * by the contained PrimitiveAttributeDefinition objects.
112      */
113     public void setObjectAttributeName(String objectAttributeName) {
114         if (StringUtils.isBlank(objectAttributeName)) {
115             throw new IllegalArgumentException("invalid (blank) objectAttributeName");
116         }
117 
118         this.objectAttributeName = objectAttributeName;
119     }
120 
121     @BeanTagAttribute(name = "primitiveAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN)
122     public List<PrimitiveAttributeDefinition> getPrimitiveAttributes() {
123         return primitiveAttributes;
124     }
125 
126     @BeanTagAttribute(name = "supportAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN)
127     public List<SupportAttributeDefinition> getSupportAttributes() {
128         return supportAttributes;
129     }
130 
131     public boolean hasIdentifier() {
132         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
133             if (supportAttributeDefinition.isIdentifier()) {
134                 return true;
135             }
136         }
137         return false;
138     }
139 
140     public SupportAttributeDefinition getIdentifier() {
141         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
142             if (supportAttributeDefinition.isIdentifier()) {
143                 return supportAttributeDefinition;
144             }
145         }
146         return null;
147     }
148 
149     /**
150      * Directly validate simple fields, call completeValidation on Definition fields.
151      *
152      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
153      */
154     public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass) {
155         String propertyName = objectAttributeName;
156         if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, propertyName)) {
157             throw new AttributeValidationException("property '"
158                     + propertyName
159                     + "' is not an attribute of class '"
160                     + rootBusinessObjectClass
161                     + "' ("
162                     + ""
163                     + ")");
164         }
165 
166         getTargetClass(); // performs validation when this is called the first time
167 
168         for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) {
169             primitiveAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass);
170         }
171         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
172             supportAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass);
173         }
174     }
175 
176     /**
177      * Directly validate simple fields
178      *
179      * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation(org.kuali.rice.krad.datadictionary.validator.ValidationTrace)
180      */
181     public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass,
182             ValidationTrace tracer) {
183         tracer.addBean(this.getClass().getSimpleName(), "Attribute: " + getObjectAttributeName());
184         try {
185             if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getObjectAttributeName())) {
186                 String currentValues[] =
187                         {"property = " + getObjectAttributeName(), "Class =" + rootBusinessObjectClass};
188                 tracer.createError("Property is not an attribute of the class", currentValues);
189             }
190         } catch (RuntimeException ex) {
191             String currentValues[] = {"attribute = " + getObjectAttributeName(), "Exception = " + ex.getMessage()};
192             tracer.createError("Unable to validate attribute", currentValues);
193         }
194 
195         if (targetClass == null) {
196             Class propertyClass = DataDictionary.getAttributeClass(sourceClass, objectAttributeName);
197             if (propertyClass == null) {
198                 String currentValues[] =
199                         {"property = " + getObjectAttributeName(), "sourceClass = " + getSourceClass()};
200                 tracer.createError("Cannot get valid class for property", currentValues);
201             } else {
202                 targetClass = propertyClass;
203             }
204         }
205 
206         for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) {
207             primitiveAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy());
208         }
209         for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) {
210             supportAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy());
211         }
212     }
213 
214     /**
215      * @see java.lang.Object#toString()
216      */
217     @Override
218     public String toString() {
219         return "RelationshipDefinition for relationship " + getObjectAttributeName();
220     }
221 
222     /**
223      * The primitiveAttribute element identifies one pair of
224      * corresponding fields in the primary business object and
225      * the related business object.
226      *
227      * JSTL: primitiveAttribute is a Map which is accessed by the
228      * sequential key of "0", "1", etc.  Each entry contains the following
229      * keys:
230      * sourceName (String)
231      * targetName (String)
232      * The value corresponding to the sourceName key is the attribute name defined
233      * for the primary business object.
234      * The value corresponding to the targetName key is the attribute name for
235      * the object being referenced by objectAttributeName.
236      */
237     public void setPrimitiveAttributes(List<PrimitiveAttributeDefinition> primitiveAttributes) {
238         this.primitiveAttributes = primitiveAttributes;
239     }
240 
241     /**
242      * Support attributes define additional attributes that can be used to generate
243      * lookup field conversions and lookup parameters.
244      *
245      * Field conversions and lookup parameters are normally generated using foreign key relationships
246      * defined within OJB and the DD.  Because Person objects are linked in a special way (i.e. they may
247      * come from an external data source and not from the DB, such as LDAP), it is often necessary to define
248      * extra fields that are related to each other, sort of like a supplemental foreign key.
249      *
250      * sourceName is the name of the POJO property of the business object
251      * targetName is the name of attribute that corresponds to the sourceName in the looked up BO
252      * identifier when true, only the field marked as an identifier will be passed in as a lookup parameter
253      * at most one supportAttribute for each relationship should be defined as identifier="true"
254      */
255     public void setSupportAttributes(List<SupportAttributeDefinition> supportAttributes) {
256         this.supportAttributes = supportAttributes;
257     }
258 
259     /**
260      * @param sourceClass the sourceClass to set
261      */
262     public void setSourceClass(Class<?> sourceClass) {
263         this.sourceClass = sourceClass;
264     }
265 }
266