/* * Copyright (c) 2007, 2010, 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 net.enilink.komma.core; import static java.util.Collections.unmodifiableSet; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.Vector; import net.enilink.composition.annotations.Iri; /** * Defines the Scope of an {@link IEntityManager} and its factory. This includes * roles, literals, factories, datasets, and contexts. */ public class KommaModule { public static class Association { private Class<?> javaClass; private String rdfType; private Association(Class<?> javaClass, String rdfType) { this.javaClass = javaClass; this.rdfType = rdfType; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Association other = (Association) obj; if (javaClass == null) { if (other.javaClass != null) return false; } else if (!javaClass.equals(other.javaClass)) return false; if (rdfType == null) { if (other.rdfType != null) return false; } else if (!rdfType.equals(other.rdfType)) return false; return true; } public Class<?> getJavaClass() { return javaClass; } public String getRdfType() { return rdfType; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((javaClass == null) ? 0 : javaClass.hashCode()); result = prime * result + ((rdfType == null) ? 0 : rdfType.hashCode()); return result; } public String toString() { return javaClass.getName() + "=" + rdfType; } } private static class CombinedClassLoader extends ClassLoader { private Set<ClassLoader> alternatives; public CombinedClassLoader(ClassLoader parent) { super(parent); } public void addAlternative(ClassLoader loader) { if (alternatives == null) { alternatives = new LinkedHashSet<ClassLoader>(); } if (loader instanceof CombinedClassLoader) { alternatives.add(loader.getParent()); if (((CombinedClassLoader) loader).alternatives != null) { alternatives .addAll(((CombinedClassLoader) loader).alternatives); } } else { alternatives.add(loader); } // do not include parent class loader in alternatives alternatives.remove(getParent()); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (alternatives != null) { for (ClassLoader alt : alternatives) { try { return alt.loadClass(name); } catch (ClassNotFoundException e2) { // ignore and try next alternative class loader } } } throw new ClassNotFoundException(name); } @Override protected URL findResource(String name) { URL resource = null; if (alternatives != null) { for (ClassLoader alt : alternatives) { resource = alt.getResource(name); if (resource != null) { break; } } } return resource; } @Override protected Enumeration<URL> findResources(String name) throws IOException { if (alternatives != null) { Vector<URL> list = new Vector<URL>(); for (ClassLoader alt : alternatives) { Enumeration<URL> e = alt.getResources(name); while (e.hasMoreElements()) { list.add(e.nextElement()); } } return list.elements(); } return Collections.emptyEnumeration(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getParent() == null) ? 0 : getParent().hashCode()); result = prime * result + ((alternatives == null) ? 0 : alternatives.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof CombinedClassLoader)) return false; CombinedClassLoader other = (CombinedClassLoader) obj; if (getParent() == null) { if (other.getParent() != null) return false; } else if (!getParent().equals(other.getParent())) return false; if (alternatives == null) { if (other.alternatives != null) return false; } else if (!alternatives.equals(other.alternatives)) return false; return true; } } private final CombinedClassLoader cl; private Map<Class<?>, Association> annotations = new HashMap<Class<?>, Association>(); private Set<Association> datatypes = new HashSet<Association>(); private Set<Association> concepts = new HashSet<Association>(); private Set<Association> behaviours = new HashSet<Association>(); private Set<URI> readableGraphs = new LinkedHashSet<URI>(); private Set<URI> writableGraphs = new LinkedHashSet<URI>(); private Set<INamespace> namespaces = new HashSet<INamespace>(); public KommaModule() { cl = new CombinedClassLoader(getClass().getClassLoader()); } public KommaModule(ClassLoader classLoader) { cl = new CombinedClassLoader(classLoader); cl.addAlternative(getClass().getClassLoader()); } /** * Associates this annotation with its type. * * @param annotation * annotation class */ public KommaModule addAnnotation(Class<?> annotation) { String uri = null; for (Method m : annotation.getDeclaredMethods()) { if (m.isAnnotationPresent(Iri.class)) { uri = m.getAnnotation(Iri.class).value(); break; } } if (uri == null) { throw new IllegalArgumentException( "@Iri annotation required on method of " + annotation.getSimpleName()); } addAnnotation(annotation, uri); return this; } /** * Associates the annotation concept with the given type. * * @param annotation * annotation class * @param type * URI */ public KommaModule addAnnotation(Class<?> annotation, String type) { Association registered = annotations.get(annotation); if (registered != null && !registered.getRdfType().equals(type)) { throw new IllegalArgumentException( "annotation is already associated to type " + registered.getRdfType()); } annotations.put(annotation, new Association(annotation, type)); return this; } /** * Associates this behaviour with its default subject type. * * @param behaviour * class */ public KommaModule addBehaviour(Class<?> behaviour) { behaviours.add(new Association(behaviour, null)); return this; } /** * Associates this behaviour with the given subject type. * * @param behaviour * class * @param type * URI */ public KommaModule addBehaviour(Class<?> behaviour, String type) { behaviours.add(new Association(behaviour, type)); return this; } /** * Associates this concept with its default subject type. * * @param concept * interface or class */ public KommaModule addConcept(Class<?> concept) { concepts.add(new Association(concept, null)); return this; } /** * Associates this concept with the given subject type. * * @param concept * interface or class * @param type * URI */ public KommaModule addConcept(Class<?> concept, String type) { concepts.add(new Association(concept, type)); return this; } /** * Associates this datatype with the given uri within this factory. * * @param type * serializable class * @param datatype * URI */ public KommaModule addDatatype(Class<?> type, String uri) { datatypes.add(new Association(type, uri)); return this; } /** * Adds a prefix for a namespace. * * @param prefix * The prefix. * @param name * The namespace URI that the prefix maps to. */ public KommaModule addNamespace(String prefix, URI uri) { namespaces.add(new Namespace(prefix, uri)); return this; } /** * Adds a readable graph this module. This limits the readable scope to this * and other readable graphs. * * @param graph * @return this */ public KommaModule addReadableGraph(URI graph) { readableGraphs.add(graph); return this; } /** * Adds a writable graph this module. This limits the writable and the * readable scope to this and other writable graphs and causes any add * operations to be added to these graphs. * * @param graph * @return this */ public KommaModule addWritableGraph(URI graph) { writableGraphs.add(graph); addReadableGraph(graph); return this; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; KommaModule other = (KommaModule) obj; if (annotations == null) { if (other.annotations != null) return false; } else if (!annotations.equals(other.annotations)) return false; if (behaviours == null) { if (other.behaviours != null) return false; } else if (!behaviours.equals(other.behaviours)) return false; if (cl == null) { if (other.cl != null) return false; } else if (!cl.equals(other.cl)) return false; if (concepts == null) { if (other.concepts != null) return false; } else if (!concepts.equals(other.concepts)) return false; if (datatypes == null) { if (other.datatypes != null) return false; } else if (!datatypes.equals(other.datatypes)) return false; if (readableGraphs == null) { if (other.readableGraphs != null) return false; } else if (!readableGraphs.equals(other.readableGraphs)) return false; if (writableGraphs == null) { if (other.writableGraphs != null) return false; } else if (!writableGraphs.equals(other.writableGraphs)) return false; return true; } public Collection<Association> getAnnotations() { return annotations.values(); } public Collection<Association> getBehaviours() { return unmodifiableSet(behaviours); } public synchronized ClassLoader getClassLoader() { return cl; } public Collection<Association> getConcepts() { return unmodifiableSet(concepts); } public Collection<Association> getDatatypes() { return unmodifiableSet(datatypes); } public Set<INamespace> getNamespaces() { return unmodifiableSet(namespaces); } public Set<URI> getReadableGraphs() { return unmodifiableSet(readableGraphs); } public Set<URI> getWritableGraphs() { return unmodifiableSet(writableGraphs); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((annotations == null) ? 0 : annotations.hashCode()); result = prime * result + ((behaviours == null) ? 0 : behaviours.hashCode()); result = prime * result + ((cl == null) ? 0 : cl.hashCode()); result = prime * result + ((concepts == null) ? 0 : concepts.hashCode()); result = prime * result + ((datatypes == null) ? 0 : datatypes.hashCode()); result = prime * result + ((readableGraphs == null) ? 0 : readableGraphs.hashCode()); result = prime * result + ((writableGraphs == null) ? 0 : writableGraphs.hashCode()); return result; } /** * Include all the information from the given module in this module. * * @param module * to be included * @return this */ public KommaModule includeModule(KommaModule module) { return includeModule(module, true); } /** * Include the information from the given module in this module, but allow * for the graphs (read/write) to be ignored upon request. * * @param module * to be included * @param includeGraphs * flag to indicate wether to include the graphs * @return this */ public KommaModule includeModule(KommaModule module, boolean includeGraphs) { annotations.putAll(module.annotations); datatypes.addAll(module.datatypes); concepts.addAll(module.concepts); behaviours.addAll(module.behaviours); if (includeGraphs) { readableGraphs.addAll(module.writableGraphs); readableGraphs.addAll(module.readableGraphs); namespaces.addAll(module.namespaces); } if (!cl.equals(module.cl)) { cl.addAlternative(module.cl); } return this; } @Override public String toString() { if (!writableGraphs.isEmpty()) return writableGraphs.toString(); Set<Package> pkg = new LinkedHashSet<Package>(); for (Association concept : concepts) { pkg.add(concept.getJavaClass().getPackage()); } return pkg.toString(); } }