001/** 002 * Copyright 2005-2018 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.util.ArrayList; 019import java.util.List; 020 021import org.apache.commons.lang.StringUtils; 022import org.kuali.rice.krad.datadictionary.parse.BeanTag; 023import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 024import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 025 026/** 027 * A single Relationship definition in the DataDictionary, which contains information concerning which primitive 028 * attributes of this 029 * class can be used to retrieve an instance of some related Object instance 030 * 031 * The relationship element defines how primitive attributes of this 032 * class can be used to retrieve an instance of some related Object instance 033 * DD: See RelationshipDefinition.java. 034 * 035 * JSTL: relationship is a Map which is accessed using a key which is the 036 * objectAttributeName of a relationship. The map contains a single entry 037 * with a key of "primitiveAttributes" and value which is an attributesMap ExportMap. 038 * 039 * The attributesMap ExportMap contains the following keys: 040 * 0 (for first primitiveAttribute) 041 * 1 (for second primitiveAttribute) 042 * etc. 043 * The corresponding value for each entry is an primitiveAttribute ExportMap 044 * which contains the following keys: 045 * "sourceName" 046 * "targetName" 047 */ 048@BeanTag(name = "relationshipDefinition") 049public class RelationshipDefinition extends DataDictionaryDefinitionBase { 050 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RelationshipDefinition.class); 051 private static final long serialVersionUID = 2946722646095412576L; 052 053 protected String objectAttributeName; //Same as parentAttributeName of DataObjectRelationship 054 protected Class<?> sourceClass; //parentClass 055 056 /** 057 * For 1:1 relationships, this class represents the type of the reference class. For 1:n references, this class 058 * represents the type of the element 059 * of the collection 060 */ 061 protected Class<?> targetClass; //relatedClass 062 063 protected List<PrimitiveAttributeDefinition> primitiveAttributes = new ArrayList<PrimitiveAttributeDefinition>(); 064 //parentToChildReferences 065 protected List<SupportAttributeDefinition> supportAttributes = new ArrayList<SupportAttributeDefinition>(); 066 //parentToChildReferences 067 068 public RelationshipDefinition() {} 069 070 @BeanTagAttribute(name = "objectAttributeName") 071 public String getObjectAttributeName() { 072 return objectAttributeName; 073 } 074 075 @BeanTagAttribute(name = "sourceClass") 076 public Class<?> getSourceClass() { 077 return sourceClass; 078 } 079 080 /** 081 * Returns the {@link #targetClass} 082 */ 083 @BeanTagAttribute(name = "targetClass") 084 public Class<?> getTargetClass() { 085 return targetClass; 086 } 087 088 /** 089 * Sets the {@link #targetClass} 090 * 091 * @param targetClass 092 */ 093 public void setTargetClass(Class<?> targetClass) { 094 this.targetClass = targetClass; 095 } 096 097 /** 098 * Name of the business object property on the containing business object that is linked 099 * by the contained PrimitiveAttributeDefinition objects. 100 */ 101 public void setObjectAttributeName(String objectAttributeName) { 102 if (StringUtils.isBlank(objectAttributeName)) { 103 throw new IllegalArgumentException("invalid (blank) objectAttributeName"); 104 } 105 106 this.objectAttributeName = objectAttributeName; 107 } 108 109 @BeanTagAttribute(name = "primitiveAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN) 110 public List<PrimitiveAttributeDefinition> getPrimitiveAttributes() { 111 return primitiveAttributes; 112 } 113 114 @BeanTagAttribute(name = "supportAttributes", type = BeanTagAttribute.AttributeType.LISTBEAN) 115 public List<SupportAttributeDefinition> getSupportAttributes() { 116 return supportAttributes; 117 } 118 119 public boolean hasIdentifier() { 120 for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) { 121 if (supportAttributeDefinition.isIdentifier()) { 122 return true; 123 } 124 } 125 return false; 126 } 127 128 public SupportAttributeDefinition getIdentifier() { 129 for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) { 130 if (supportAttributeDefinition.isIdentifier()) { 131 return supportAttributeDefinition; 132 } 133 } 134 return null; 135 } 136 137 /** 138 * This overridden method ... 139 * 140 * @see org.kuali.rice.krad.datadictionary.DictionaryBeanBase#dataDictionaryPostProcessing() 141 */ 142 @Override 143 public void dataDictionaryPostProcessing() { 144 super.dataDictionaryPostProcessing(); 145 if (targetClass == null) { 146 Class<?> propertyClass = DataDictionary.getAttributeClass(sourceClass, objectAttributeName); 147 if (propertyClass != null) { 148 targetClass = propertyClass; 149 } 150 } 151 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) { 152 primitiveAttributeDefinition.dataDictionaryPostProcessing(); 153 } 154 for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) { 155 supportAttributeDefinition.dataDictionaryPostProcessing(); 156 } 157 } 158 159 /** 160 * Directly validate simple fields, call completeValidation on Definition fields. 161 * 162 * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation() 163 */ 164 @Override 165 @Deprecated 166 public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass) { 167 completeValidation(rootBusinessObjectClass, otherBusinessObjectClass, new ValidationTrace()); 168 } 169 170 /** 171 * Directly validate simple fields 172 * 173 * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation(org.kuali.rice.krad.datadictionary.validator.ValidationTrace) 174 */ 175 @Override 176 public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass, 177 ValidationTrace tracer) { 178 tracer.addBean(this.getClass().getSimpleName(), "Attribute: " + getObjectAttributeName()); 179 try { 180 if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getObjectAttributeName())) { 181 String currentValues[] = 182 {"property = " + getObjectAttributeName(), "Class =" + rootBusinessObjectClass}; 183 tracer.createError("Property is not an attribute of the class", currentValues); 184 } 185 } catch (RuntimeException ex) { 186 String currentValues[] = {"attribute = " + getObjectAttributeName(), "Exception = " + ex.getMessage()}; 187 tracer.createError("Unable to validate attribute", currentValues); 188 LOG.error( "Exception while validating attribute: " + getObjectAttributeName(), ex ); 189 } 190 191 if (targetClass == null) { 192 String currentValues[] = 193 {"property = " + getObjectAttributeName(), "sourceClass = " + getSourceClass()}; 194 tracer.createError("Cannot get valid class for property", currentValues); 195 } else { 196 197 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitiveAttributes) { 198 primitiveAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy()); 199 } 200 for (SupportAttributeDefinition supportAttributeDefinition : supportAttributes) { 201 supportAttributeDefinition.completeValidation(rootBusinessObjectClass, targetClass, tracer.getCopy()); 202 } 203 } 204 } 205 206 /** 207 * The primitiveAttribute element identifies one pair of 208 * corresponding fields in the primary business object and 209 * the related business object. 210 * 211 * JSTL: primitiveAttribute is a Map which is accessed by the 212 * sequential key of "0", "1", etc. Each entry contains the following 213 * keys: 214 * sourceName (String) 215 * targetName (String) 216 * The value corresponding to the sourceName key is the attribute name defined 217 * for the primary business object. 218 * The value corresponding to the targetName key is the attribute name for 219 * the object being referenced by objectAttributeName. 220 */ 221 public void setPrimitiveAttributes(List<PrimitiveAttributeDefinition> primitiveAttributes) { 222 this.primitiveAttributes = primitiveAttributes; 223 } 224 225 /** 226 * Support attributes define additional attributes that can be used to generate 227 * lookup field conversions and lookup parameters. 228 * 229 * Field conversions and lookup parameters are normally generated using foreign key relationships 230 * defined within OJB and the DD. Because Person objects are linked in a special way (i.e. they may 231 * come from an external data source and not from the DB, such as LDAP), it is often necessary to define 232 * extra fields that are related to each other, sort of like a supplemental foreign key. 233 * 234 * sourceName is the name of the POJO property of the business object 235 * targetName is the name of attribute that corresponds to the sourceName in the looked up BO 236 * identifier when true, only the field marked as an identifier will be passed in as a lookup parameter 237 * at most one supportAttribute for each relationship should be defined as identifier="true" 238 */ 239 public void setSupportAttributes(List<SupportAttributeDefinition> supportAttributes) { 240 this.supportAttributes = supportAttributes; 241 } 242 243 /** 244 * @param sourceClass the sourceClass to set 245 */ 246 public void setSourceClass(Class<?> sourceClass) { 247 this.sourceClass = sourceClass; 248 } 249 250 @Override 251 public String toString() { 252 StringBuilder builder = new StringBuilder(); 253 builder.append("RelationshipDefinition [objectAttributeName=").append(this.objectAttributeName) 254 .append(", sourceClass=").append(this.sourceClass).append(", targetClass=").append(this.targetClass) 255 .append(", primitiveAttributes=").append(this.primitiveAttributes).append(", supportAttributes=") 256 .append(this.supportAttributes).append("]"); 257 return builder.toString(); 258 } 259} 260