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

org.openrdf.repository.object.managers.RoleMapper 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.
 * Copyright (c) 2011 Talis Inc., Some 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.managers;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.openrdf.annotations.Classpath;
import org.openrdf.annotations.Iri;
import org.openrdf.annotations.Mixin;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.repository.object.exceptions.ObjectStoreConfigException;
import org.openrdf.repository.object.managers.helpers.HierarchicalRoleMapper;
import org.openrdf.repository.object.managers.helpers.RoleMatcher;
import org.openrdf.repository.object.managers.helpers.WeakValueMap;
import org.openrdf.repository.object.vocabulary.MSG;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Tracks the annotation, concept, and behaviour classes and what rdf:type they
 * should be used with.
 * 
 * @author James Leigh
 * 
 */
public class RoleMapper implements Cloneable {
	private static final WeakHashMap,ClassLoader>> classloaders = new WeakHashMap,ClassLoader>>();
	private ValueFactory vf;
	private Logger logger = LoggerFactory.getLogger(RoleMapper.class);
	private HierarchicalRoleMapper roleMapper = new HierarchicalRoleMapper();
	private Map>> instances = new ConcurrentHashMap>>(
			256);
	private RoleMatcher matches = new RoleMatcher();
	private Map annotations = new HashMap();
	private Map annotationURIs = new HashMap();
	private Map, String> complementIDs;
	private Map, Class> complementClasses;
	private Map, List>> intersections;
	private Set> conceptClasses = new HashSet>();

	public RoleMapper() {
		this(ValueFactoryImpl.getInstance());
	}

	public RoleMapper(ValueFactory vf) {
		this.vf = vf;
		roleMapper.setURIFactory(vf);
		complementIDs = new ConcurrentHashMap, String>();
		complementClasses = new ConcurrentHashMap, Class>();
		intersections = new ConcurrentHashMap, List>>();
	}

	public RoleMapper clone() {
		try {
			RoleMapper cloned = (RoleMapper) super.clone();
			cloned.roleMapper = roleMapper.clone();
			cloned.instances = clone(instances);
			cloned.matches = matches.clone();
			cloned.annotations = new HashMap(annotations);
			cloned.annotationURIs = new HashMap(annotationURIs);
			cloned.complementIDs = new ConcurrentHashMap, String>(complementIDs);
			cloned.complementClasses = new ConcurrentHashMap, Class>(complementClasses);
			cloned.intersections = clone(intersections);
			cloned.conceptClasses = new HashSet>(conceptClasses);
			return cloned;
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}

	private  Map> clone(Map> map) {
		Map> cloned = new ConcurrentHashMap>(map);
		for (Map.Entry> e : cloned.entrySet()) {
			e.setValue(new CopyOnWriteArrayList(e.getValue()));
		}
		return cloned;
	}

	public Collection> getConceptClasses() {
		return conceptClasses;
	}

	public Collection> findIndividualRoles(URI instance,
			Collection> classes) {
		List> list = instances.get(instance);
		if (list != null) {
			classes.addAll(list);
			addImpliedRoles(list, classes);
		}
		list = new ArrayList>();
		matches.findRoles(instance.stringValue(), list);
		classes.addAll(list);
		addImpliedRoles(list, classes);
		return classes;
	}

	public boolean isRecordedConcept(URI type, ClassLoader cl) {
		if (roleMapper.isTypeRecorded(type)) {
			for (Class role : findAllRoles(type)) {
				if (findType(role) != null)
					return true;
			}
		}
		if ("java:".equals(type.getNamespace())) {
			try {
				synchronized (cl) {
					java.lang.Class.forName(type.getLocalName(), true, cl);
				}
				return true;
			} catch (ClassNotFoundException e) {
				return false;
			}
		}
		return false;
	}

	public Class findInterfaceConcept(URI uri) {
		Class concept = null;
		Class mapped = null;
		Collection> rs = findAllRoles(uri);
		for (Class r : rs) {
			URI type = findType(r);
			if (r.isInterface() && type != null) {
				concept = r;
				if (uri.equals(type)) {
					mapped = r;
					if (r.getSimpleName().equals(uri.getLocalName())) {
						return r;
					}
				}
			}
		}
		if (mapped != null)
			return mapped;
		if (concept != null)
			return concept;
		return null;
	}

	public Class findConcept(URI uri, ClassLoader cl) {
		if (roleMapper.isTypeRecorded(uri)) {
			Class concept = null;
			Class mapped = null;
			Class face = null;
			Collection> rs = findAllRoles(uri);
			for (Class r : rs) {
				URI type = findType(r);
				if (type != null && r.isInterface()) {
					concept = r;
				}
				if (uri.equals(type)) {
					mapped = r;
					if (r.getSimpleName().equals(uri.getLocalName())) {
						return r;
					} else if (r.isInterface()) {
						face = r;
					}
				}
			}
			if (face != null)
				return face;
			if (mapped != null)
				return mapped;
			if (concept != null)
				return concept;
		}
		if ("java:".equals(uri.getNamespace())) {
			try {
				if (cl == null) {
					ClassLoader ccl = Thread.currentThread().getContextClassLoader();
					return java.lang.Class.forName(uri.getLocalName(), true, ccl);
				}
				return java.lang.Class.forName(uri.getLocalName(), true, cl);
			} catch (ClassNotFoundException e) {
				return null;
			}
		}
		return null;
	}

	public Collection> findRoles(URI type) {
		return findAdditionalRoles(roleMapper.findRoles(type));
	}

	public Collection> findRoles(Collection types,
			Collection> roles) {
		return findAdditionalRoles(roleMapper.findRoles(types, roles));
	}

	public Collection> findAdditionalRoles(Collection> classes) {
		if (intersections.isEmpty() && complementIDs.isEmpty() && complementClasses.isEmpty())
			return classes;
		List> result;
		result = new ArrayList>(classes.size() * 2 + 2);
		result.addAll(classes);
		int before = result.size();
		addIntersectionsAndComplements(result);
		int after = result.size();
		if (before != after) {
			ArrayList> anonymous;
			anonymous = new ArrayList>(result.subList(before, after));
			addImpliedRoles(anonymous, result);
		}
		return result;
	}

	public Collection findSubTypes(Class role, Collection rdfTypes) {
		return roleMapper.findSubTypes(role, rdfTypes);
	}

	public URI findType(Class concept) {
		return roleMapper.findType(concept);
	}

	public boolean isNamedTypePresent() {
		return roleMapper.isNamedTypePresent();
	}

	public boolean isIndividualRolesPresent(URI instance) {
		return !matches.isEmpty() || !instances.isEmpty() && instances.containsKey(instance);
	}

	public URI findAnnotation(Method ann) {
		return annotations.get(ann);
	}

	public Method findAnnotationMethod(URI uri) {
		return annotationURIs.get(uri);
	}

	public boolean isRecordedAnnotation(URI uri) {
		return findAnnotationMethod(uri) != null;
	}

	public void addAnnotation(Class annotation) {
		for (Method m : annotation.getDeclaredMethods()) {
			if (!m.isAnnotationPresent(Iri.class)) {
				String msg = "@" + Iri.class.getSimpleName()
						+ " annotation required in " + m.toGenericString();
				throw new IllegalArgumentException(msg);
			}
			String uri = m.getAnnotation(Iri.class).value();
			addAnnotation(m, new URIImpl(uri));
		}
	}

	public void addAnnotation(Class annotation, URI uri) {
		if (annotation.getDeclaredMethods().length != 1)
			throw new IllegalArgumentException(
					"Must specify annotation method if multiple methods exist: "
							+ annotation);
		addAnnotation(annotation.getDeclaredMethods()[0], uri);
	}

	public void addAnnotation(Method annotation) {
		if (!annotation.isAnnotationPresent(Iri.class))
			throw new IllegalArgumentException("@" + Iri.class.getSimpleName()
					+ " annotation required in " + annotation.toGenericString());
		String uri = annotation.getAnnotation(Iri.class).value();
		addAnnotation(annotation, new URIImpl(uri));
	}

	public void addAnnotation(Method annotation, URI uri) {
		annotations.put(annotation, uri);
		annotationURIs.put(uri, annotation);
		if (annotation.isAnnotationPresent(Iri.class)) {
			String iri = annotation.getAnnotation(Iri.class).value();
			if (!uri.stringValue().equals(iri)) {
				addAnnotation(annotation);
			}
		}
	}

	public void addConcept(Class role) throws ObjectStoreConfigException {
		recordRole(role, role, null, true, true, true);
	}

	public void addConcept(Class role, URI type)
			throws ObjectStoreConfigException {
		recordRole(role, role, type, true, true, true);
	}

	public void addBehaviour(Class role) throws ObjectStoreConfigException {
		assertBehaviour(role);
		boolean hasType = false;
		for (Class face : role.getInterfaces()) {
			boolean recorded = recordRole(role, face, null, true, false, false);
			if (recorded && hasType) {
				throw new ObjectStoreConfigException(role.getSimpleName()
						+ " can only implement one concept");
			} else {
				hasType |= recorded;
			}
		}
		if (!hasType)
			throw new ObjectStoreConfigException(role.getSimpleName()
					+ " must implement a concept or mapped explicitly");
	}

	public void addBehaviour(Class role, URI type)
			throws ObjectStoreConfigException {
		assertBehaviour(role);
		recordRole(role, null, type, true, false, false);
	}

	private void assertBehaviour(Class role)
			throws ObjectStoreConfigException {
		if (isAnnotationPresent(role))
			throw new ObjectStoreConfigException(role.getSimpleName()
					+ " cannot have a concept annotation");
		if (role.isInterface())
			throw new ObjectStoreConfigException(role.getSimpleName()
					+ " is an interface and not a behaviour");
		for (Method method : role.getDeclaredMethods()) {
			if (isAnnotationPresent(method)
					&& method.getName().startsWith("get"))
				throw new ObjectStoreConfigException(role.getSimpleName()
						+ " cannot have a property annotation");
		}
	}

	private Collection> findAllRoles(URI type) {
		Set> set = new HashSet>();
		for (Class role : findRoles(type)) {
			if (set.add(role)) {
				addInterfaces(set, role.getSuperclass());
				addInterfaces(set, role.getInterfaces());
			}
		}
		return set;
	}

	private void addInterfaces(Set> set, Class... list) {
		for (Class c : list) {
			if (c != null && set.add(c)) {
				addInterfaces(set, c.getSuperclass());
				addInterfaces(set, c.getInterfaces());
			}
		}
	}

	private boolean isAnnotationPresent(AnnotatedElement role)
			throws ObjectStoreConfigException {
		return role.isAnnotationPresent(Iri.class);
	}

	private boolean recordRole(Class role, Class elm, URI rdfType,
			boolean equiv, boolean concept, boolean primary)
			throws ObjectStoreConfigException {
		boolean hasType = false;
		if (rdfType != null) {
			if (concept) {
				roleMapper.recordConcept(role, rdfType, equiv, primary);
			} else {
				roleMapper.recordBehaviour(role, rdfType, equiv);
			}
			hasType = true;
		} else if (elm != null) {
			URI defType = findDefaultType(elm);
			if (defType != null) {
				if (concept) {
					roleMapper.recordConcept(role, defType, equiv, role.equals(elm));
				} else {
					roleMapper.recordBehaviour(role, defType, equiv);
				}
				hasType = true;
			}
			hasType |= recordAnonymous(role, elm, concept);
		}
		if (!hasType && elm != null) {
			for (Class face : elm.getInterfaces()) {
				hasType |= recordRole(role, face, null, equiv, concept, false);
			}
		}
		if (!hasType && primary) {
			throw new ObjectStoreConfigException(role.getSimpleName()
					+ " does not have an RDF type mapping");
		}
		recordMixins(role, elm, rdfType);
		if (concept && !role.isInterface()) {
			conceptClasses.add(role);
		}
		return hasType;
	}

	private void recordMixins(Class role, Class elm, URI rdfType)
			throws ObjectStoreConfigException {
		Mixin mixin = role.getAnnotation(Mixin.class);
		if (mixin != null) {
			for (Class mix : mixin.value()) {
				assertBehaviour(mix);
				recordRole(mix, elm, rdfType, true, false, false);
			}
			for (String name : mixin.name()) {
				Classpath cp = role.getAnnotation(Classpath.class);
				String[] jars = cp == null ? new String[0] : cp.value();
				Class mix = findClass(name, role.getClassLoader(), jars,
						role);
				if (mix != null) {
					assertBehaviour(mix);
					recordRole(mix, elm, rdfType, true, false, false);
				} else {
					logger.error("Could not find {} in {}", name, jars);
				}
			}
		}
	}

	private Class findClass(String name, ClassLoader cl, String[] jars,
			Class base) {
		try {
			return Class.forName(name, true, findClassLoader(cl, jars, base));
		} catch (Throwable e) {
			logger.error(e.getMessage(), e);
			return null;
		}
	}

	private ClassLoader findClassLoader(ClassLoader parent, String[] jars,
			Class base) {
		if (jars.length == 0)
			return parent; // no children
		Set urls = resolve(jars, base);
		if (urls.isEmpty())
			return parent; // no valid children
		return createClassLoader(parent, urls);
	}

	private static synchronized ClassLoader createClassLoader(ClassLoader parent, Set urls) {
		WeakValueMap, ClassLoader> map = classloaders.get(parent);
		if (map == null) {
			map = new WeakValueMap, ClassLoader>();
			classloaders.put(parent, map);
		}
		ClassLoader direct = map.get(urls);
		if (direct != null)
			return direct; // already loaded
		for (Map.Entry, ClassLoader> e : map.entrySet()) {
			ClassLoader p = e.getValue();
			if (p != null && urls.containsAll(e.getKey())) {
				Set set = new HashSet(urls);
				set.removeAll(e.getKey());
				URL[] array = set.toArray(new URL[set.size()]);
				ClassLoader cl = new URLClassLoader(array, p);
				map.put(urls, cl);
				return cl; // reuse an exiting cl as parent
			}
		}
		URL[] array = urls.toArray(new URL[urls.size()]);
		ClassLoader cl = new URLClassLoader(array, parent);
		map.put(urls, cl);
		return cl; // new cl
	}

	private Set resolve(String[] jars, Class base) {
		Set urls = new HashSet(jars.length);
		for (int i = 0; i < jars.length; i++) {
			try {
				urls.add(resolve(jars[i], base));
			} catch (MalformedURLException e) {
				logger.error(e.getMessage(), e);
			}
		}
		return urls;
	}

	private URL resolve(String jar, Class base) throws MalformedURLException {
		java.net.URI jurl = java.net.URI.create(jar);
		if (jurl.isAbsolute())
			return jurl.toURL();
		URL url = base.getResource(jar);
		if (url != null)
			return url;
		java.net.URI uri = java.net.URI.create(getSystemId(base));
		return uri.resolve(jar).toURL();
	}

	private String getSystemId(Class base) {
		if (base.isAnnotationPresent(Iri.class))
			return base.getAnnotation(Iri.class).value();
		String name = base.getSimpleName() + ".class";
		URL url = base.getResource(name);
		if (url != null)
			return url.toExternalForm();
		return "java:" + base.getName();
	}

	private boolean recordAnonymous(Class role, Class elm,
			boolean isConcept) throws ObjectStoreConfigException {
		boolean recorded = false;
		for (Annotation ann : elm.getAnnotations()) {
			for (Method m : ann.annotationType().getDeclaredMethods()) {
				try {
					URI name = findAnnotation(m);
					if (name == null && m.isAnnotationPresent(Iri.class)) {
						addAnnotation(m);
						name = findAnnotation(m);
					}
					if (name == null)
						continue;
					Object value = m.invoke(ann);
					recorded |= recordAnonymous(role, isConcept, name, value);
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
					continue;
				}
			}
		}
		return recorded;
	}

	private boolean recordAnonymous(Class role, boolean isConcept, URI name,
			Object value) throws ObjectStoreConfigException {
		boolean recorded = false;
		if (OWL.EQUIVALENTCLASS.equals(name)) {
			Object[] values = (Object[]) value;
			for (Object v : values) {
				if (v instanceof Class) {
					Class concept = (Class) v;
					URI uri = findDefaultType(concept);
					if (uri != null) {
						// only equivalent named concepts are supported here
						recorded |= recordRole(role, concept, uri, true, isConcept, false);
					}
				} else if (v instanceof String) {
					URI uri = vf.createURI((String) v);
					recorded |= recordRole(role, role, uri, true, isConcept, false);
				} else {
					logger.error("{} must have a value of type Class[] or String[]",
							name);
				}
			}
		}
		if (MSG.MATCHING.equals(name)) {
			String[] values = (String[]) value;
			for (String pattern : values) {
				matches.addRoles(pattern, role);
				recorded = true;
			}
		}
		if (OWL.ONEOF.equals(name)) {
			String[] values = (String[]) value;
			for (String instance : values) {
				URI uri = vf.createURI(instance);
				List> list = instances.get(uri);
				if (list == null) {
					list = new CopyOnWriteArrayList>();
					instances.put(uri, list);
				}
				list.add(role);
				recorded = true;
			}
		}
		if (OWL.COMPLEMENTOF.equals(name)) {
			if (value instanceof Object[]) {
				Object[] ar = (Object[]) value;
				if (ar.length != 1) {
					logger.error("{} must have exactly one value",
							name);
				}
				value = ar[0];
			}
			if (value instanceof Class) {
				Class concept = (Class) value;
				recordRole(concept, concept, null, true, true, true);
				complementClasses.put(role, concept);
				recorded = true;
			} else if (value instanceof String) {
				complementIDs.put(role, (String) value);
				recorded = true;
			} else {
				logger.error("{} must have a value of type Class or String",
						name);
			}
		}
		if (OWL.INTERSECTIONOF.equals(name)) {
			List> ofs = new ArrayList>();
			loop: for (Object v : (Object[]) value) {
				if (v instanceof Class) {
					Class concept = (Class) v;
					recordRole(concept, concept, null, true, true, true);
					ofs.add(concept);
				} else if (v instanceof String) {
					Class superclass = role.getSuperclass();
					if (superclass != null
							&& superclass.isAnnotationPresent(Iri.class)) {
						if (v.equals(superclass.getAnnotation(Iri.class)
								.value())) {
							recordRole(superclass, superclass, null, true, true, true);
							ofs.add(superclass);
							continue loop;
						}
					}
					for (Class sp : role.getInterfaces()) {
						if (sp.isAnnotationPresent(Iri.class)) {
							if (v.equals(sp.getAnnotation(Iri.class).value())) {
								recordRole(sp, sp, null, true, true, true);
								ofs.add(sp);
								continue loop;
							}
						}
					}
					logger.error("{} can only reference super classes", name);
				} else {
					logger.error(
							"{} must have a value of type Class[] or String[]",
							name);
				}
			}
			intersections.put(role, ofs);
			recorded = true;
		}
		if (OWL.UNIONOF.equals(name)) {
			for (Object v : (Object[]) value) {
				if (v instanceof Class) {
					Class concept = (Class) v;
					recordRole(concept, concept, null, true, true, true);
					if (role.isAssignableFrom(concept)) {
						recorded = true; // implied
					} else {
						recorded |= recordRole(role, concept, null, false,
								isConcept, false);
					}
				} else if (v instanceof String) {
					recorded |= recordRole(role, null,
							vf.createURI((String) v), false, isConcept, false);
				} else {
					logger.error(
							"{} must have a value of type Class[] or String[]",
							name);
				}
			}
		}
		return recorded;
	}

	private URI findDefaultType(AnnotatedElement elm) {
		if (elm.isAnnotationPresent(Iri.class)) {
			String value = elm.getAnnotation(Iri.class).value();
			if (value != null) {
				return vf.createURI(value);
			}
		}
		return null;
	}

	private void addIntersectionsAndComplements(Collection> roles) {
		for (Map.Entry, List>> e : intersections.entrySet()) {
			Class inter = e.getKey();
			List> of = e.getValue();
			if (!roles.contains(inter) && intersects(roles, of)) {
				roles.add(inter);
			}
		}
		if (complementIDs.isEmpty() && complementClasses.isEmpty())
			return;
		boolean complementAdded = false;
		for (Map.Entry, String> e : complementIDs.entrySet()) {
			Class comp = e.getKey();
			String of = e.getValue();
			if (!roles.contains(comp) && !contains(roles, of)) {
				complementAdded = true;
				roles.add(comp);
			}
		}
		for (Map.Entry, Class> e : complementClasses.entrySet()) {
			Class comp = e.getKey();
			Class of = e.getValue();
			if (!roles.contains(comp) && !contains(roles, of)) {
				complementAdded = true;
				roles.add(comp);
			}
		}
		if (!complementAdded)
			return;
		for (Map.Entry, List>> e : intersections.entrySet()) {
			Class inter = e.getKey();
			List> of = e.getValue();
			if (!roles.contains(inter) && intersects(roles, of)) {
				roles.add(inter);
			}
		}
	}

	private void addImpliedRoles(Collection> anonymous,
			Collection> result) {
		for (Class r : anonymous) {
			Class sc = r.getSuperclass();
			if (sc != null) {
				URI uri = findType(sc);
				if (uri != null) {
					result.addAll(roleMapper.findRoles(uri));
				}
			}
			for (Class c : r.getInterfaces()) {
				URI uri = findType(c);
				if (uri != null) {
					result.addAll(roleMapper.findRoles(uri));
				}
			}
		}
	}

	private boolean intersects(Collection> roles, Collection> ofs) {
		for (Class of : ofs) {
			if (!contains(roles, of))
				return false;
		}
		return true;
	}

	private boolean contains(Collection> roles, Class of) {
		for (Class type : roles) {
			if (of.isAssignableFrom(type))
				return true;
		}
		return false;
	}

	private boolean contains(Collection> roles, String iri) {
		Set> set = new HashSet>();
		for (Class type : roles) {
			if (isImplementationPresent(type, iri, set))
				return true;
		}
		return false;
	}

	private boolean isImplementationPresent(Class type, String iri, Set> ignore) {
		if (type == null || ignore.contains(type))
			return false;
		Iri id = type.getAnnotation(Iri.class);
		if (id != null && iri.equals(id.value()))
			return true;
		for (Class face : type.getInterfaces()) {
			if (isImplementationPresent(face, iri, ignore))
				return true;
		}
		return isImplementationPresent(type.getSuperclass(), iri, ignore);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy