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(); } }