/*
* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.core.support;
import grails.core.GrailsApplication;
import grails.core.GrailsClass;
import grails.core.GrailsDomainClass;
import grails.core.GrailsDomainClassProperty;
import grails.util.CollectionUtils;
import grails.util.GrailsClassUtils;
import groovy.lang.GroovyObject;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import grails.validation.Constrained;
import org.grails.core.util.ClassPropertyFetcher;
import org.grails.core.io.support.GrailsFactoriesLoader;
import grails.validation.ConstraintsEvaluator;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
/**
* Utility methods used in configuring the Grails Hibernate integration.
*
* @author Graeme Rocher
* @deprecated Use the {@link org.grails.datastore.mapping.model.MappingContext} and {@link org.grails.datastore.mapping.model.MappingFactory} APIs instead
*/
@Deprecated
public class GrailsDomainConfigurationUtil {
public static final String PROPERTY_NAME = "constraints";
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
public static final String PROPERTIES_PROPERTY = "properties";
public static Serializable getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
/**
* Configures the relationships between domain classes after they have been all loaded.
*
* @param domainClasses The domain classes to configure relationships for
* @param domainMap The domain class map
*/
public static void configureDomainClassRelationships(GrailsClass[] domainClasses, Map<?, ?> domainMap) {
// configure super/sub class relationships
// and configure how domain class properties reference each other
for (GrailsClass grailsClass : domainClasses) {
GrailsDomainClass domainClass = (GrailsDomainClass) grailsClass;
if (!domainClass.isRoot()) {
Class<?> superClass = grailsClass.getClazz().getSuperclass();
while (!superClass.equals(Object.class) && !superClass.equals(GroovyObject.class)) {
GrailsDomainClass gdc = (GrailsDomainClass) domainMap.get(superClass.getName());
if (gdc == null || gdc.getSubClasses() == null) {
break;
}
gdc.getSubClasses().add((GrailsDomainClass)grailsClass);
superClass = superClass.getSuperclass();
}
}
GrailsDomainClassProperty[] props = domainClass.getPersistentProperties();
for (GrailsDomainClassProperty prop : props) {
if (prop != null && prop.isAssociation()) {
GrailsDomainClass referencedGrailsDomainClass = (GrailsDomainClass) domainMap.get(prop.getReferencedPropertyType().getName());
prop.setReferencedDomainClass(referencedGrailsDomainClass);
}
}
}
// now configure so that the 'other side' of a property can be resolved by the property itself
for (GrailsClass domainClass1 : domainClasses) {
GrailsDomainClass domainClass = (GrailsDomainClass) domainClass1;
GrailsDomainClassProperty[] props = domainClass.getPersistentProperties();
for (GrailsDomainClassProperty prop : props) {
if (prop == null || !prop.isAssociation()) {
continue;
}
GrailsDomainClass referenced = prop.getReferencedDomainClass();
if (referenced == null) {
continue;
}
boolean isOwnedBy = referenced.isOwningClass(domainClass.getClazz());
prop.setOwningSide(isOwnedBy);
String refPropertyName = null;
try {
refPropertyName = prop.getReferencedPropertyName();
}
catch (UnsupportedOperationException e) {
// ignore (to support Hibernate entities)
}
if (!StringUtils.hasText(refPropertyName)) {
GrailsDomainClassProperty[] referencedProperties = referenced.getPersistentProperties();
for (GrailsDomainClassProperty referencedProp : referencedProperties) {
// for bi-directional circular dependencies we don't want the other side
// to be equal to self
if (prop.equals(referencedProp) && prop.isBidirectional()) {
continue;
}
if (isCandidateForOtherSide(domainClass, prop, referencedProp)) {
prop.setOtherSide(referencedProp);
break;
}
}
}
else {
GrailsDomainClassProperty otherSide = referenced.getPropertyByName(refPropertyName);
prop.setOtherSide(otherSide);
otherSide.setOtherSide(prop);
}
}
}
}
private static boolean isCandidateForOtherSide(GrailsDomainClass domainClass,
GrailsDomainClassProperty prop, GrailsDomainClassProperty referencedProp) {
if (prop.equals(referencedProp)) return false;
if (prop.isOneToMany() && referencedProp.isOneToMany()) return false;
Class<?> referencedPropertyType = referencedProp.getReferencedPropertyType();
if (referencedPropertyType == null || !referencedPropertyType.isAssignableFrom(domainClass.getClazz())) {
return false;
}
Map<?, ?> mappedBy = domainClass.getMappedBy();
Object propertyMapping = mappedBy.get(prop.getName());
boolean mappedToDifferentProperty = propertyMapping != null && !propertyMapping.equals(referencedProp.getName());
mappedBy = referencedProp.getDomainClass().getMappedBy();
propertyMapping = mappedBy.get(referencedProp.getName());
boolean mappedFromDifferentProperty = propertyMapping != null && !propertyMapping.equals(prop.getName());
return !mappedToDifferentProperty && !mappedFromDifferentProperty;
}
/**
* Returns the ORM framework's mapping file name for the specified class name.
*
* @param className The class name of the mapped file
* @return The mapping file name
*/
public static String getMappingFileName(String className) {
return className.replaceAll("\\.", "/") + ".hbm.xml";
}
/**
* Returns the association map for the specified domain class
*
* @param domainClass the domain class
* @return The association map
*/
public static Map<?, ?> getAssociationMap(Class<?> domainClass) {
ClassPropertyFetcher cpf = ClassPropertyFetcher.forClass(domainClass);
Map<?, ?> associationMap = cpf.getPropertyValue(GrailsDomainClassProperty.HAS_MANY, Map.class);
if (associationMap == null) {
associationMap = Collections.emptyMap();
}
return associationMap;
}
/**
* Retrieves the mappedBy map for the specified class.
*
* @param domainClass The domain class
* @return The mappedBy map
*/
public static Map<?, ?> getMappedByMap(Class<?> domainClass) {
ClassPropertyFetcher cpf = ClassPropertyFetcher.forClass(domainClass);
Map<?, ?> mappedByMap = cpf.getPropertyValue(GrailsDomainClassProperty.MAPPED_BY, Map.class);
if (mappedByMap == null) {
return Collections.emptyMap();
}
return mappedByMap;
}
/**
* Establish whether it's a basic type.
*
* @param prop The domain class property
* @return true if it is basic
*/
public static boolean isBasicType(GrailsDomainClassProperty prop) {
return prop == null ? false : isBasicType(prop.getType());
}
private static final Set<String> BASIC_TYPES;
static {
Set<String> basics = CollectionUtils.newSet(
boolean.class.getName(),
long.class.getName(),
short.class.getName(),
int.class.getName(),
byte.class.getName(),
float.class.getName(),
double.class.getName(),
char.class.getName(),
Boolean.class.getName(),
Long.class.getName(),
Short.class.getName(),
Integer.class.getName(),
Byte.class.getName(),
Float.class.getName(),
Double.class.getName(),
Character.class.getName(),
String.class.getName(),
java.util.Date.class.getName(),
Time.class.getName(),
Timestamp.class.getName(),
java.sql.Date.class.getName(),
BigDecimal.class.getName(),
BigInteger.class.getName(),
Locale.class.getName(),
Calendar.class.getName(),
GregorianCalendar.class.getName(),
java.util.Currency.class.getName(),
TimeZone.class.getName(),
Object.class.getName(),
Class.class.getName(),
byte[].class.getName(),
Byte[].class.getName(),
char[].class.getName(),
Character[].class.getName(),
Blob.class.getName(),
Clob.class.getName(),
Serializable.class.getName(),
URI.class.getName(),
URL.class.getName());
BASIC_TYPES = Collections.unmodifiableSet(basics);
}
public static boolean isBasicType(Class<?> propType) {
if (propType == null) return false;
if (propType.isArray()) {
return isBasicType(propType.getComponentType());
}
return BASIC_TYPES.contains(propType.getName());
}
/**
* Checks whether is property is configurational.
*
* @param descriptor The descriptor
* @return true if it is configurational
*/
public static boolean isNotConfigurational(PropertyDescriptor descriptor) {
final String name = descriptor.getName();
Method readMethod = descriptor.getReadMethod();
Method writeMethod = descriptor.getWriteMethod();
if ((readMethod != null && Modifier.isStatic(readMethod.getModifiers()) ||
(writeMethod != null && Modifier.isStatic(writeMethod.getModifiers())))) {
return false;
}
return !Errors.class.isAssignableFrom(descriptor.getPropertyType())
&& isNotConfigurational(name);
}
private static final Set<String> CONFIGURATIONAL_PROPERTIES;
static {
Set<String> configurational = CollectionUtils.newSet(
GrailsDomainClassProperty.META_CLASS,
GrailsDomainClassProperty.CLASS,
GrailsDomainClassProperty.TRANSIENT,
GrailsDomainClassProperty.ATTACHED,
GrailsDomainClassProperty.DIRTY,
GrailsDomainClassProperty.DIRTY_PROPERTY_NAMES,
GrailsDomainClassProperty.HAS_MANY,
GrailsDomainClassProperty.CONSTRAINTS,
GrailsDomainClassProperty.MAPPING_STRATEGY,
GrailsDomainClassProperty.MAPPED_BY,
GrailsDomainClassProperty.BELONGS_TO,
GrailsDomainClassProperty.ERRORS,
GrailsApplication.TRANSACTION_MANAGER_BEAN,
GrailsApplication.DATA_SOURCE_BEAN,
GrailsApplication.SESSION_FACTORY_BEAN,
GrailsApplication.MESSAGE_SOURCE_BEAN,
"applicationContext",
PROPERTIES_PROPERTY);
CONFIGURATIONAL_PROPERTIES = Collections.unmodifiableSet(configurational);
}
public static boolean isConfigurational(String name) {
return CONFIGURATIONAL_PROPERTIES.contains(name);
}
public static boolean isNotConfigurational(String name) {
return !isConfigurational(name);
}
private static Map<String, Constrained> getConstraintMap(GrailsDomainClassProperty[] properties, Map<String, Object> defaultConstraints, Class<?> theClass) {
ConstraintsEvaluator constraintsEvaluator = GrailsFactoriesLoader.loadFactory(ConstraintsEvaluator.class, defaultConstraints);
if(constraintsEvaluator != null) {
return constraintsEvaluator.evaluate(theClass, properties);
}
return Collections.emptyMap();
}
public static LinkedList<?> getSuperClassChain(Class<?> theClass) {
LinkedList<Class<?>> classChain = new LinkedList<Class<?>>();
Class<?> clazz = theClass;
while (clazz != Object.class && clazz != null) {
classChain.addFirst(clazz);
clazz = clazz.getSuperclass();
}
return classChain;
}
}