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