1 /* Copyright 2004-2005 the original author or authors.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 package org
.codehaus
.groovy
.grails
.commons
;
17 import groovy
.lang
.Closure
;
18 import groovy
.lang
.GroovyObject
;
19 import org
.apache
.commons
.lang
.StringUtils
;
20 import org
.codehaus
.groovy
.grails
.commons
.metaclass
.DynamicMethods
;
21 import org
.codehaus
.groovy
.grails
.commons
.metaclass
.GroovyDynamicMethodsInterceptor
;
22 import org
.codehaus
.groovy
.grails
.exceptions
.GrailsConfigurationException
;
23 import org
.codehaus
.groovy
.grails
.validation
.metaclass
.ConstraintsEvaluatingDynamicProperty
;
25 import java
.beans
.IntrospectionException
;
26 import java
.beans
.PropertyDescriptor
;
27 import java
.io
.Serializable
;
28 import java
.math
.BigDecimal
;
29 import java
.math
.BigInteger
;
35 import java
.sql
.Timestamp
;
37 import java
.lang
.reflect
.Method
;
38 import java
.lang
.reflect
.InvocationTargetException
;
41 * Utility methods used in configuring the Grails Hibernate integration
43 * @author Graeme Rocher
46 public class GrailsDomainConfigurationUtil
{
49 private static final Class
[] EMPTY_CLASS_ARRAY
= new Class
[0];
51 public static Serializable
getAssociationIdentifier(Object target
, String propertyName
, GrailsDomainClass referencedDomainClass
) {
52 String getterName
= GrailsClassUtils
.getGetterName(propertyName
);
55 Method m
= target
.getClass().getDeclaredMethod(getterName
, EMPTY_CLASS_ARRAY
);
56 Object value
= m
.invoke(target
, null);
57 if(value
!= null && referencedDomainClass
!= null) {
58 String identifierGetter
= GrailsClassUtils
.getGetterName(referencedDomainClass
.getIdentifier().getName());
59 m
= value
.getClass().getDeclaredMethod(identifierGetter
, EMPTY_CLASS_ARRAY
);
60 return (Serializable
)m
.invoke(value
, null);
62 } catch (NoSuchMethodException e
) {
64 } catch (IllegalAccessException e
) {
66 } catch (InvocationTargetException e
) {
73 * Configures the relationships between domain classes after they have been all loaded.
75 * @param domainClasses The domain classes to configure relationships for
76 * @param domainMap The domain class map
78 public static void configureDomainClassRelationships(GrailsClass
[] domainClasses
, Map domainMap
) {
80 // configure super/sub class relationships
81 // and configure how domain class properties reference each other
82 for (int i
= 0; i
< domainClasses
.length
; i
++) {
83 GrailsDomainClass domainClass
= (GrailsDomainClass
) domainClasses
[i
];
84 if (!domainClass
.isRoot()) {
85 Class superClass
= domainClasses
[i
].getClazz().getSuperclass();
86 while (!superClass
.equals(Object
.class) && !superClass
.equals(GroovyObject
.class)) {
87 GrailsDomainClass gdc
= (GrailsDomainClass
) domainMap
.get(superClass
.getName());
88 if (gdc
== null || gdc
.getSubClasses() == null)
91 gdc
.getSubClasses().add(domainClasses
[i
]);
92 superClass
= superClass
.getSuperclass();
95 GrailsDomainClassProperty
[] props
= domainClass
.getPersistentProperties();
97 for (int j
= 0; j
< props
.length
; j
++) {
98 if (props
[j
] != null && props
[j
].isAssociation()) {
99 GrailsDomainClassProperty prop
= props
[j
];
100 GrailsDomainClass referencedGrailsDomainClass
= (GrailsDomainClass
) domainMap
.get(props
[j
].getReferencedPropertyType().getName());
101 prop
.setReferencedDomainClass(referencedGrailsDomainClass
);
108 // now configure so that the 'other side' of a property can be resolved by the property itself
109 for (int i
= 0; i
< domainClasses
.length
; i
++) {
110 GrailsDomainClass domainClass
= (GrailsDomainClass
) domainClasses
[i
];
111 GrailsDomainClassProperty
[] props
= domainClass
.getPersistentProperties();
113 for (int j
= 0; j
< props
.length
; j
++) {
114 if (props
[j
] != null && props
[j
].isAssociation()) {
115 GrailsDomainClassProperty prop
= props
[j
];
116 GrailsDomainClass referenced
= prop
.getReferencedDomainClass();
117 if (referenced
!= null) {
118 boolean isOwnedBy
= referenced
.isOwningClass(domainClass
.getClazz());
119 prop
.setOwningSide(isOwnedBy
);
120 String refPropertyName
= null;
122 refPropertyName
= prop
.getReferencedPropertyName();
123 } catch (UnsupportedOperationException e
) {
124 // ignore (to support Hibernate entities)
126 if (!StringUtils
.isBlank(refPropertyName
)) {
127 GrailsDomainClassProperty otherSide
= referenced
.getPropertyByName(refPropertyName
);
128 prop
.setOtherSide(otherSide
);
129 otherSide
.setOtherSide(prop
);
131 GrailsDomainClassProperty
[] referencedProperties
= referenced
.getPersistentProperties();
132 for (int k
= 0; k
< referencedProperties
.length
; k
++) {
133 // for bi-directional circular dependencies we don't want the other side
134 // to be equal to self
135 GrailsDomainClassProperty referencedProp
= referencedProperties
[k
];
136 if (prop
.equals(referencedProp
) && prop
.isBidirectional())
138 if (isCandidateForOtherSide(domainClass
, prop
, referencedProp
)) {
139 prop
.setOtherSide(referencedProp
);
151 private static boolean isCandidateForOtherSide(GrailsDomainClass domainClass
, GrailsDomainClassProperty prop
, GrailsDomainClassProperty referencedProp
) {
153 if (prop
.equals(referencedProp
)) return false;
154 if (prop
.isOneToMany() && referencedProp
.isOneToMany()) return false;
155 Class referencedPropertyType
= referencedProp
.getReferencedPropertyType();
156 if (referencedPropertyType
== null || !referencedPropertyType
.isAssignableFrom(domainClass
.getClazz()))
158 Map mappedBy
= domainClass
.getMappedBy();
160 Object propertyMapping
= mappedBy
.get(prop
.getName());
161 boolean mappedToDifferentProperty
= propertyMapping
!= null && !propertyMapping
.equals(referencedProp
.getName());
163 mappedBy
= referencedProp
.getDomainClass().getMappedBy();
164 propertyMapping
= mappedBy
.get(referencedProp
.getName());
165 boolean mappedFromDifferentProperty
= propertyMapping
!= null && !propertyMapping
.equals(prop
.getName());
168 return !mappedToDifferentProperty
&& !mappedFromDifferentProperty
;
172 * Returns the ORM frameworks mapping file name for the specified class name
174 * @param className The class name of the mapped file
175 * @return The mapping file name
177 public static String
getMappingFileName(String className
) {
178 String fileName
= className
.replaceAll("\\.", "/");
179 return fileName
+= ".hbm.xml";
183 * Returns the association map for the specified domain class
185 * @param domainClass the domain class
186 * @return The association map
188 public static Map
getAssociationMap(Class domainClass
) {
189 Map associationMap
= (Map
) GrailsClassUtils
.getPropertyValueOfNewInstance(domainClass
, GrailsDomainClassProperty
.RELATES_TO_MANY
, Map
.class);
190 if (associationMap
== null) {
191 associationMap
= (Map
) GrailsClassUtils
.getPropertyValueOfNewInstance(domainClass
, GrailsDomainClassProperty
.HAS_MANY
, Map
.class);
192 if (associationMap
== null) {
193 associationMap
= Collections
.EMPTY_MAP
;
196 return associationMap
;
200 * Retrieves the mappedBy map for the specified class
202 * @param domainClass The domain class
203 * @return The mappedBy map
205 public static Map
getMappedByMap(Class domainClass
) {
206 Map mappedByMap
= (Map
) GrailsClassUtils
.getPropertyValueOfNewInstance(domainClass
, GrailsDomainClassProperty
.MAPPED_BY
, Map
.class);
207 if (mappedByMap
== null) {
208 return Collections
.EMPTY_MAP
;
214 * Establish whether its a basic type
216 * @param prop The domain class property
217 * @return True if it is basic
219 public static boolean isBasicType(GrailsDomainClassProperty prop
) {
220 if (prop
== null) return false;
221 Class propType
= prop
.getType();
222 return isBasicType(propType
);
225 private static final Set BASIC_TYPES
;
228 Set basics
= new HashSet();
229 basics
.add(boolean.class.getName());
230 basics
.add(long.class.getName());
231 basics
.add(short.class.getName());
232 basics
.add(int.class.getName());
233 basics
.add(byte.class.getName());
234 basics
.add(float.class.getName());
235 basics
.add(double.class.getName());
236 basics
.add(char.class.getName());
237 basics
.add(Boolean
.class.getName());
238 basics
.add(Long
.class.getName());
239 basics
.add(Short
.class.getName());
240 basics
.add(Integer
.class.getName());
241 basics
.add(Byte
.class.getName());
242 basics
.add(Float
.class.getName());
243 basics
.add(Double
.class.getName());
244 basics
.add(Character
.class.getName());
245 basics
.add(String
.class.getName());
246 basics
.add(java
.util
.Date
.class.getName());
247 basics
.add(Time
.class.getName());
248 basics
.add(Timestamp
.class.getName());
249 basics
.add(java
.sql
.Date
.class.getName());
250 basics
.add(BigDecimal
.class.getName());
251 basics
.add(BigInteger
.class.getName());
252 basics
.add(Locale
.class.getName());
253 basics
.add(Calendar
.class.getName());
254 basics
.add(GregorianCalendar
.class.getName());
255 basics
.add(java
.util
.Currency
.class.getName());
256 basics
.add(TimeZone
.class.getName());
257 basics
.add(Object
.class.getName());
258 basics
.add(Class
.class.getName());
259 basics
.add(byte[].class.getName());
260 basics
.add(Byte
[].class.getName());
261 basics
.add(char[].class.getName());
262 basics
.add(Character
[].class.getName());
263 basics
.add(Blob
.class.getName());
264 basics
.add(Clob
.class.getName());
265 basics
.add(Serializable
.class.getName());
266 basics
.add(URI
.class.getName());
267 basics
.add(URL
.class.getName());
269 BASIC_TYPES
= Collections
.unmodifiableSet(basics
);
272 public static boolean isBasicType(Class propType
) {
273 if (propType
.isArray()) {
274 return isBasicType(propType
.getComponentType());
276 return BASIC_TYPES
.contains(propType
.getName());
281 * Checks whether is property is configurational
283 * @param descriptor The descriptor
284 * @return True if it is configurational
286 public static boolean isNotConfigurational(PropertyDescriptor descriptor
) {
287 return !descriptor
.getName().equals(GrailsDomainClassProperty
.META_CLASS
) &&
288 !descriptor
.getName().equals(GrailsDomainClassProperty
.CLASS
) &&
289 !descriptor
.getName().equals(GrailsDomainClassProperty
.TRANSIENT
) &&
290 !descriptor
.getName().equals(GrailsDomainClassProperty
.RELATES_TO_MANY
) &&
291 !descriptor
.getName().equals(GrailsDomainClassProperty
.HAS_MANY
) &&
292 !descriptor
.getName().equals(GrailsDomainClassProperty
.EVANESCENT
) &&
293 !descriptor
.getName().equals(GrailsDomainClassProperty
.CONSTRAINTS
) &&
294 !descriptor
.getName().equals(GrailsDomainClassProperty
.MAPPING_STRATEGY
) &&
295 !descriptor
.getName().equals(GrailsDomainClassProperty
.MAPPED_BY
) &&
296 !descriptor
.getName().equals(GrailsDomainClassProperty
.BELONGS_TO
);
300 * Evaluates the constraints closure to build the list of constraints
302 * @param instance The instance to evaluate constraints for
303 * @param properties The properties of the instance
304 * @return A Map of constraints
305 * @throws java.beans.IntrospectionException
306 * When the bean cannot be introspected
308 public static Map
evaluateConstraints(Object instance
, GrailsDomainClassProperty
[] properties
) throws IntrospectionException
{
309 Closure constraintsClosure
= (Closure
) GrailsClassUtils
.getPropertyOrStaticPropertyOrFieldValue(instance
, GrailsDomainClassProperty
.CONSTRAINTS
);
310 Class objClass
= instance
.getClass();
311 String fullName
= objClass
.getName();
312 if (constraintsClosure
!= null && !GrailsClassUtils
.isStaticProperty(objClass
, GrailsDomainClassProperty
.CONSTRAINTS
)) {
313 throw new GrailsConfigurationException(
314 "Domain class [" + fullName
+ "] has non-static constraints. Constraints must be " +
319 GroovyObject go
= (GroovyObject
) instance
;
320 DynamicMethods interceptor
= new GroovyDynamicMethodsInterceptor(go
);
321 interceptor
.addDynamicProperty(new ConstraintsEvaluatingDynamicProperty(properties
));
323 return (Map
) go
.getProperty(GrailsDomainClassProperty
.CONSTRAINTS
);