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