001 /* 002 * Copyright 2005-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 package org.kuali.rice.kns.datadictionary; 017 018 import java.util.ArrayList; 019 import java.util.LinkedHashMap; 020 import java.util.List; 021 import java.util.Map; 022 import java.util.Set; 023 024 import org.apache.commons.lang.StringUtils; 025 import org.kuali.rice.kns.datadictionary.exception.DuplicateEntryException; 026 import org.kuali.rice.kns.exception.ValidationException; 027 import org.springframework.beans.BeanUtils; 028 import org.springframework.beans.factory.InitializingBean; 029 030 /** 031 * Contains common properties and methods for data dictionary entries. 032 * 033 * 034 */ 035 abstract public class DataDictionaryEntryBase implements DataDictionaryEntry, InitializingBean { 036 037 protected List<AttributeDefinition> attributes; 038 protected List<ComplexAttributeDefinition> complexAttributes; 039 protected List<CollectionDefinition> collections; 040 protected List<RelationshipDefinition> relationships; 041 protected Map<String, AttributeDefinition> attributeMap; 042 protected Map<String, ComplexAttributeDefinition> complexAttributeMap; 043 protected Map<String, CollectionDefinition> collectionMap; 044 protected Map<String, RelationshipDefinition> relationshipMap; 045 046 public DataDictionaryEntryBase() { 047 this.attributes = new ArrayList<AttributeDefinition>(); 048 this.collections = new ArrayList<CollectionDefinition>(); 049 this.relationships = new ArrayList<RelationshipDefinition>(); 050 this.attributeMap = new LinkedHashMap<String, AttributeDefinition>(); 051 this.collectionMap = new LinkedHashMap<String, CollectionDefinition>(); 052 this.relationshipMap = new LinkedHashMap<String, RelationshipDefinition>(); 053 } 054 055 /* Returns the given entry class (bo class or document class) */ 056 public abstract Class<?> getEntryClass(); 057 058 /** 059 * @param attributeName 060 * @return AttributeDefinition with the given name, or null if none with that name exists 061 */ 062 public AttributeDefinition getAttributeDefinition(String attributeName) { 063 if (StringUtils.isBlank(attributeName)) { 064 throw new IllegalArgumentException("invalid (blank) attributeName"); 065 } 066 return attributeMap.get(attributeName); 067 } 068 069 /** 070 * @return a Map containing all AttributeDefinitions associated with this BusinessObjectEntry, indexed by attributeName 071 */ 072 public List<AttributeDefinition> getAttributes() { 073 return this.attributes; 074 } 075 076 /** 077 * @return the complexAttributes 078 */ 079 public List<ComplexAttributeDefinition> getComplexAttributes() { 080 return this.complexAttributes; 081 } 082 083 /** 084 * @param complexAttributes the complexAttributes to set 085 */ 086 public void setComplexAttributes( 087 List<ComplexAttributeDefinition> complexAttributes) { 088 this.complexAttributes = complexAttributes; 089 } 090 091 /** 092 * @param collectionName 093 * @return CollectionDefinition with the given name, or null if none with that name exists 094 */ 095 public CollectionDefinition getCollectionDefinition(String collectionName) { 096 if (StringUtils.isBlank(collectionName)) { 097 throw new IllegalArgumentException("invalid (blank) collectionName"); 098 } 099 return collectionMap.get(collectionName); 100 } 101 102 /** 103 * @return a Map containing all CollectionDefinitions associated with this BusinessObjectEntry, indexed by collectionName 104 */ 105 public List<CollectionDefinition> getCollections() { 106 return this.collections; 107 } 108 109 /** 110 * @param relationshipName 111 * @return RelationshipDefinition with the given name, or null if none with that name exists 112 */ 113 public RelationshipDefinition getRelationshipDefinition(String relationshipName) { 114 if (StringUtils.isBlank(relationshipName)) { 115 throw new IllegalArgumentException("invalid (blank) relationshipName"); 116 } 117 return relationshipMap.get(relationshipName); 118 } 119 120 /** 121 * @return a Map containing all RelationshipDefinitions associated with this BusinessObjectEntry, indexed by relationshipName 122 */ 123 public List<RelationshipDefinition> getRelationships() { 124 return this.relationships; 125 } 126 127 128 /** 129 * Directly validate simple fields, call completeValidation on Definition fields. 130 */ 131 public void completeValidation() { 132 133 for ( AttributeDefinition attributeDefinition : attributes ) { 134 attributeDefinition.completeValidation(getEntryClass(), null); 135 } 136 137 for ( CollectionDefinition collectionDefinition : collections ) { 138 collectionDefinition.completeValidation(getEntryClass(), null); 139 } 140 141 for ( RelationshipDefinition relationshipDefinition : relationships ) { 142 relationshipDefinition.completeValidation(getEntryClass(), null); 143 } 144 } 145 146 /** 147 The attributes element contains attribute 148 elements. These define the specifications for business object fields. 149 150 JSTL: attributes is a Map which is accessed by a key of "attributes". 151 This map contains entries with the following keys: 152 * attributeName of first attribute 153 * attributeName of second attribute 154 etc. 155 156 The corresponding value for each entry is an attribute ExportMap. 157 By the time the JSTL export happens, all attributeReferences will be 158 indistinguishable from attributes. 159 160 See AttributesMapBuilder.java 161 162 The attribute element specifies the way in which a business object 163 field appears on a screen for data entry or display purposes. These 164 specifications include the following: 165 * The title and formatting of the field 166 * Descriptive information about the field 167 * The edits used at time of data-entry 168 169 DD: See AttributeDefinition.java 170 171 JSTL: attribute is a Map which is accessed using a key which is the attributeName 172 of an attribute. Each entry contains the following keys: 173 * name (String) 174 * forceUppercase (boolean String) 175 * label (String) 176 * shortLabel (String, copied from label if not present) 177 * maxLength (String) 178 * exclusiveMin (bigdecimal String) 179 * exclusiveMax (bigdecimal String) 180 * validationPattern (Map, optional) 181 * required (boolean String) 182 * control (Map) 183 * summary (String) 184 * description (String) 185 * formatterClass (String, optional) 186 * fullClassName (String) 187 * displayWorkgroup(String, optional) 188 * displayMaskClass(String, optional) 189 190 See AttributesMapBuilder.java 191 Note: exclusiveMax is mapped from the inclusiveMax element! 192 The validation logic seems to be assuming inclusiveMax. 193 * 194 */ 195 public void setAttributes(List<AttributeDefinition> attributes) { 196 attributeMap.clear(); 197 for ( AttributeDefinition attribute : attributes ) { 198 if (attribute == null) { 199 throw new IllegalArgumentException("invalid (null) attributeDefinition"); 200 } 201 String attributeName = attribute.getName(); 202 if (StringUtils.isBlank(attributeName)) { 203 throw new ValidationException("invalid (blank) attributeName"); 204 } 205 206 if (attributeMap.containsKey(attributeName)) { 207 throw new DuplicateEntryException("collection '" + attributeName + "' already defined as an Attribute for class '" + getEntryClass().getName() + "'"); 208 } else if (collectionMap.containsKey(attributeName)) { 209 throw new DuplicateEntryException("attribute '" + attributeName + "' already defined as a Collection for class '" + getEntryClass().getName() + "'"); 210 } 211 212 attributeMap.put(attributeName, attribute); 213 } 214 this.attributes = attributes; 215 } 216 217 /** 218 The collections element contains collection elements. These define 219 the lists of other business objects which are related to and 220 defined in the business objects. 221 222 JSTL: collections is a Map which is accessed by a key of "collections". 223 This map contains entries with the following keys: 224 * name of first collection 225 * name of second collection 226 etc. 227 The corresponding value for each entry is a collection ExportMap. 228 229 The collection element defines the name and description a 230 list of objects related to the business object. 231 232 DD: See CollectionDefinition.java. 233 234 JSTL: collection is a Map which is accessed using a key which is the 235 name of the collection. Each entry contains the following keys: 236 * name (String) 237 * label (String) 238 * shortLabel (String, copied from label if missing) 239 * elementLabel (String, copied from contained class if missing) 240 * summary (String) 241 * description (String) 242 243 See CollectionsMapBuilder.java. 244 */ 245 public void setCollections(List<CollectionDefinition> collections) { 246 collectionMap.clear(); 247 for ( CollectionDefinition collection : collections ) { 248 if (collection == null) { 249 throw new IllegalArgumentException("invalid (null) collectionDefinition"); 250 } 251 String collectionName = collection.getName(); 252 if (StringUtils.isBlank(collectionName)) { 253 throw new ValidationException("invalid (blank) collectionName"); 254 } 255 256 if (collectionMap.containsKey(collectionName)) { 257 throw new DuplicateEntryException("collection '" + collectionName + "' already defined for class '" + getEntryClass().getName() + "'"); 258 } else if (attributeMap.containsKey(collectionName)) { 259 throw new DuplicateEntryException("collection '" + collectionName + "' already defined as an Attribute for class '" + getEntryClass().getName() + "'"); 260 } 261 262 collectionMap.put(collectionName, collection); 263 264 } 265 this.collections = collections; 266 } 267 268 /** 269 The relationships element contains relationship elements. 270 These are used to map attribute names to fields in a reference object. 271 272 JSTL: relationships is a Map which is accessed by a key of "relationships". 273 This map contains entries with the following keys: 274 * objectAttributeName of first relationship 275 * objectAttributeName of second relationship 276 etc. 277 The corresponding value for each entry is a relationship ExportMap. 278 279 The relationship element defines how primitive attributes of this 280 class can be used to retrieve an instance of some related Object instance 281 DD: See RelationshipDefinition.java. 282 283 JSTL: relationship is a Map which is accessed using a key which is the 284 objectAttributeName of a relationship. The map contains a single entry 285 with a key of "primitiveAttributes" and value which is an attributesMap ExportMap. 286 287 The attributesMap ExportMap contains the following keys: 288 * 0 (for first primitiveAttribute) 289 * 1 (for second primitiveAttribute) 290 etc. 291 The corresponding value for each entry is an primitiveAttribute ExportMap 292 which contains the following keys: 293 * "sourceName" 294 * "targetName" 295 296 See RelationshipsMapBuilder.java. 297 298 */ 299 public void setRelationships(List<RelationshipDefinition> relationships) { 300 this.relationships = relationships; 301 } 302 303 public Set<String> getCollectionNames() { 304 return collectionMap.keySet(); 305 } 306 307 public Set<String> getAttributeNames() { 308 return attributeMap.keySet(); 309 } 310 311 public Set<String> getRelationshipNames() { 312 return relationshipMap.keySet(); 313 } 314 315 /** 316 * This overridden method ... 317 * 318 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 319 */ 320 public void afterPropertiesSet() throws Exception { 321 if ( relationships != null ) { 322 relationshipMap.clear(); 323 for ( RelationshipDefinition relationship : relationships ) { 324 if (relationship == null) { 325 throw new IllegalArgumentException("invalid (null) relationshipDefinition"); 326 } 327 String relationshipName = relationship.getObjectAttributeName(); 328 if (StringUtils.isBlank(relationshipName)) { 329 throw new ValidationException("invalid (blank) relationshipName"); 330 } 331 relationship.setSourceClass(getEntryClass()); 332 relationshipMap.put(relationshipName, relationship); 333 } 334 } 335 336 //Populate attributes with nested attribute definitions 337 if (complexAttributes != null){ 338 for (ComplexAttributeDefinition complexAttribute:complexAttributes){ 339 addNestedAttributes(complexAttribute, complexAttribute.getName()); 340 } 341 } 342 } 343 344 private void addNestedAttributes(ComplexAttributeDefinition complexAttribute, String attrPath){ 345 DataDictionaryEntryBase dataDictionaryEntry = (DataDictionaryEntryBase)complexAttribute.getDataObjectEntry(); 346 347 //Add attributes for the complex attibutes 348 for (AttributeDefinition attribute:dataDictionaryEntry.getAttributes()){ 349 String nestedAttributeName = attrPath + "." + attribute.getName(); 350 AttributeDefinition nestedAttribute = copyAttributeDefinition(attribute); 351 nestedAttribute.setName(nestedAttributeName); 352 353 if (!attributeMap.containsValue(nestedAttributeName)){ 354 this.attributes.add(nestedAttribute); 355 this.attributeMap.put(nestedAttributeName, nestedAttribute); 356 } 357 } 358 359 //Recursively add complex attributes 360 List<ComplexAttributeDefinition> nestedComplexAttributes = dataDictionaryEntry.getComplexAttributes(); 361 if (nestedComplexAttributes != null){ 362 for (ComplexAttributeDefinition nestedComplexAttribute:nestedComplexAttributes){ 363 addNestedAttributes(nestedComplexAttribute, attrPath); 364 } 365 } 366 } 367 368 private AttributeDefinition copyAttributeDefinition(AttributeDefinition attrDefToCopy){ 369 AttributeDefinition attrDefCopy = new AttributeDefinition(); 370 371 try { 372 373 BeanUtils.copyProperties(attrDefToCopy, attrDefToCopy, new String[] { "formatterClass" }); 374 } catch (Exception e) { 375 e.printStackTrace(); 376 } 377 378 return attrDefCopy; 379 } 380 }