001 /*
002 * Copyright 2006-2007 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 */
016
017 package org.kuali.rice.kns.datadictionary;
018
019 import java.util.ArrayList;
020 import java.util.List;
021
022 import org.apache.commons.lang.StringUtils;
023 import org.kuali.rice.kns.bo.BusinessObject;
024 import org.kuali.rice.kns.datadictionary.exception.AttributeValidationException;
025
026 /**
027 * A single Relationship definition in the DataDictionary, which contains information concerning which primitive attributes of this
028 * class can be used to retrieve an instance of some related Object instance
029 *
030 The relationship element defines how primitive attributes of this
031 class can be used to retrieve an instance of some related Object instance
032 DD: See RelationshipDefinition.java.
033
034 JSTL: relationship is a Map which is accessed using a key which is the
035 objectAttributeName of a relationship. The map contains a single entry
036 with a key of "primitiveAttributes" and value which is an attributesMap ExportMap.
037
038 The attributesMap ExportMap contains the following keys:
039 * 0 (for first primitiveAttribute)
040 * 1 (for second primitiveAttribute)
041 etc.
042 The corresponding value for each entry is an primitiveAttribute ExportMap
043 which contains the following keys:
044 * "sourceName"
045 * "targetName"
046 *
047 */
048 public class RelationshipDefinition extends DataDictionaryDefinitionBase {
049 private static final long serialVersionUID = 2946722646095412576L;
050
051 protected String objectAttributeName; //Same as parentAttributeName of BusinessObjectRelationship
052 protected Class<? extends BusinessObject> sourceClass; //parentClass
053 /**
054 * 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
055 * of the collection
056 */
057 protected Class<? extends BusinessObject> targetClass; //relatedClass
058
059 protected List<PrimitiveAttributeDefinition> primitiveAttributes = new ArrayList<PrimitiveAttributeDefinition>(); //parentToChildReferences
060 protected List<SupportAttributeDefinition> supportAttributes = new ArrayList<SupportAttributeDefinition>(); //parentToChildReferences
061
062
063 public RelationshipDefinition() {}
064
065 public String getObjectAttributeName() {
066 return objectAttributeName;
067 }
068
069 public Class<? extends BusinessObject> getSourceClass() {
070 return sourceClass;
071 }
072
073 /**
074 * Returns the {@link #targetClass}
075 *
076 * @param targetClass
077 */
078 public Class<? extends BusinessObject> getTargetClass() {
079 if (targetClass == null) {
080 Class propertyClass = DataDictionary.getAttributeClass(sourceClass, objectAttributeName);
081 if (propertyClass == null) {
082 throw new AttributeValidationException("cannot get valid class for property '" + objectAttributeName + "' as an attribute of '" + sourceClass + "'");
083 }
084 if (!BusinessObject.class.isAssignableFrom(propertyClass)) {
085 throw new AttributeValidationException("property '" + objectAttributeName + "' is not a BusinessObject (" + propertyClass.getName() + ")");
086 }
087
088
089 targetClass = propertyClass;
090 }
091 return targetClass;
092 }
093
094 /**
095 * Sets the {@link #targetClass}
096 *
097 * @param targetClass
098 */
099 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