/*
* Copyright (C) 2003-2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.chromattic.core;
import org.chromattic.api.BuilderException;
import org.chromattic.common.ObjectInstantiator;
import org.chromattic.common.jcr.Path;
import org.chromattic.common.jcr.PathException;
import org.chromattic.core.mapper.ObjectMapper;
import org.chromattic.core.jcr.type.TypeManager;
import org.chromattic.core.query.QueryManager;
import org.chromattic.metamodel.mapping.BeanMapping;
import org.chromattic.spi.instrument.Instrumentor;
import org.chromattic.api.format.ObjectFormatter;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.lang.annotation.Annotation;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.chromattic.common.collection.Collections;
import org.chromattic.spi.instrument.MethodHandler;
import org.chromattic.spi.instrument.ProxyType;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
* @version $Revision$
*/
public class Domain {
/** . */
private static final ProxyType<?> NULL_PROXY_TYPE = new ProxyType<Object>() {
public Object createProxy(MethodHandler handler) {
throw new UnsupportedOperationException("Cannot create proxy for " + handler);
}
public MethodHandler getInvoker(Object proxy) {
return null;
}
public Class<?> getType() {
throw new UnsupportedOperationException("Cannot get proxy type for NULL_PROXY_TYPE");
}
};
/** . */
public static int LAZY_CREATE_MODE = 0;
/** . */
public static int CREATE_MODE = 1;
/** . */
public static int NO_CREATE_MODE = 2;
/** . */
private static final Set<Integer> CREATE_MODES = Collections.set(LAZY_CREATE_MODE, CREATE_MODE, NO_CREATE_MODE);
/** . */
private final Map<String, ObjectMapper> typeMapperByNodeType;
/** . */
private final Map<Class<?>, ObjectMapper> typeMapperByClass;
/** . */
private final Instrumentor defaultInstrumentor;
/** . */
private final Map<Class<?>, ProxyType<?>> proxyTypeMap;
/** . */
private final Map<Class<?>, ProxyType<?>> chromatticTypeMap;
/** . */
final ObjectFormatter objectFormatter;
/** . */
final boolean propertyCacheEnabled;
/** . */
final boolean propertyReadAheadEnabled;
/** . */
final boolean hasPropertyOptimized;
/** . */
final boolean hasNodeOptimized;
/** . */
final String rootNodePath;
/** . */
final List<String> rootNodePathSegments;
/** . */
final String rootNodeType;
/** . */
final int rootCreateMode;
/** . */
final TypeManager nodeInfoManager;
/** . */
final QueryManager queryManager;
public Domain(
Collection<ObjectMapper<?>> mappers,
Instrumentor defaultInstrumentor,
ObjectFormatter objectFormatter,
boolean propertyCacheEnabled,
boolean propertyReadAheadEnabled,
boolean hasPropertyOptimized,
boolean hasNodeOptimized,
String rootNodePath,
int rootCreateMode,
String rootNodeType) {
//
if (!CREATE_MODES.contains(rootCreateMode)) {
throw new IllegalArgumentException("Invalid create mode " + rootCreateMode);
}
//
Map<Class<?>, ProxyType<?>> proxyClassToProxyType = new HashMap<Class<?>, ProxyType<?>>();
Map<Class<?>, ProxyType<?>> chromatticClassToProxyType = new HashMap<Class<?>, ProxyType<?>>();
mapping: for (ObjectMapper<?> mapper : mappers) {
BeanMapping beanMapping = mapper.getMapping();
Class<?> clazz = (Class<?>)beanMapping.getBean().getClassType().unwrap();
for (Annotation annotation : clazz.getAnnotations()) {
if ("org.chromattic.groovy.annotations.GroovyInstrumentor".equals(annotation.annotationType().getName())) {
Class<?> instrumentorClass = null;
try {
instrumentorClass = (Class<?>)annotation.annotationType().getMethod("value").invoke(annotation);
} catch (Exception ignore) {}
Instrumentor i = ObjectInstantiator.newInstance(instrumentorClass.getName(), Instrumentor.class);
ProxyType<?> proxyType = i.getProxyType(clazz);
proxyClassToProxyType.put(i.getProxyType(clazz).getType(), proxyType);
chromatticClassToProxyType.put(clazz, proxyType);
continue mapping;
}
}
if (Object.class.equals(clazz)) {
proxyClassToProxyType.put(clazz, NULL_PROXY_TYPE);
chromatticClassToProxyType.put(clazz, NULL_PROXY_TYPE);
} else {
ProxyType<?> proxyType = defaultInstrumentor.getProxyType(clazz);
proxyClassToProxyType.put(proxyType.getType(), proxyType);
chromatticClassToProxyType.put(clazz, proxyType);
}
}
//
Map<String, ObjectMapper> typeMapperByNodeType = new HashMap<String, ObjectMapper>();
Map<Class<?>, ObjectMapper> typeMapperByClass = new HashMap<Class<?>, ObjectMapper>();
for (ObjectMapper typeMapper : mappers) {
if (typeMapperByNodeType.containsKey(typeMapper.getNodeTypeName())) {
throw new IllegalStateException("Duplicate node type name " + typeMapper);
}
typeMapperByNodeType.put(typeMapper.getNodeTypeName(), typeMapper);
typeMapperByClass.put(typeMapper.getObjectClass(), typeMapper);
}
//
final List<String> rootNodePathSegments;
try {
rootNodePathSegments = Path.splitAbsolutePath(Path.normalizeAbsolutePath(rootNodePath));
}
catch (PathException e) {
throw new BuilderException("Root node path must be valid");
}
//
this.typeMapperByClass = typeMapperByClass;
this.typeMapperByNodeType = typeMapperByNodeType;
this.defaultInstrumentor = defaultInstrumentor;
this.objectFormatter = objectFormatter;
this.propertyCacheEnabled = propertyCacheEnabled;
this.propertyReadAheadEnabled = propertyReadAheadEnabled;
this.hasPropertyOptimized = hasPropertyOptimized;
this.hasNodeOptimized = hasNodeOptimized;
this.rootNodePath = rootNodePath;
this.rootNodePathSegments = rootNodePathSegments;
this.nodeInfoManager = new TypeManager();
this.queryManager = new QueryManager(rootNodePath);
this.rootCreateMode = rootCreateMode;
this.rootNodeType = rootNodeType;
this.proxyTypeMap = proxyClassToProxyType;
this.chromatticTypeMap = chromatticClassToProxyType;
}
public boolean isHasPropertyOptimized() {
return hasPropertyOptimized;
}
public boolean isHasNodeOptimized() {
return hasNodeOptimized;
}
public MethodHandler getHandler(Object o) {
ProxyType<?> instrumentor = proxyTypeMap.get(o.getClass());
return instrumentor != null ? instrumentor.getInvoker(o) : null;
}
public <O> ProxyType<O> getProxyType(Class<O> type) {
return (ProxyType<O>)chromatticTypeMap.get(type);
}
public ObjectMapper getTypeMapper(String nodeTypeName) {
return typeMapperByNodeType.get(nodeTypeName);
}
public ObjectMapper getTypeMapper(Class<?> clazz) {
return typeMapperByClass.get(clazz);
}
public QueryManager getQueryManager() {
return queryManager;
}
String decodeName(Node ownerNode, String internal, NameKind nameKind) throws
NullPointerException, UndeclaredThrowableException, IllegalStateException, RepositoryException {
if (ownerNode == null) {
throw new NullPointerException();
}
String nodeTypeName = ownerNode.getPrimaryNodeType().getName();
ObjectMapper ownerMapper = getTypeMapper(nodeTypeName);
ObjectFormatter formatter = null;
if (ownerMapper != null) {
formatter = ownerMapper.getFormatter();
}
return decodeName(formatter, internal, nameKind);
}
String decodeName(ObjectContext ownerCtx, String internal, NameKind nameKind) throws
NullPointerException, UndeclaredThrowableException, IllegalStateException, RepositoryException {
if (ownerCtx == null) {
throw new NullPointerException();
}
return decodeName(ownerCtx.getMapper().getFormatter(), internal, nameKind);
}
private String decodeName(ObjectFormatter formatter, String internal, NameKind nameKind) throws
UndeclaredThrowableException, IllegalStateException, RepositoryException {
if (nameKind == NameKind.PROPERTY) {
return internal;
}
if (formatter == null) {
formatter = objectFormatter;
}
//
String external;
try {
if (nameKind == NameKind.OBJECT) {
external = formatter.decodeNodeName(null, internal);
} else {
// external = formatter.decodePropertyName(null, internal);
throw new UnsupportedOperationException();
}
}
catch (Exception e) {
if (e instanceof IllegalStateException) {
throw (IllegalStateException)e;
}
throw new UndeclaredThrowableException(e);
}
if (external == null) {
if (nameKind == NameKind.OBJECT) {
throw new IllegalStateException();
}
}
return external;
}
/**
* Encodes the name for the specified context.
*
* @param ownerNode the node
* @param externalName the external name
* @param nameKind the name kind
* @return the encoded name
* @throws NullPointerException if the owner context argument is null
* @throws UndeclaredThrowableException when the formatter throws an exception
* @throws RepositoryException any repository exception
*/
String encodeName(Node ownerNode, String externalName, NameKind nameKind) throws
NullPointerException, UndeclaredThrowableException, RepositoryException {
if (ownerNode == null) {
throw new NullPointerException();
}
String nodeTypeName = ownerNode.getPrimaryNodeType().getName();
ObjectMapper ownerMapper = getTypeMapper(nodeTypeName);
ObjectFormatter formatter = null;
if (ownerMapper != null) {
formatter = ownerMapper.getFormatter();
}
return encodeName(formatter, externalName, nameKind);
}
/**
* Encodes the name for the specified context.
*
* @param ownerCtx the context
* @param externalName the external name
* @param nameKind the name kind
* @return the encoded name
* @throws NullPointerException if any argument is null
* @throws UndeclaredThrowableException when the formatter throws an exception
* @throws RepositoryException any repository exception
*/
String encodeName(ObjectContext ownerCtx, String externalName, NameKind nameKind) throws
NullPointerException, UndeclaredThrowableException, RepositoryException {
if (ownerCtx == null) {
throw new NullPointerException("No null owner node accepted");
}
return encodeName(ownerCtx.getMapper().getFormatter(), externalName, nameKind);
}
private String encodeName(ObjectFormatter formatter, String externalName, NameKind nameKind) throws
UndeclaredThrowableException, NullPointerException, RepositoryException {
if (externalName == null) {
throw new NullPointerException("No null name accepted");
}
if (nameKind == null) {
throw new NullPointerException("No null name kind accepted");
}
if (nameKind == NameKind.PROPERTY) {
return externalName;
}
if (formatter == null) {
formatter = objectFormatter;
}
//
String internal;
try {
if (nameKind == NameKind.OBJECT) {
internal = formatter.encodeNodeName(null, externalName);
} else {
// internal = formatter.encodePropertyName(null, external);
throw new UnsupportedOperationException();
}
}
catch (Exception e) {
if (e instanceof NullPointerException) {
throw (NullPointerException)e;
}
if (e instanceof IllegalArgumentException) {
throw (IllegalArgumentException)e;
}
throw new UndeclaredThrowableException(e);
}
if (internal == null) {
throw new IllegalArgumentException("Name " + externalName + " was converted to null");
}
Path.validateLocalName(internal);
return internal;
}
}