package org.castor.cpa.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.castor.cpa.util.classresolution.command.ClassDescriptorResolutionCommand;
import org.castor.cpa.util.classresolution.command.ClassResolutionByAnnotations;
import org.castor.cpa.util.classresolution.command.ClassResolutionByCDR;
import org.castor.cpa.util.classresolution.command.ClassResolutionByFile;
import org.castor.cpa.util.classresolution.command.ClassResolutionByMappingLoader;
import org.castor.cpa.util.classresolution.nature.ClassLoaderNature;
import org.castor.cpa.util.classresolution.nature.MappingLoaderNature;
import org.castor.cpa.util.classresolution.nature.PackageBasedCDRResolutionNature;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.MappingLoader;
import org.exolab.castor.xml.ResolverException;
/**
* JDO-specific {@link ClassDescriptorResolver} instance that provides
* functionality to find or "resolve" {@link ClassDescriptor}s from a given
* class (name).
*
* @see JDOClassDescriptorResolver
*/
public class JDOClassDescriptorResolverImpl implements JDOClassDescriptorResolver {
/**
* A key ({@link Class}) value ({@link ClassDescriptor}) pair to cache
* already resolved JDO class descriptors.
*/
private Map<Class<?>, ClassDescriptor> _classDescriptorCache =
new ConcurrentHashMap<Class<?>, ClassDescriptor>();
/**
* A {@link MappingLoader} instance which this
* {@link ClassDescriptorResolver} instance turns to to load
* {@link ClassDescriptor} instances as defined in a Castor JDO mapping
* file.
*/
private MappingLoader _mappingLoader;
/**
* List of manually added domain <code>Class</code>es.
*/
protected List<Class<?>> _classes = new LinkedList<Class<?>>();
/**
* List of manually added package names.
*/
protected List<String> _packages = new LinkedList<String>();
/**
* JDO-specific {@link ClassDescriptor} resolution commands.
*/
private Map<String, ClassDescriptorResolutionCommand> _commands =
new HashMap<String, ClassDescriptorResolutionCommand>();
/**
* Creates an instance of this class, with no classed manually added.
*/
public JDOClassDescriptorResolverImpl() {
super();
registerCommand(new ClassResolutionByMappingLoader());
registerCommand(new ClassResolutionByFile());
registerCommand(new ClassResolutionByCDR());
registerCommand(new ClassResolutionByAnnotations());
}
/**
* Registers a {@link ClassDescriptorResolutionCommand} used to resolve
* {@link ClassDescriptor}s.
*
* @param command to register.
*/
private void registerCommand(final ClassDescriptorResolutionCommand command) {
// TODO temporal implementation - following 2 lines need to be revised!
// ClassLoaderNature clNature = new ClassLoaderNature(command);
// clNature.setClassLoader(getClass().getClassLoader());
command.setClassDescriptorResolver(this);
_commands.put(command.getClass().getName(), command);
}
/**
* {@inheritDoc}
*/
public ClassDescriptor resolve(final String type) throws ResolverException {
try {
if (getMappingLoader().getClassLoader() != null) {
return resolve(getMappingLoader().getClassLoader().loadClass(type));
}
return resolve(Class.forName(type));
} catch (ClassNotFoundException e) {
throw new ResolverException("Problem loading class " + type);
}
}
/**
* Returns the ClassDescriptor for the given class using the following
* strategy.<br>
* <br>
* <ul>
* <li>Lookup the class descriptor cache
* <li>Call {@link ClassResolutionByMappingLoader} command
* <li>Call {@link ClassResolutionByFile} command
* </ul>
*
* @param type the Class to find the ClassDescriptor for
* @return the ClassDescriptor for the given class, null if not found
* @throws ResolverException Indicates that the given {@link Class} cannot be resolved.
*/
public ClassDescriptor resolve(final Class<?> type) throws ResolverException {
if (type == null) {
return null;
}
ClassDescriptor classDesc = null;
// 1) consult with cache
classDesc = resolveByCache(type);
if (classDesc != null) {
return classDesc;
}
// generate ClassDescriptor from annotated Class
classDesc = lookup(ClassResolutionByAnnotations.class.getName()).resolve(type);
if (classDesc != null) {
registerDescriptor(type, classDesc);
return classDesc;
}
classDesc = lookup(ClassResolutionByMappingLoader.class.getName()).resolve(type);
if (classDesc != null) {
registerDescriptor(type, classDesc);
return classDesc;
}
// 3) load ClassDescriptor from file system
classDesc = lookup(ClassResolutionByFile.class.getName()).resolve(type);
if (classDesc != null) {
registerDescriptor(type, classDesc);
return classDesc;
}
// 4) load ClassDescriptor from file system through CDR file
// (as added via addPackage())
classDesc = (lookup(ClassResolutionByCDR.class.getName())).resolve(type);
if (classDesc != null) {
registerDescriptor(type, classDesc);
return classDesc;
}
// TODO consider for future extensions
// String pkgName = getPackageName(type.getName());
//
// //-- check package mapping
// Mapping mapping = loadPackageMapping(pkgName, type.getClassLoader());
// if (mapping != null) {
// try {
// MappingLoader mappingLoader = mapping.getResolver(BindingType.JDO);
// classDesc = mappingLoader.getDescriptor(type);
// }
// catch(MappingException mx) {}
// if (classDesc != null) {
// associate(type, classDesc);
// return classDesc;
// }
// }
return classDesc;
}
/**
* Look up the given command in the command map.
*
* @param commandName The command.
* @return A {@link ClassDescriptorResolutionCommand}, null if not found.
*/
private ClassDescriptorResolutionCommand lookup(final String commandName) {
return _commands.get(commandName);
}
/**
* Resolves a {@link ClassDescriptor} by a cache lookup.
*
* @param type type to look up.
* @return a {@link ClassDescriptor} if found, null if not.
*/
private ClassDescriptor resolveByCache(final Class<?> type) {
return _classDescriptorCache.get(type);
}
/**
* {@inheritDoc}
*/
public void registerDescriptor(final Class<?> type, final ClassDescriptor classDescriptor) {
_classDescriptorCache.put(type, classDescriptor);
}
/**
* {@inheritDoc}
*/
public MappingLoader getMappingLoader() {
return _mappingLoader;
}
/**
* {@inheritDoc}
*/
public void setMappingLoader(final MappingLoader mappingLoader) {
_mappingLoader = mappingLoader;
for (ClassDescriptorResolutionCommand command : _commands.values()) {
if (command.hasNature(MappingLoaderNature.class.getName())) {
new MappingLoaderNature(command).setMappingLoader(mappingLoader);
}
if (command.hasNature(ClassLoaderNature.class.getName())) {
new ClassLoaderNature(command).setClassLoader(_mappingLoader.getClassLoader());
}
}
}
/**
* {@inheritDoc}
*/
public void addClass(final Class<?> domainClass) {
_classes.add(domainClass);
}
/**
* {@inheritDoc}
*/
public void addPackage(final String packageName) {
_packages.add(packageName);
for (ClassDescriptorResolutionCommand command : _commands.values()) {
if (command.hasNature(PackageBasedCDRResolutionNature.class.getName())) {
new PackageBasedCDRResolutionNature(command).addPackageName(packageName);
}
}
}
/**
* {@inheritDoc}
*/
public Iterator<ClassDescriptor> descriptorIterator() {
List<ClassDescriptor> allDescriptors = new ArrayList<ClassDescriptor>();
// add all descriptors cached by MappingLoader
allDescriptors.addAll(_mappingLoader.getDescriptors());
// add all descriptors as loaded from file system or by annotation
for (Class<?> aClass : _classes) {
ClassDescriptor resolve = lookup(ClassResolutionByFile.class.getName())
.resolve(aClass);
if (resolve != null) {
allDescriptors.add(resolve);
} else {
resolve = lookup(ClassResolutionByAnnotations.class.getName())
.resolve(aClass);
if (resolve != null) {
allDescriptors.add(resolve);
}
}
}
// add all descriptors as loaded by package from file system
ClassResolutionByCDR cdrNature = (ClassResolutionByCDR)
lookup(ClassResolutionByCDR.class.getName());
for (String packageName : _packages) {
Map<String, ClassDescriptor> descriptors = cdrNature.getDescriptors(packageName);
for (Map.Entry<String, ClassDescriptor> entry : descriptors.entrySet()) {
allDescriptors.add(entry.getValue());
}
}
return allDescriptors.iterator();
}
/**
* {@inheritDoc}
*/
public ClassLoader getClassLoader() {
return _mappingLoader.getClassLoader();
}
}