/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.Callable; import org.hypergraphdb.HGQuery.hg; import org.hypergraphdb.atom.HGSubsumes; import org.hypergraphdb.atom.HGTypeStructuralInfo; import org.hypergraphdb.event.HGLoadPredefinedTypeEvent; import org.hypergraphdb.handle.HGLiveHandle; import org.hypergraphdb.storage.BAtoHandle; import org.hypergraphdb.storage.BAtoString; import org.hypergraphdb.transaction.HGTransactionConfig; import org.hypergraphdb.type.HGAtomType; import org.hypergraphdb.type.HGTypeConfiguration; import org.hypergraphdb.type.HGTypeSchema; import org.hypergraphdb.type.JavaTypeFactory; import org.hypergraphdb.type.JavaTypeMapper; import org.hypergraphdb.type.JavaTypeSchema; import org.hypergraphdb.type.LinkType; import org.hypergraphdb.type.NullType; import org.hypergraphdb.type.SubsumesType; import org.hypergraphdb.type.Top; import org.hypergraphdb.util.HGUtils; /** * <p> * The <code>HGTypeSystem</code> manages atom type information for a given * {@link HyperGraph} database. Each atom stored in the database has a type * that carries some semantic information about the atom as well as implementing * the low-level storage representation of the atom's value. Types can be dynamically * added to and removed from the database - they themselves are atoms. Such atom types * are distinguished by the fact that their runtime instances implement the * {@link HGAtomType} interface. Every * database can have its own user-definable type system bootstrapped with a * set of predefined types. The type of all predefined types is called top. When * the instances of a given type are themselves types (i.e. implement the * {@link HGAtomType} interface, they are called type constructors. Such type * constructors are used to manage dynamically added types, for example for * application specific Java classes. * * <p> * This class is also responsible for creating and managing mappings from Java classes to * HyperGraphDB types. The runtime instance of each atom is of some Java class and * there is one and only one HyperGraphDB type associated with that class. Predefined * types and the mechanism by which Java class are mapped to HyperGraphDB type go hand in * hand and they are both part of the global database configuration in the form of a * {@link HGTypeConfiguration} instance. * </p> * * <p> * You can obtain the type of a given atom or the type associated with a given Java class by * calling one of the <code>getAtomType</code> methods. To obtain the handle of a type (instead * of the actual {@link HGAtomType} instance), call one of the <code>getTypeHandle</code> methods. * </p> * * <h3>Class loading</h3> * * Since this class does a lot of introspection when mapping newly seen Java classes to * HyperGraphDB types, it is worth noting how classes are looked up. The mechanism is rather * simple: the <code>ClassLoader</code> set via the <code>setClassLoader</code> is * tried if provided at all, otherwise the current thread class loader is tried, and finally * <code>this.getClass().getClassLoader()</code> is tried. * * <h3>Aliases</h3> * * <p> * An alias can be defined for a commonly used type. An alias is simply a name * that is associated with a type. The type instance can then be retrieved by * using the alias. A type may have more than one alias. Use the * <code>addAlias</code>, <code>removeAlias</code>, <code>getType(String)</code> * and <code>getHandle</code> methods for working with aliases. * </p> * * @author Borislav Iordanov */ public class HGTypeSystem { private static final String TYPE_ALIASES_DB_NAME = "hg_typesystem_type_alias"; // private static final String JAVA2HG_TYPES_DB_NAME = "hg_typesystem_java2hg_types"; private static final String JAVA_PREDEFINED_TYPES_DB_NAME = "hg_typesystem_javapredefined_types"; private static final String URI2HG_TYPES_DB_NAME = "hg_typesystem_uri2hg_types"; // static final HGPersistentHandle TOP_PERSISTENT_HANDLE = null; // // HGHandleFactory.makeHandle("a395bb09-07cd-11da-831d-8d375c1471fe"); // // static final HGPersistentHandle LINK_PERSISTENT_HANDLE = null; // // HGHandleFactory.makeHandle("822fa5aa-a376-11da-9470-a5016df901a7"); // // static final HGPersistentHandle PLAINLINK_PERSISTENT_HANDLE = null; // // HGHandleFactory.makeHandle("8c8202fb-a376-11da-9470-a5016df901a7"); // // static final HGPersistentHandle SUBSUMES_PERSISTENT_HANDLE = null; // // HGHandleFactory.makeHandle("971a1bdc-a376-11da-9470-a5016df901a7"); // // static final HGPersistentHandle NULLTYPE_PERSISTENT_HANDLE = null; // // HGHandleFactory.makeHandle("db733325-19d5-11db-8b55-23bc8177d6ec"); public static final HGAtomType top = Top.getInstance(); private HyperGraph graph = null; private HGTypeConfiguration config = null; // Useful for the many methods that work on Java Class(es) for backward compatibility. private HGTypeSchema<Class<?>> javaSchema = null; // private HGBidirectionalIndex<String, HGPersistentHandle> classToTypeDB = null; private HGBidirectionalIndex<String, HGPersistentHandle> aliases = null; private HGBidirectionalIndex<String, HGPersistentHandle> urisDB = null; private HGIndex<HGPersistentHandle, String> predefinedTypesDB = null; private HGLiveHandle topHandle; private HGLiveHandle nullTypeHandle; private HGBidirectionalIndex<String, HGPersistentHandle> getUriDB() { if (urisDB == null) { urisDB = graph.getStore().getBidirectionalIndex(URI2HG_TYPES_DB_NAME, BAtoString.getInstance(), BAtoHandle.getInstance(graph.getHandleFactory()), null, true); } return urisDB; } // private HGBidirectionalIndex<String, HGPersistentHandle> getClassToTypeDB() // { // if (classToTypeDB == null) // { // classToTypeDB = graph.getStore().getBidirectionalIndex(JAVA2HG_TYPES_DB_NAME, // BAtoString.getInstance(), // BAtoHandle.getInstance(graph.getHandleFactory()), // null, // true); // } // return classToTypeDB; // } private HGBidirectionalIndex<String, HGPersistentHandle> getAliases() { if (aliases == null) { aliases = graph.getStore().getBidirectionalIndex(TYPE_ALIASES_DB_NAME, BAtoString.getInstance(), BAtoHandle.getInstance(graph.getHandleFactory()), null, true); } return aliases; } private HGIndex<HGPersistentHandle, String> getPredefinedTypesDB() { if (predefinedTypesDB == null) { predefinedTypesDB = graph.getStore().getIndex(JAVA_PREDEFINED_TYPES_DB_NAME, BAtoHandle.getInstance(graph.getHandleFactory()), BAtoString.getInstance(), null, true); } return predefinedTypesDB; } void addPrimitiveTypeToStore(HGPersistentHandle handle) { HGPersistentHandle [] layout = new HGPersistentHandle[] { topHandle.getPersistent(), graph.getHandleFactory().nullHandle() }; graph.getStore().store(handle, layout); graph.indexByType.addEntry(topHandle.getPersistent(), handle); } void bootstrap(HGTypeConfiguration typeConfiguration) { this.config = typeConfiguration; this.javaSchema = config.getSchema("javaclass"); top.setHyperGraph(graph); topHandle = graph.cache.atomRead(graph.getHandleFactory().topTypeHandle(), top, new HGAtomAttrib()); graph.cache.freeze(topHandle); // HGAtomType plainLinktype = new PlainLinkType(); // plainLinktype.setHyperGraph(graph); // HGLiveHandle plainLinkHandle = graph.cache.atomRead(config.getHandleOf(plainLinktype.getClass()), // plainLinktype, // new HGAtomAttrib()); // classToAtomType.put(HGPlainLink.class, plainLinkHandle); // graph.cache.freeze(plainLinkHandle); HGAtomType linkType = new LinkType(); linkType.setHyperGraph(graph); HGLiveHandle linkHandle = graph.cache.atomRead(graph.getHandleFactory().linkTypeHandle(), linkType, new HGAtomAttrib()); graph.cache.freeze(linkHandle); HGAtomType subsumesType = new SubsumesType(); subsumesType.setHyperGraph(graph); HGLiveHandle subsumesHandle = graph.cache.atomRead(graph.getHandleFactory().subsumesTypeHandle(), subsumesType, new HGAtomAttrib()); graph.cache.freeze(subsumesHandle); HGAtomType nullType = new NullType(); nullType.setHyperGraph(graph); nullTypeHandle = graph.cache.atomRead(graph.getHandleFactory().nullTypeHandle(), nullType, new HGAtomAttrib()); graph.cache.freeze(nullTypeHandle); // // If we are actually creating a new database, populate with primitive types. // if (graph.getStore().getLink(topHandle.getPersistent()) == null) { addPrimitiveTypeToStore(topHandle.getPersistent()); addPrimitiveTypeToStore(linkHandle.getPersistent()); addPrimitiveTypeToStore(subsumesHandle.getPersistent()); // graph.add(new HGSubsumes(topHandle.getPersistent(), // linkHandle.getPersistent()), // subsumesHandle.getPersistent()); // graph.add(new HGSubsumes(linkHandle.getPersistent(), // plainLinkHandle.getPersistent()), // subsumesHandle.getPersistent()); // graph.add(new HGSubsumes(plainLinkHandle.getPersistent(), // subsumesHandle.getPersistent()), // subsumesHandle.getPersistent()); // //storePrimitiveTypes(typeDefResource); // for (HGPersistentHandle typeHandle : config.getHandles()) // { // Class<? extends HGAtomType> cl = config.getTypeImplementation(typeHandle); // if (cl.equals(Top.class) || // cl.equals(LinkType.class) || // cl.equals(PlainLinkType.class) || // cl.equals(SubsumesType.class)) // continue; // HGAtomType typeInstance = null; // try { typeInstance = cl.newInstance(); } // catch (Exception ex) { System.err.println("[HYPERGRAPHDB WARNING]: failed to create instance of type '" + // cl.getName() + "'"); ex.printStackTrace(System.err); } // List<Class<?>> targets = config.getMappedClasses(typeHandle); // if (targets.isEmpty()) // addPredefinedType(typeHandle, typeInstance, null); // else for (Class<?> target : targets) // addPredefinedType(typeHandle, typeInstance, target); // } } for (HGTypeSchema<?> schema : config.getSchemas()) schema.initialize(graph); // javaTypes = typeConfiguration.getJavaTypeMapper(); // javaTypes.setHyperGraph(graph); } /** * <p> * Use this method to load a set of primitive types in bulk, from a text descriptor * resource (loaded using this class's class loader). * </p> * <p> * The resource is expected to be in the following format: 1 type per line where * each line consists of two or more columns separated by the space character. * The first columns should be a canonical string representation of a UUID. The second * column should be the class name of the class implementing the type. The (optional) * subsequent columns should list the names of the classes that this type "covers". * The following is an example where the first line simply adds a predefined type * without any corresponding covered Java classes, and the second shows a type * that covers only one class: * </p> * <p> * <pre><code> * db733325-19d5-11db-8b55-23bc8177d6ec org.hypergraphdb.type.NullType * 2ec10476-d964-11db-a08c-eb6f4c8f155a org.hypergraphdb.type.AtomRefType org.hypergraphdb.atom.HGAtomRef * </code></pre> * </p> * @param resource */ public void storePrimitiveTypes(String resource) { InputStream resourceIn = getClass().getResourceAsStream(resource); if (resourceIn == null) throw new HGException("Fatal error: could not load primitive types from " + resource + ", this resource could not be found!"); BufferedReader reader = new BufferedReader(new InputStreamReader(resourceIn)); try { for (String line = reader.readLine(); line != null; line = reader.readLine()) { line = line.trim(); if (line.length() == 0) continue; if (line.startsWith("#")) continue; StringTokenizer tok = new StringTokenizer(line, " "); if (tok.countTokens() < 2) throw new HGException("Fatal error: could not load primitive types from " + resource + ", the line " + line + " is ill formed."); String pHandleStr = tok.nextToken(); String typeClassName = tok.nextToken(); Class<?> typeClass = Class.forName(typeClassName); HGAtomType type = (HGAtomType)typeClass.newInstance(); type.setHyperGraph(graph); HGPersistentHandle pHandle = graph.getHandleFactory().makeHandle(pHandleStr); if (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) { String valueClassName = tok.nextToken(); Class<?> valueClass = Class.forName(valueClassName); addPredefinedType(pHandle, type, valueClass); } } else addPredefinedType(pHandle, type, (URI)null); } } catch (IOException ex) { throw new HGException("Fatal error: could not load primitive types from " + resource + " due to an IO exception!", ex); } catch (ClassNotFoundException ex) { throw new HGException("Fatal error: could not load primitive types from " + resource + " due to a missing class from the classpath: " + ex.getMessage(), ex); } catch (Throwable t) { throw new HGException("Fatal error: could not load primitive types from " + resource, t); } } HGLiveHandle loadPredefinedType(HGPersistentHandle pHandle) { graph.getEventManager().dispatch(graph, new HGLoadPredefinedTypeEvent(pHandle)); // HGLiveHandle result = graph.cache.get(pHandle); // if (result != null) // return result; // else // { String classname = getPredefinedTypesDB().findFirst(pHandle); if (classname == null) { throw new HGException("Unable to load predefined type with handle " + pHandle + " please review the documentation about predefined types and how to hook them with the HyperGraph type system."); } try { Class<?> clazz = loadClass(classname); HGAtomType type = (HGAtomType)clazz.newInstance(); type.setHyperGraph(graph); return (HGLiveHandle)addPredefinedType(pHandle, type, (URI)null); } catch (Throwable ex) { throw new HGException("Could not create predefined type instance with " + classname + " for type " + pHandle + ": " + ex.toString(), ex); } // } } public HGTypeSchema<?> getSchema() { // TODO: what about contextual schemas? thread-bound? transaction-bound? return config.getDefaultSchema(); } public void defineTypeAtom(final HGHandle typeHandle, final URI typeUri) { final HGTypeSchema<?> schema = config.getSchema(typeUri.getScheme()); if (graph.getTransactionManager().getContext().getCurrent() != null) { schema.defineType(typeUri, typeHandle); getUriDB().addEntry(typeUri.toString(), typeHandle.getPersistent()); // HGHandle h = defineNewJavaTypeTransaction(handle, clazz); // if (h == null) // throw new HGException("Could not create HyperGraph type for class '" + clazz.getName() + "'"); // else if (!h.equals(handle)) // throw new HGException("The class '" + clazz.getName() + "' already has a HyperGraph Java:"+h); } else graph.getTransactionManager().transact(new Callable<HGHandle>() { public HGHandle call() { schema.defineType(typeUri, typeHandle); getUriDB().addEntry(typeUri.toString(), typeHandle.getPersistent()); return null; // ignored // HGHandle h = defineNewJavaTypeTransaction(handle, clazz); // if (h == null) // throw new HGException("Could not create HyperGraph type for class '" + clazz.getName() + "'"); // else if (!h.equals(handle)) // throw new HGException("The class '" + clazz.getName() + "' already has a HyperGraph Java:"+h); // else // return h; } }); } /** * <p> * Create a HyperGraph type for the specified Java class and store the type * under the passed in <code>handle</code>. * </p> * * @param handle * @param clazz * @deprecated Please use {@link #defineTypeAtom(HGHandle, URI)} instead. */ public void defineTypeAtom(final HGPersistentHandle handle, final Class<?> clazz) { defineTypeAtom(handle, config.getDefaultSchema().toTypeURI(clazz));//javaSchema.toTypeURI(clazz)); } /** * <p> * Declare that a given type is a sub-type of another type. It is not necessary to * call this method for sub-typing relationships that are automatically inferred * from a Java class hierarchy. However, custom types and type constructors can use * this method for sub-type bookkeeping, which is important for querying and indexing. * A sub-type is represented by a {@link HGSubsumes} link. * </p> * <p> * In addition, the {@link HGIndexManager} * must be informed about a sub-typing relationships in order to maintain indices * appropriately. * </p> * * @param superType The parent type. * @param subType The child type. */ public void assertSubtype(HGHandle superType, HGHandle subType) { graph.add(new HGSubsumes(superType, subType)); graph.getIndexManager().registerSubtype(superType, subType); } /** * <p>Construct the <code>HGtypeSystem</code> associated with a hypergraph.</p> * * @param graph The <code>HyperGraph</code> which the type system is bound. */ public HGTypeSystem(HyperGraph graph) { this.graph = graph; // // Initialize databases to avoid heaving to synchronize later. // this.getAliases(); this.getUriDB(); this.getPredefinedTypesDB(); // this.javaTypes = new JavaTypeFactory(); // javaTypes.setHyperGraph(graph); } /** * <p>Return the <code>HyperGraph</code> on which this type * system operates. * </p> */ public HyperGraph getHyperGraph() { return graph; } /** * <p>Return the <code>JavaTypeFactory</code> which is responsible for mapping * Java class to HyperGraph types.</p> * @deprecated */ public JavaTypeMapper getJavaTypeFactory() { if (javaSchema instanceof JavaTypeSchema) return ((JavaTypeSchema)javaSchema).getJavaTypeFactory(); JavaTypeFactory f = new JavaTypeFactory(); f.setHyperGraph(this.graph); return f; } /** * <p>Return the type of the special Java value <code>null</code>.</p> */ public HGHandle getNullType() { return nullTypeHandle; } /** * <p>Return the type of all predefined types.</p> */ public HGHandle getTop() { return topHandle; } /** * <p> * Return the HyperGraphDB type handle corresponding to the given Java class if * a type for this class was previously defined. Return <code>null</code> otherwise. * </p> */ public HGHandle getTypeHandleIfDefined(URI uri) { HGTypeSchema<?> schema = this.config.getSchema(uri.getScheme()); if (schema == null) throw new HGException("Couldn't find schema for type " + uri); return schema.findType(uri); } public HGHandle getHandleForIdentifier(URI typeId) { return getUriDB().findFirst(typeId.toString()); } public Set<URI> getIdentifiersForHandle(HGHandle typeHandle) { HGSearchResult<String> rs = this.getUriDB().findByValue(typeHandle.getPersistent()); HashSet<URI> S = new HashSet<URI>(); try { while (rs.hasNext()) try { S.add(new URI(rs.next())); } catch (Exception ex) { throw new HGException(ex); } return S; } finally { rs.close(); } } /** * <p> * Load a class with the given name the way the <code>HGTypeSystem</code> would try * to load it. First try a user-defined class loader with this instance, then try * the Thread content class loader, finally try <code>this.getClass().getClassLoader()</code> * which is usually the system class loader. * </p> * * @param classname * @return */ public Class<?> loadClass(String classname) { try { return HGUtils.loadClass(this.getHyperGraph(), classname); } catch (Exception t) { throw new HGException("Could not load class " + classname, t); } // Class<?> clazz; // ClassLoader loader = graph.getConfig().getClassLoader(); // if (loader == null) // loader = Thread.currentThread().getContextClassLoader(); // if (loader == null) // loader = this.getClass().getClassLoader(); // try // { // if(classname.startsWith("[L")) // { // classname = classname.substring(2, classname.length() - 1); //remove ending ";" // clazz = Array.newInstance(loader.loadClass(classname), 0).getClass(); // } // else // clazz = loader.loadClass(classname); // return clazz; // } // catch (Throwable t) // { // throw new HGException("Could not load class " + classname, t); // } } /** * <p>HyperGraph internal method to handle the loading of a type. A type can be * loaded in one of two ways: either through the <code>getType(HGHandle)</code> * of this class or directly by calling <code>HyperGraph.get</code>. In both cases, * the type system should be explicitly made aware that a new type has been loaded * and be given the possibility to decorate the HGAtomType instance.... * </p> * * @param type */ HGAtomType toRuntimeInstance(HGPersistentHandle handle, HGAtomType type) { return getSchema().toRuntimeType(handle, type); } /** * <p>Specify an application specific predefined type, possibly overriding a default * HyperGraph basic type. This method allows you to add base level types when the primitive types * and type constructors provided with HyperGraph are not sufficient. For instance, one may * replace the handling of simple data types such as strings and booleans, or the management * of certain structured data such as a particular Java class etc.</p> * * <p> * Any <code>HGAtomType</code> that does not have a proper representation in the HyperGraph storage, * should be added at application startup time through this method. While generally it will, * such a top-level type does not need to correspond to a Java type. If there's no corresponding * Java type, the <code>clazz</code> parameter in a call to this method should be <code>null</code>. * </p> * * <p> * Note that a HyperGraph type may map to more than one corresponding Java class. Thus, multiple * calls with the same <code>type</code> parameter, but different <code>clazz</code> parameters * can be made to create a many-to-one relationship between Java type and HyperGraph types. * </p> * * <p> * There is one special case in the mapping of HyperGraph types to Java types: the handling of Java * primitive arrays. From HyperGraph's storage perspective, all arrays are generally recorded in the same * way regardless of the type of their elements (each element is stored through its own type). * Therefore, a single HyperGraph array type would be able to handle all Java primitive arrays. * Of course, it is possible to have specific implementations for a particular <code>T[]</code> * Java types (for instance, an optimized <code>boolean[]</code>). But there is a special generic * handling of all Java built-in arrays that is specified as the HyperGraph type of the * <code>Object[]</code> class. That is, to specify the <code>HGAtomType</code> * that should be used for Java primitive array storage, use the class of <code>Object[]</code> * as the third parameter of this method. For example: * </p> * * <p><code> * typeSystem.addPredefinedType(persistentHandle, type, Class.forName("[Ljava.lang.Object;")); * </code></p> * * @param handle The persistent handle of this type. * @param type The run-time instance of the type. * @param clazz The Java class to which this type corresponds. All atoms that are instances * of this Java class will be handled through this type. This parameter may be null if the * type should not be mapped to a Java class. * @return A run-time handle for the newly added type. */ public HGHandle addPredefinedType(final HGPersistentHandle handle, final HGAtomType type, final URI typeId) { if (graph.getTransactionManager().getContext().getCurrent() != null) return addPredefinedTypeTransaction(handle, type, typeId); else return graph.getTransactionManager().transact(new Callable<HGHandle>() { public HGHandle call() { return addPredefinedTypeTransaction(handle, type, typeId); } }); } /** * @deprecated Use {@link #addPredefinedType(HGPersistentHandle, HGAtomType, URI) instead; */ public HGHandle addPredefinedType(final HGPersistentHandle handle, final HGAtomType type, final Class<?> clazz) { return addPredefinedType(handle, type, config.getDefaultSchema().toTypeURI(clazz)); } private HGHandle addPredefinedTypeTransaction(HGPersistentHandle handle, HGAtomType type, final URI typeId) { type.setHyperGraph(graph); // // Make sure the type is in storage... // if (graph.getStore().getLink(handle) == null) { addPrimitiveTypeToStore(handle); graph.add(new HGSubsumes(getTop(), handle)); try { // // If the type class has a default constructor, we add it to the list // automatically instantiable predefined types. Otherwise, we can't // instantiate it, and it is somebody else's business to do so. // if (type.getClass().getConstructor(new Class[0]) != null) getPredefinedTypesDB().addEntry(handle, type.getClass().getName()); } catch (NoSuchMethodException e) { /* TODO Log this some day when we have logging. */ //hilpold 2011.10.11 System.out.println("No default constructor for: " + type.getClass()); } } HGLiveHandle typeHandle = graph.cache.get(handle); if (typeHandle == null) typeHandle = graph.cache.atomRead(handle, type, new HGAtomAttrib()); else typeHandle = graph.cache.atomRefresh(typeHandle, type, false); graph.cache.freeze(typeHandle); if (typeId != null) { // HGTypeSchema<?> schema = config.getSchema(typeId.getScheme()); // if (schema == null) // throw new NullPointerException("No schema installed with name " + typeId.getScheme()); if (getUriDB().findFirst(typeId.toString()) != null) getUriDB().removeAllEntries(typeId.toString()); getUriDB().addEntry(typeId.toString(), handle); // TODO: is this important? do we really need to state that the Java class of the predefined // type has type top? Perhaps it's even wrong...that class may well be a primitive type in some // type schema, yet be stored as a Java bean or something else in another type schema. // classToAtomType.put(type.getClass(), classToAtomType.get(Top.class)); // This shouldn't be needed, because it's just a caching thing, now specific to the JavaTypeSchema... // if (clazz != null) // { // classToAtomType.put(clazz, typeHandle); // } } return typeHandle; } /** * <p> * Return the Java class that corresponds to the given HyperGraphDB type handle. The * result is the class of the run-time instances constructed with the type identified * by <code>typeHandle</code>. * </p> * * @param typeHandle The <code>HGHandle</code> identifying the type whose runtime Java * class is required. * @return The Java class corresponding to <code>typeHandle</code> or <code>null</code> * if there's no such correspondence. */ public Class<?> getClassForType(HGHandle typeHandle) { Set<URI> ids = getIdentifiersForHandle(typeHandle); for (URI u : ids) if (u.getScheme().equals(javaSchema.getName())) return javaSchema.getTypeDescriptor(u); return null; // String classname = getClassNameForType(typeHandle); // return classname != null ? loadClass(classname) : null; } /** * <p> * Specifically map a HyperGraphDB {@link HGAtomType}, already stored as an * atom with handle <code>typeHandle</code> to the Java class <code>clazz</code>. * Any previous type association with <code>clazz</code> will be removed. * </p> * @param typeHandle can't be null * @param clazz can't be null */ public void setTypeForClass(final HGHandle typeHandle, final Class<?> clazz) { graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() { public HGHandle call() { URI uri = JavaTypeSchema.classToURI(clazz); javaSchema.removeType(uri); getUriDB().removeAllEntries(uri.toString()); getUriDB().addEntry(uri.toString(), typeHandle.getPersistent()); return null; } }); } /** * <p> * Return the Java classname that corresponds to the given HyperGraphDB type handle. The * result is the name of the class class of the run-time instances constructed with * the type identified * by <code>typeHandle</code>. * </p> * * @param typeHandle The <code>HGHandle</code> identifying the type whose runtime Java * class is required. * @return The Java class name corresponding to <code>typeHandle</code> or <code>null</code> * if there's no such correspondence. */ public String getClassNameForType(HGHandle typeHandle) { Class<?> cl = null; Set<URI> uris = getIdentifiersForHandle(typeHandle); for (URI u : uris) if (u.getScheme().equals(javaSchema.getName())) { cl = javaSchema.getTypeDescriptor(u); break; } return cl == null ? null : cl.getName(); //return getClassToTypeDB().findFirstByValue(graph.getPersistentHandle(typeHandle)); } /** * <p>Return the <code>HGAtomType</code> by its <code>HGHandle</code>.</p> * * @param handle The handle of the atom type itself. Note that to retrieve the type * of an atom, you must use the <code>getAtomType(Object)</code> method. */ public HGAtomType getType(HGHandle handle) { return (HGAtomType)graph.get(handle); } /** * <p>Return the <code>HGAtomType</code> corresponding to the given alias.</p> * * @param alias The alias. * @return The type instance or <code>null</code> if this alias has not * been defined. */ public HGAtomType getType(String alias) { HGHandle handle = getTypeHandle(alias); if (handle != null) return getType(handle); else return null; } /** * <p> * Return the <code>HGAtomType</code> corresponding to the passed in * Java class. This is equivalent to <code>(HGAtomType)HyperGraph.get(getTypeHandle(clazz))</code>. * </p> * */ @SuppressWarnings("unchecked") public <T extends HGAtomType> T getAtomType(Class<?> clazz) { return (T)graph.get(getTypeHandle(clazz)); } /** * <p> * Return the default <code>HyperGraph</code> type of the given atom object. Note * the <em>default</em> here means that the type returned is the one that would * be automatically assigned to instances of the concrete type of <code>object</code>. * That is, calling this method is equivalent to calling <code>getAtomType(object.getClass())</code>. * If <code>object</code> is the run-time instance of an actual HyperGraph atom that * was explicitely assigned a type, the latter may be different than the default type. * </p> */ public HGAtomType getAtomType(Object object) { return getType(getTypeHandle(object)); } /** * <p>Return the type instance of a given atom.</p> * * @param handle The atom whose type is desired. * @return The type of the atom. */ public HGAtomType getAtomType(HGHandle handle) { return getType(getTypeHandle(handle)); } /** * <p>Return <code>true</code> if there is a HyperGraph type corresponding to the given * class and <code>false</code> otherwise.</p> */ public boolean hasType(Class<?> clazz) { return this.getTypeHandleIfDefined(clazz) != null; } /** * Return the handle of the type associated with the specified class. The name of * the class is first converted to a type URI. See {@link #getHandleForIdentifier(URI)}. */ public HGHandle getTypeHandleIfDefined(Class<?> clazz) { if (javaSchema instanceof JavaTypeSchema) return ((JavaTypeSchema)javaSchema).findType(clazz); else return javaSchema.findType(javaSchema.toTypeURI(clazz)); } /** * <p>Return the <code>HGHandle</code> of the HyperGraph type representing a given * Java class. If no type has been associated yet with that particular <code>Class</code>, a * new one will be created using the currently active <code>JavaTypeFactory</code>.</p> * * @param clazz The <code>Class</code> instance of the Java class. Cannot be <code>null</code> * @return The <code>HGHandle</code> for that class. If the Java class hasn't been previously * mapped to a HyperGraph atom type, a new HyperGraph type will be created and the new handle * will be returned. */ public HGHandle getTypeHandle(final Class<?> clazz) { HGHandle h = graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() { public HGHandle call() { return getTypeHandleIfDefined(clazz); } }, HGTransactionConfig.READONLY); if (h != null) return h; return graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() { public HGHandle call() { HGHandle h = getTypeHandleIfDefined(clazz); return h != null ? h : createNewType(config.getDefaultSchema().toTypeURI(clazz)); } }); } public HGHandle getTypeHandle(final URI typeIdentifier) { HGHandle h = graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() { public HGHandle call() { return getTypeHandleIfDefined(typeIdentifier); } }, HGTransactionConfig.READONLY); if (h != null) return h; return graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() { public HGHandle call() { HGHandle h = getTypeHandleIfDefined(typeIdentifier); return h != null ? h : createNewType(typeIdentifier); } }); } private HGHandle createNewType(final URI typeIdentifier) { HGPersistentHandle typeHandle = graph.getHandleFactory().makeHandle(); defineTypeAtom(typeHandle, typeIdentifier); return typeHandle; } /** * <p>Return the handle of the type corresponding to the given alias.</p> * * @param alias The alias. * @return The type handle or <code>null</code> if this alias has not * been defined. */ public HGHandle getTypeHandle(String alias) { if (alias == null) return null; else { HGPersistentHandle handle = getAliases().findFirst(alias); if (handle != null) return graph.refreshHandle(handle); else return null; } } /** * <p> * Return the (handle of the) type of the given atom. * </p> * * @param atomHandle The handle of the atom whose type is desired. * @deprecated Please call {@link #HyperGraph.getType(HGHandle)} instead. This method * <strong>will</strong> be removed in future versions. */ public HGHandle getTypeHandle(HGHandle atomHandle) { HGPersistentHandle [] layout = graph.getStore().getLink(graph.getPersistentHandle(atomHandle)); if (layout == null || layout.length == 0) throw new HGException("Could not retrieve atom with handle " + graph.getPersistentHandle(atomHandle) + " from the HyperGraph store."); HGHandle live = graph.cache.get(layout[0]); return live == null ? layout[0] : live; } /** * <p> * Return the HyperGraph type handle of the given Java object. * </p> * * <p> * This method will first try to find the HyperGraph <code>HGHandle</code> of the object * and retrieve the type based on that handle. If not, it will retrieve the default * HyperGraph type of the concrete Java class of the object (i.e. of x.getClass()). * </p> * * @param x The object whose HyperGraph type is desired. Cannot be <code>null</code>. * @return The <code>HGHandle</code> of the HyperGraph type for that object. */ public HGHandle getTypeHandle(Object x) { if (x == null) throw new NullPointerException( "HGTypeSystem.getAtomType(Object) invoked with a null object -- and 'null' has no type."); HGHandle atom = graph.getHandle(x); if (atom != null) return getTypeHandle(atom); else { return getTypeHandle(config.getDefaultSchema().toTypeURI(x)); } } /** * <p> * Add a new alias for a given type. * </p> * * @param typeHandle The <code>HGPersistentHandle</code> of the type. Note * that the method doesn't check whether this is in fact a type handle. * @param alias A non-null alias name. If this name is already used to alias * another type, an exception is thrown. */ public void addAlias(final HGHandle typeHandle, final String alias) { if (graph.getTransactionManager().getContext().getCurrent() != null) addAliasTransaction(typeHandle, alias); else graph.getTransactionManager().transact(new Callable<Object>() { public Object call() { addAliasTransaction(typeHandle, alias); return null; } }); } private void addAliasTransaction(HGHandle typeHandle, String alias) { HGBidirectionalIndex<String, HGPersistentHandle> aliases = getAliases(); synchronized (aliases) { HGPersistentHandle handle = aliases.findFirst(alias); if (handle == null) aliases.addEntry(alias, graph.getPersistentHandle(typeHandle)); else throw new HGException("Alias '" + alias + "' already defined."); } } /** * <p> * Retrieve all the aliases of a given type. * </p> * * @param typeHandle The handle of the type whose aliases are desired. * @return A regular <code>HGSearchResult</code> containing the aliases. Make * sure to close the result set as all other result sets in HyperGraphDB. */ public Set<String> findAliases(HGHandle typeHandle) { Set<String> result = new HashSet<String>(); HGSearchResult<String> rs = getAliases().findByValue(graph.getPersistentHandle(typeHandle)); try { while (rs.hasNext()) result.add(rs.next()); return result; } finally { HGUtils.closeNoException(rs); } } /** * <p>Return any metadata associated with the given type or <code>null</code>.</p> */ public HGTypeStructuralInfo getTypeMetaData(HGHandle typeHandle) { HGTypeStructuralInfo typeStruct = hg.getOne(graph, hg.and(hg.type(HGTypeStructuralInfo.class), hg.eq("typeHandle", typeHandle))); return typeStruct; } /** * <p> * Remove a type alias. If the alias hasn't been previously * defined, nothing is done. * </p> * * @param alias The alias to remove. Cannot by <code>null</code>. * @throws NullPointerException if <code>alias</code> is null. */ public void removeAlias(final String alias) { if (graph.getTransactionManager().getContext().getCurrent() != null) removeAliasTransaction(alias); else graph.getTransactionManager().transact(new Callable<Object>() { public Object call() { removeAliasTransaction(alias); return null; } }); } private void removeAliasTransaction(String alias) { HGBidirectionalIndex<String, HGPersistentHandle> aliases = getAliases(); synchronized (aliases) { HGPersistentHandle handle = aliases.findFirst(alias); if (handle != null) aliases.removeEntry(alias, handle); } } /** * <p>Permanently delete the type referred to by the passed in persistent handle.</p> * * <p>Should be called only by HyperGraph when removing an atom that is also a type.</p> * * @param typeHandle * @param type the type instance */ void remove(final HGPersistentHandle typeHandle, final HGAtomType type) { // // Remove subsumes relationships. // List<HGHandle> subsumesLinks = hg.findAll(graph, hg.and(hg.incident(typeHandle), hg.type(HGSubsumes.class))); for (HGHandle h : subsumesLinks) graph.remove(h); // // Remove HGTypeStructuralInfo attached to this type. // HGHandle typeStruct = hg.findOne(graph, hg.and(hg.type(HGTypeStructuralInfo.class), hg.eq("typeHandle", typeHandle))); if (typeStruct != null) graph.remove(typeStruct); // // Remove all aliases // HGBidirectionalIndex<String, HGPersistentHandle> aliases = getAliases(); HGSearchResult<String> rs = aliases.findByValue(typeHandle); try { while (rs.hasNext()) { // TODO: maybe a problem here if we are removing while iterating... aliases.removeEntry((String)rs.next(), typeHandle); } } finally { rs.close(); } // // Remove from HG type <-> Java class mappings // try { HGBidirectionalIndex<String, HGPersistentHandle> idx = getUriDB(); Set<URI> uris = this.getIdentifiersForHandle(typeHandle); for (URI u : uris) { idx.removeEntry(u.toString(), typeHandle); HGTypeSchema<?> schema = config.getSchema(u.getScheme()); if (schema != null) schema.removeType(u); } // rs = idx.findByValue(typeHandle); // while (rs.hasNext()) // { // String classname = rs.next(); // idx.removeEntry(classname, typeHandle); // // Remove from class->atom cache if there. // Class<?> clazz = loadClass(classname); // classToAtomType.remove(clazz); // } } catch (Throwable t) { throw new HGException(t); } finally { if (rs != null) try { rs.close(); } catch (Throwable _) { } } } }