All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrdf.repository.object.composition.ClassResolver Maven / Gradle / Ivy

Go to download

The Object Composition library merges multiple Java objects into a single multi-subject object.

There is a newer version: 2.4
Show newest version
/*
 * Copyright (c) 2009, James Leigh All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution. 
 * - Neither the name of the openrdf.org nor the names of its contributors may
 *   be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package org.openrdf.repository.object.composition;

import org.openrdf.model.URI;
import org.openrdf.repository.object.composition.helpers.BehaviourConstructor;
import org.openrdf.repository.object.composition.helpers.BehaviourProviderService;
import org.openrdf.repository.object.composition.helpers.ClassComposer;
import org.openrdf.repository.object.exceptions.ObjectCompositionException;
import org.openrdf.repository.object.exceptions.ObjectStoreConfigException;
import org.openrdf.repository.object.managers.PropertyMapper;
import org.openrdf.repository.object.managers.RoleMapper;
import org.openrdf.repository.object.managers.helpers.DirUtil;
import org.openrdf.repository.object.managers.helpers.RoleClassLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static java.lang.reflect.Modifier.isAbstract;

/**
 * Find a proxy class that can be used for a set of rdf:types.
 * 
 * @author James Leigh
 *
 */
public class ClassResolver {
	private static final Set EMPTY_SET = Collections.emptySet();
	private static final String PKG_PREFIX = "object.proxies._";
	private static final String CLASS_PREFIX = "_EntityProxy";

	private static RoleMapper newRoleMapper(ClassLoader cl) throws ObjectStoreConfigException {
		if (cl == null) {
			return newRoleMapper(ClassResolver.class.getClassLoader());
		} else {
			RoleMapper mapper = new RoleMapper();
			new RoleClassLoader(mapper).loadRoles(cl);
			return mapper;
		}
	}

	private final Logger logger = LoggerFactory.getLogger(ClassResolver.class);
	private final PropertyMapper properties;
	private final ClassFactory cp;
	private final Collection> baseClassRoles;
	private final RoleMapper mapper;
	private final Class blank;
	private final ConcurrentMap, Class> multiples = new ConcurrentHashMap, Class>();
	private final BehaviourProviderService behaviourService;

	public ClassResolver() throws ObjectStoreConfigException {
		this(Thread.currentThread().getContextClassLoader());
	}

	public ClassResolver(ClassLoader cl) throws ObjectStoreConfigException {
		this(newRoleMapper(cl), cl == null ? ClassResolver.class.getClassLoader() : cl);
	}

	public ClassResolver(RoleMapper mapper, ClassLoader cl)
			throws ObjectStoreConfigException {
		this(mapper, new PropertyMapper(cl, mapper.isNamedTypePresent()), cl);
	}

	public ClassResolver(RoleMapper mapper, PropertyMapper properties,
			ClassLoader cl) throws ObjectStoreConfigException {
		this.mapper = mapper;
		this.properties = properties;
		try {
			File dir = DirUtil.createTempDir("classes");
			DirUtil.deleteOnExit(dir);
			this.cp = new ClassFactory(dir, cl);
			behaviourService = BehaviourProviderService.newInstance(cp);
			Collection> baseClassRoles = mapper.getConceptClasses();
			this.baseClassRoles = new ArrayList>(baseClassRoles.size());
			for (Class base : baseClassRoles) {
				try {
					// ensure the base class has a default constructor
					base.getConstructor();
					this.baseClassRoles.add(base);
				} catch (NoSuchMethodException e) {
					logger.warn("Concept will only be mergable: {}", base);
				}
			}
			blank = resolveBlankEntity(EMPTY_SET);
		} catch (IOException e) {
			throw new ObjectStoreConfigException(e);
		}
	}

	public RoleMapper getRoleMapper() {
		return mapper;
	}

	public PropertyMapper getPropertyMapper() {
		return properties;
	}

	public ClassLoader getClassLoader() {
		return cp;
	}

	public Class resolveBlankEntity() {
		return blank;
	}

	public Class resolveBlankEntity(Set types) {
		Class proxy = multiples.get(types);
		if (proxy != null)
			return proxy;
		Collection> roles = new ArrayList>();
		proxy = resolveRoles(mapper.findRoles(types, roles));
		multiples.putIfAbsent(types, proxy);
		return proxy;
	}

	public Class resolveEntity(URI resource) {
		if (resource != null && mapper.isIndividualRolesPresent(resource))
			return resolveIndividualEntity(resource, EMPTY_SET);
		return resolveBlankEntity();
	}

	public Class resolveEntity(URI resource, Set types) {
		if (resource != null && mapper.isIndividualRolesPresent(resource))
			return resolveIndividualEntity(resource, types);
		return resolveBlankEntity(types);
	}

	private Class resolveIndividualEntity(URI resource, Collection types) {
		Collection> roles = new ArrayList>();
		roles = mapper.findIndividualRoles(resource, roles);
		roles = mapper.findRoles(types, roles);
		return resolveRoles(roles);
	}

	private Class resolveRoles(Collection> roles) {
		try {
			String className = getJavaClassName(roles);
			return getComposedBehaviours(className, roles);
		} catch (Exception e) {
			List roleNames = new ArrayList();
			for (Class f : roles) {
				roleNames.add(f.getSimpleName());
			}
			throw new ObjectCompositionException(e.toString()
					+ " for entity with roles: " + roleNames, e);
		}
	}

	private Class getComposedBehaviours(String className,
			Collection> roles) throws Exception {
		synchronized (cp) {
			try {
				return cp.classForName(className);
			} catch (ClassNotFoundException e1) {
				return composeBehaviours(className, roles);
			}
		}
	}

	private Class composeBehaviours(String className,
			Collection> roles) throws Exception {
		List> types = new ArrayList>(roles.size());
		types.addAll(roles);
		types = removeSuperClasses(types);
		ClassComposer cc = new ClassComposer(className, types.size());
		cc.setClassFactory(cp);
		Set> behaviours = new LinkedHashSet>(types.size());
		Set concretes = new LinkedHashSet(types.size());
		Set> bases = new LinkedHashSet>();
		Class baseClass = Object.class;
		for (Class role : types) {
			if (role.isInterface()) {
				cc.addInterface(role);
			} else {
				if (baseClassRoles.contains(role)) {
					if (baseClass != null && baseClass.isAssignableFrom(role)) {
						baseClass = role;
					} else if (!role.equals(baseClass)) {
						baseClass = null;
					}
					bases.add(role);
				} else if (!isAbstract(role.getModifiers())) {
					try {
						concretes.add(new BehaviourConstructor(role));
					} catch (NoSuchMethodException e) {
						// ignore
					}
				}
				behaviours.add(role);
			}
		}
		if (baseClass == null) {
			logger.error("Cannot compose multiple concept classes: " + types);
		} else {
			cc.setBaseClass(baseClass);
		}
		Set> allRoles = new HashSet>(behaviours.size() + cc.getInterfaces().size());
		allRoles.addAll(behaviours);
		allRoles.addAll(cc.getInterfaces());
		PropertyMapper pm = properties;
		cc.addAllBehaviours(concretes);
		cc.addAllBehaviours(behaviourService.findImplementations(pm, allRoles, bases));
		return cc.compose();
	}

	private List> removeSuperClasses(List> classes) {
		for (int i = classes.size() - 1; i >= 0; i--) {
			Class c = classes.get(i);
			for (int j = classes.size() - 1; j >= 0; j--) {
				Class d = classes.get(j);
				if (i != j && c.isAssignableFrom(d)
						&& c.isInterface() == d.isInterface()) {
					classes.remove(i);
					break;
				}
			}
		}
		return classes;
	}

	private String getJavaClassName(Collection> javaClasses) {
		String phex = packagesToHexString(javaClasses);
		String chex = classesToHexString(javaClasses);
		return PKG_PREFIX + phex + "." + CLASS_PREFIX + chex;
	}

	private String packagesToHexString(Collection> javaClasses) {
		TreeSet names = new TreeSet();
		for (Class clazz : javaClasses) {
			if (clazz.getPackage() != null) {
				names.add(clazz.getPackage().getName());
			}
		}
		return toHexString(names);
	}

	private String classesToHexString(Collection> javaClasses) {
		TreeSet names = new TreeSet();
		for (Class clazz : javaClasses) {
			names.add(clazz.getName());
		}
		return toHexString(names);
	}

	private String toHexString(TreeSet names) {
		long hashCode = 0;
		for (String name : names) {
			hashCode = 31 * hashCode + name.hashCode();
		}
		return Long.toHexString(hashCode);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy