Package com.tinkerpop.blueprints.impls.orient

Source Code of com.tinkerpop.blueprints.impls.orient.OrientBaseGraph

package com.tinkerpop.blueprints.impls.orient;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.configuration.Configuration;

import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.io.OFileUtils;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OCallable;
import com.orientechnologies.orient.core.command.OCommandRequest;
import com.orientechnologies.orient.core.command.traverse.OTraverse;
import com.orientechnologies.orient.core.config.OStorageEntryConfiguration;
import com.orientechnologies.orient.core.db.ODatabase.ATTRIBUTES;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.index.OPropertyIndexDefinition;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.type.tree.provider.OMVRBTreeRIDProvider;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Index;
import com.tinkerpop.blueprints.IndexableGraph;
import com.tinkerpop.blueprints.KeyIndexableGraph;
import com.tinkerpop.blueprints.MetaGraph;
import com.tinkerpop.blueprints.Parameter;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.ExceptionFactory;
import com.tinkerpop.blueprints.util.StringFactory;

/**
* A Blueprints implementation of the graph database OrientDB
* (http://www.orientechnologies.com)
*
* @author Luca Garulli (http://www.orientechnologies.com)
*/
public abstract class OrientBaseGraph implements IndexableGraph,
    MetaGraph<ODatabaseDocumentTx>, KeyIndexableGraph {
  public static final String CONNECTION_OUT = "out";
  public static final String CONNECTION_IN = "in";
  public static final String CLASS_PREFIX = "class:";
  public static final String CLUSTER_PREFIX = "cluster:";

  public enum THREAD_MODE {
    MANUAL, AUTOSET_IFNULL, ALWAYS_AUTOSET
  };

  protected final static String ADMIN = "admin";
  protected boolean useLightweightEdges = true;
  protected boolean useClassForEdgeLabel = true;
  protected boolean useClassForVertexLabel = true;
  protected boolean keepInMemoryReferences = false;
  protected boolean useVertexFieldsForEdgeLabels = true;
  protected boolean saveOriginalIds = false;
  protected boolean standardElementConstraints = true;
  protected boolean warnOnForceClosingTx = true;
  protected THREAD_MODE threadMode = THREAD_MODE.AUTOSET_IFNULL;

  private String url;
  private String username;
  private String password;

  private static final ThreadLocal<OrientGraphContext> threadContext = new ThreadLocal<OrientGraphContext>();
  private static final List<OrientGraphContext> contexts = new ArrayList<OrientGraphContext>();

  /**
   * Constructs a new object using an existent OGraphDatabase instance.
   *
   * @param iDatabase
   *            Underlying OGraphDatabase object to attach
   */
  public OrientBaseGraph(final ODatabaseDocumentTx iDatabase) {
    reuse(iDatabase);
    readDatabaseConfiguration();
  }

  public OrientBaseGraph(final String url) {
    this(url, ADMIN, ADMIN);
  }

  public OrientBaseGraph(final String url, final String username,
      final String password) {
    this.url = OFileUtils.getPath(url);
    this.username = username;
    this.password = password;
    this.openOrCreate();
    readDatabaseConfiguration();
  }

  /**
   * Builds a OrientGraph instance passing a configuration. Supported
   * configuration settings are:
   * <table>
   * <tr>
   * <td><b>Name</b></td>
   * <td><b>Description</b></td>
   * <td><b>Default value</b></td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.url</td>
   * <td>Database URL</td>
   * <td>-</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.username</td>
   * <td>User name</td>
   * <td>admin</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.password</td>
   * <td>User password</td>
   * <td>admin</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.saveOriginalIds</td>
   * <td>Saves the original element IDs by using the property origId. This could
   * be useful on import of graph to preserve original ids</td>
   * <td>false</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.keepInMemoryReferences</td>
   * <td>Avoid to keep records in memory but only RIDs</td>
   * <td>false</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.useCustomClassesForEdges</td>
   * <td>Use Edge's label as OrientDB class. If doesn't exist create it under
   * the hood</td>
   * <td>true</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.useCustomClassesForVertex</td>
   * <td>Use Vertex's label as OrientDB class. If doesn't exist create it
   * under the hood</td>
   * <td>true</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.useVertexFieldsForEdgeLabels</td>
   * <td>Store the edge relationships in vertex by using the Edge's class.
   * This allow to use multiple fields and make faster traversal by edge's
   * label (class)</td>
   * <td>true</td>
   * </tr>
   * <tr>
   * <td>blueprints.orientdb.lightweightEdges</td>
   * <td>Uses lightweight edges. This avoid to create a physical document per
   * edge. Documents are created only when they have properties</td>
   * <td>true</td>
   * </tr>
   * </table>
   *
   * @param configuration
   */
  public OrientBaseGraph(final Configuration configuration) {
    this(configuration.getString("blueprints.orientdb.url", null),
        configuration.getString("blueprints.orientdb.username", null),
        configuration.getString("blueprints.orientdb.password", null));

    final Boolean saveOriginalIds = configuration.getBoolean(
        "blueprints.orientdb.saveOriginalIds", null);
    if (saveOriginalIds != null)
      setSaveOriginalIds(saveOriginalIds);

    final Boolean keepInMemoryReferences = configuration.getBoolean(
        "blueprints.orientdb.keepInMemoryReferences", null);
    if (keepInMemoryReferences != null)
      setKeepInMemoryReferences(keepInMemoryReferences);

    final Boolean useCustomClassesForEdges = configuration.getBoolean(
        "blueprints.orientdb.useCustomClassesForEdges", null);
    if (useCustomClassesForEdges != null)
      setUseClassForEdgeLabel(useCustomClassesForEdges);

    final Boolean useCustomClassesForVertex = configuration.getBoolean(
        "blueprints.orientdb.useCustomClassesForVertex", null);
    if (useCustomClassesForVertex != null)
      setUseClassForVertexLabel(useCustomClassesForVertex);

    final Boolean useVertexFieldsForEdgeLabels = configuration.getBoolean(
        "blueprints.orientdb.useVertexFieldsForEdgeLabels", null);
    if (useVertexFieldsForEdgeLabels != null)
      setUseVertexFieldsForEdgeLabels(useVertexFieldsForEdgeLabels);

    final Boolean lightweightEdges = configuration.getBoolean(
        "blueprints.orientdb.lightweightEdges", null);
    if (lightweightEdges != null)
      setUseLightweightEdges(lightweightEdges);
  }

  @SuppressWarnings("unchecked")
  private void readDatabaseConfiguration() {
    final List<OStorageEntryConfiguration> custom = (List<OStorageEntryConfiguration>) getRawGraph()
        .get(ATTRIBUTES.CUSTOM);
    for (OStorageEntryConfiguration c : custom) {
      if (c.name.equals("useLightweightEdges"))
        setUseLightweightEdges(Boolean.parseBoolean(c.value));
      else if (c.name.equals("useClassForEdgeLabel"))
        setUseClassForEdgeLabel(Boolean.parseBoolean(c.value));
      else if (c.name.equals("useClassForVertexLabel"))
        setUseClassForVertexLabel(Boolean.parseBoolean(c.value));
      else if (c.name.equals("useVertexFieldsForEdgeLabels"))
        setUseVertexFieldsForEdgeLabels(Boolean.parseBoolean(c.value));
    }
  }

  /**
   * Drops the database
   */
  public void drop() {
    getRawGraph().drop();
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  public <T extends Element> Index<T> createIndex(final String indexName,
      final Class<T> indexClass, final Parameter... indexParameters) {
    final OrientGraphContext context = getContext(true);

    return executeOutsideTx(new OCallable<Index<T>, OrientBaseGraph>() {
      public Index<T> call(final OrientBaseGraph g) {
        synchronized (contexts) {
          if (context.manualIndices.containsKey(indexName))
            throw ExceptionFactory.indexAlreadyExists(indexName);

          final OrientIndex<? extends OrientElement> index = new OrientIndex<OrientElement>(
              g, indexName, indexClass, null);

          // ADD THE INDEX IN ALL CURRENT CONTEXTS
          for (OrientGraphContext ctx : contexts)
            ctx.manualIndices.put(index.getIndexName(), index);
          context.manualIndices.put(index.getIndexName(), index);

          // SAVE THE CONFIGURATION INTO THE GLOBAL CONFIG
          saveIndexConfiguration();

          return (Index<T>) index;
        }
      };
    }, "create index '", indexName, "'");
  }

  @SuppressWarnings("unchecked")
  public <T extends Element> Index<T> getIndex(final String indexName,
      final Class<T> indexClass) {
    final OrientGraphContext context = getContext(true);
    Index<? extends Element> index = context.manualIndices.get(indexName);
    if (null == index) {
      return null;
    }

    if (indexClass.isAssignableFrom(index.getIndexClass()))
      return (Index<T>) index;
    else
      throw ExceptionFactory.indexDoesNotSupportClass(indexName,
          indexClass);
  }

  public Iterable<Index<? extends Element>> getIndices() {
    final OrientGraphContext context = getContext(true);
    final List<Index<? extends Element>> list = new ArrayList<Index<? extends Element>>();
    for (Index<?> index : context.manualIndices.values()) {
      list.add(index);
    }
    return list;
  }

  protected Iterable<OrientIndex<? extends OrientElement>> getManualIndices() {
    return getContext(true).manualIndices.values();
  }

  public void dropIndex(final String indexName) {
    executeOutsideTx(new OCallable<Object, OrientBaseGraph>() {
      @Override
      public Object call(OrientBaseGraph g) {
        try {
                    String recordMapIndexName = null;
          synchronized (contexts) {
            for (OrientGraphContext ctx : contexts) {
              OrientIndex<?> index = ctx.manualIndices.remove(indexName);
                            if (recordMapIndexName == null && index != null)
                                recordMapIndexName = index.getUnderlying().getConfiguration().field(OrientIndex.CONFIG_RECORD_MAP_NAME);
            }
          }

          getRawGraph().getMetadata().getIndexManager()
              .dropIndex(indexName);
                    if (recordMapIndexName != null)
                        getRawGraph().getMetadata().getIndexManager()
                                .dropIndex(recordMapIndexName);

          saveIndexConfiguration();
          return null;
        } catch (Exception e) {
          g.rollback();
          throw new RuntimeException(e.getMessage(), e);
        }
      }
    }, "drop index '", indexName, "'");
  }

  public OrientVertex addVertex(final Object id) {
    return addVertex(id, (Object[]) null);
  }

  public OrientVertex addVertex(final Object id, final Object... prop) {
    String className = null;
    String clusterName = null;
    Object[] fields = null;

    if (id != null) {
      if (id instanceof String) {
        // PARSE ARGUMENTS
        final String[] args = ((String) id).split(",");
        for (String s : args) {
          if (s.startsWith(CLASS_PREFIX))
            // GET THE CLASS NAME
            className = s.substring(CLASS_PREFIX.length());
          else if (s.startsWith(CLUSTER_PREFIX))
            // GET THE CLASS NAME
            clusterName = s.substring(CLUSTER_PREFIX.length());
        }
      }

      if (saveOriginalIds)
        // SAVE THE ID TOO
        fields = new Object[] {
            OrientElement.DEF_ORIGINAL_ID_FIELDNAME, id };
    }

    this.autoStartTransaction();

    final OrientVertex vertex = new OrientVertex(this, className, fields);
    vertex.setProperties(prop);

    // SAVE IT
    if (clusterName != null)
      vertex.save(clusterName);
    else
      vertex.save();
    return vertex;
  }

  public OrientVertex addVertex(final String iClassName,
      final String iClusterName) {
    this.autoStartTransaction();

    final OrientVertex vertex = new OrientVertex(this, iClassName);

    // SAVE IT
    if (iClusterName != null)
      vertex.save(iClusterName);
    else
      vertex.save();
    return vertex;
  }

  /**
   * Creates a temporary vertex. The vertex is not saved and the transaction
   * is not started.
   *
   * @param iClassName
   *            Vertex's class name
   * @param prop
   *            Varargs of properties to set
   * @return
   */
  public OrientVertex addTemporaryVertex(final String iClassName,
      final Object... prop) {
    this.autoStartTransaction();

    final OrientVertex vertex = new OrientVertex(this, iClassName);
    vertex.setProperties(prop);
    return vertex;
  }

  public OrientEdge addEdge(final Object id, final Vertex outVertex,
      final Vertex inVertex, final String label) {
    String className = null;
    if (id != null && id instanceof String
        && id.toString().startsWith(CLASS_PREFIX))
      // GET THE CLASS NAME
      className = id.toString().substring(CLASS_PREFIX.length());

    // SAVE THE ID TOO?
    final Object[] fields = saveOriginalIds && id != null ? new Object[] {
        OrientElement.DEF_ORIGINAL_ID_FIELDNAME, id } : null;

    return ((OrientVertex) outVertex).addEdge(label,
        (OrientVertex) inVertex, className, null, fields);
  }

  public OrientVertex getVertex(final Object id) {
    if (null == id)
      throw ExceptionFactory.vertexIdCanNotBeNull();

    if (id instanceof OrientVertex)
      return (OrientVertex) id;
    else if (id instanceof ODocument)
      return new OrientVertex(this, (OIdentifiable) id);

    setCurrentGraphInThreadLocal();

    ORID rid;
    if (id instanceof OIdentifiable)
      rid = ((OIdentifiable) id).getIdentity();
    else {
      try {
        rid = new ORecordId(id.toString());
      } catch (IllegalArgumentException iae) {
        // orientdb throws IllegalArgumentException: Argument 'xxxx' is
        // not a RecordId in form of string. Format must be:
        // <cluster-id>:<cluster-position>
        return null;
      }
    }

    if (!rid.isValid())
      return null;

    final ODocument doc = rid.getRecord();
    if (doc == null)
      return null;

    return new OrientVertex(this, doc);
  }

  public void removeVertex(final Vertex vertex) {
    vertex.remove();
  }

  public Iterable<Vertex> getVertices() {
    return getVerticesOfClass(OrientVertex.CLASS_NAME, true);
  }

  public Iterable<Vertex> getVertices(final boolean iPolymorphic) {
    return getVerticesOfClass(OrientVertex.CLASS_NAME, iPolymorphic);
  }

  public Iterable<Vertex> getVerticesOfClass(final String iClassName) {
    return getVerticesOfClass(iClassName, true);
  }

  public Iterable<Vertex> getVerticesOfClass(final String iClassName,
      final boolean iPolymorphic) {
    getContext(true);
    return new OrientElementScanIterable<Vertex>(this, iClassName,
        iPolymorphic);
  }

  public Iterable<Vertex> getVertices(final String iKey, Object iValue) {
    if (iKey.equals("@class"))
      return getVerticesOfClass(iValue.toString());

    String indexName;
    final String key;
    int pos = iKey.indexOf('.');
    if (pos > -1) {
      indexName = iKey;

      final String className = iKey.substring(0, pos);
      key = iKey.substring(iKey.indexOf('.') + 1);

      final OClass clazz = getContext(true).rawGraph.getMetadata()
          .getSchema().getClass(className);

      final Collection<? extends OIndex<?>> indexes = clazz.getIndexes();
      for (OIndex<?> index : indexes) {
        final String oInName = index.getName();
        final int point = oInName.indexOf(".");
        final String okey = oInName.substring(point + 1);
        if (okey.equals(key)) {
          indexName = oInName;
          break;
        }
      }

    } else {
      indexName = OrientVertex.CLASS_NAME + "." + iKey;
      key = iKey;
    }

    final OIndex<?> idx = getContext(true).rawGraph.getMetadata()
        .getIndexManager().getIndex(indexName);
    if (idx != null) {
      iValue = convertKey(idx, iValue);

      Object indexValue = idx.get(iValue);
      if (indexValue != null && !(indexValue instanceof Iterable<?>))
        indexValue = Arrays.asList(indexValue);

      return new OrientElementIterable<Vertex>(this,
          (Iterable<?>) indexValue);
    }

    // NO INDEX: EXECUTE A QUERY
    return query().has(key, iValue).vertices();
  }

  public Iterable<Edge> getEdges() {
    return getEdgesOfClass(OrientEdge.CLASS_NAME, true);
  }

  public Iterable<Edge> getEdges(final boolean iPolymorphic) {
    return getEdgesOfClass(OrientEdge.CLASS_NAME, iPolymorphic);
  }

  public Iterable<Edge> getEdgesOfClass(final String iClassName) {
    return getEdgesOfClass(iClassName, true);
  }

  public Iterable<Edge> getEdgesOfClass(final String iClassName,
      final boolean iPolymorphic) {
    getContext(true);
    return new OrientElementScanIterable<Edge>(this, iClassName,
        iPolymorphic);
  }

  public Iterable<Edge> getEdges(final String iKey, Object iValue) {
    if (iKey.equals("@class"))
      return getEdgesOfClass(iValue.toString());

    final String indexName;
    final String key;
    int pos = iKey.indexOf('.');
    if (pos > -1) {
      indexName = iKey;
      key = iKey.substring(iKey.indexOf('.') + 1);
    } else {
      indexName = OrientEdge.CLASS_NAME + "." + iKey;
      key = iKey;
    }

    final OIndex<?> idx = getContext(true).rawGraph.getMetadata()
        .getIndexManager().getIndex(indexName);
    if (idx != null) {
      iValue = convertKey(idx, iValue);

      Object indexValue = idx.get(iValue);
      if (indexValue != null && !(indexValue instanceof Iterable<?>))
        indexValue = Arrays.asList(indexValue);

      return new OrientElementIterable<Edge>(this,
          (Iterable<?>) indexValue);
    }

    // NO INDEX: EXECUTE A QUERY
    return query().has(key, iValue).edges();
  }

  public OrientEdge getEdge(final Object id) {
    if (null == id)
      throw ExceptionFactory.edgeIdCanNotBeNull();

    if (id instanceof OrientEdge)
      return (OrientEdge) id;
    else if (id instanceof ODocument)
      return new OrientEdge(this, (OIdentifiable) id);

    final OIdentifiable rec;
    if (id instanceof OIdentifiable)
      rec = (OIdentifiable) id;
    else {
      final String str = id.toString();

      int pos = str.indexOf("->");

      if (pos > -1) {
        // DUMMY EDGE: CREATE IT IN MEMORY
        final String from = str.substring(0, pos);
        final String to = str.substring(pos + 2);
        return new OrientEdge(this, new ORecordId(from), new ORecordId(
            to));
      }

      try {
        rec = new ORecordId(str);
      } catch (IllegalArgumentException iae) {
        // orientdb throws IllegalArgumentException: Argument 'xxxx' is
        // not a RecordId in form of string. Format must be:
        // [#]<cluster-id>:<cluster-position>
        return null;
      }
    }

    final ODocument doc = rec.getRecord();
    if (doc == null)
      return null;

    return new OrientEdge(this, rec);
  }

  public void removeEdge(final Edge edge) {
    edge.remove();
  }

  /**
   * Reuses the underlying database avoiding to create and open it every time.
   *
   * @param iDatabase
   *            Underlying OGraphDatabase object
   */
  public OrientBaseGraph reuse(final ODatabaseDocumentTx iDatabase) {
    this.url = iDatabase.getURL();
    this.username = iDatabase.getUser() != null ? iDatabase.getUser()
        .getName() : null;
    synchronized (this) {
      OrientGraphContext context = threadContext.get();
      if (context == null
          || !context.rawGraph.getName().equals(iDatabase.getName())) {
        removeContext();
        context = new OrientGraphContext();
        context.rawGraph = iDatabase;
        checkForGraphSchema(iDatabase);
        threadContext.set(context);
      }
    }
    return this;
  }

  protected void checkForGraphSchema(final ODatabaseDocumentTx iDatabase) {
    final OSchema schema = iDatabase.getMetadata().getSchema();

    schema.getOrCreateClass(OMVRBTreeRIDProvider.PERSISTENT_CLASS_NAME);

    final OClass vertexBaseClass = schema.getClass(OrientVertex.CLASS_NAME);
    final OClass edgeBaseClass = schema.getClass(OrientEdge.CLASS_NAME);

    if (vertexBaseClass == null)
      // CREATE THE META MODEL USING THE ORIENT SCHEMA
      schema.createClass(OrientVertex.CLASS_NAME).setOverSize(2);

    if (edgeBaseClass == null)
      schema.createClass(OrientEdge.CLASS_NAME);

    // @COMPATIBILITY < 1.4.0:
    boolean warn = false;
    final String MSG_SUFFIX = ". Probably you are using a database created with a previous version of OrientDB. Export in graphml format and reimport it";

    if (vertexBaseClass != null) {
      if (!vertexBaseClass.getName().equals(OrientVertex.CLASS_NAME)) {
        OLogManager.instance().warn(this,
            "Found Vertex class %s" + MSG_SUFFIX,
            vertexBaseClass.getName());
        warn = true;
      }

      if (vertexBaseClass.existsProperty(CONNECTION_OUT)
          || vertexBaseClass.existsProperty(CONNECTION_IN)) {
        OLogManager.instance().warn(this,
            "Found property in/out against V");
        warn = true;
      }
    }

    if (edgeBaseClass != null) {
      if (!warn && !edgeBaseClass.getName().equals(OrientEdge.CLASS_NAME)) {
        OLogManager.instance().warn(this,
            "Found Edge class %s" + MSG_SUFFIX,
            edgeBaseClass.getName());
        warn = true;
      }

      if (edgeBaseClass.existsProperty(CONNECTION_OUT)
          || edgeBaseClass.existsProperty(CONNECTION_IN)) {
        OLogManager.instance().warn(this,
            "Found property in/out against E");
        warn = true;
      }
    }
  }

  public boolean isClosed() {
    return getRawGraph().isClosed();
  }

  public void shutdown() {
    removeContext();

    url = null;
    username = null;
    password = null;
  }

  public String toString() {
    return StringFactory.graphString(this, getRawGraph().getURL());
  }

  public ODatabaseDocumentTx getRawGraph() {
    return getContext(true).rawGraph;
  }

  public void commit() {
  }

  public void rollback() {
  }

  public OClass getVertexBaseType() {
    return getRawGraph().getMetadata().getSchema()
        .getClass(OrientVertex.CLASS_NAME);
  }

  public final OClass getVertexType(final String iTypeName) {
    final OClass cls = getRawGraph().getMetadata().getSchema()
        .getClass(iTypeName);
    if (cls != null)
      checkVertexType(cls);
    return cls;
  }

  public OClass createVertexType(final String iClassName) {
    return createVertexType(iClassName, (String) null);
  }

  public OClass createVertexType(final String iClassName,
      final String iSuperClassName) {
    return createVertexType(iClassName,
        iSuperClassName == null ? getVertexBaseType()
            : getVertexType(iSuperClassName));
  }

  public OClass createVertexType(final String iClassName,
      final OClass iSuperClass) {
    checkVertexType(iSuperClass);

    return executeOutsideTx(
        new OCallable<OClass, OrientBaseGraph>() {
          @Override
          public OClass call(final OrientBaseGraph g) {
            return getRawGraph().getMetadata().getSchema()
                .createClass(iClassName, iSuperClass);
          }
        }, "create vertex type '", iClassName, "' as subclass of '",
        iSuperClass.getName(), "'");
  }

  public final void dropVertexType(final String iTypeName) {
    executeOutsideTx(new OCallable<OClass, OrientBaseGraph>() {
      @Override
      public OClass call(final OrientBaseGraph g) {
        getRawGraph().getMetadata().getSchema().dropClass(iTypeName);
        return null;
      }
    }, "drop vertex type '", iTypeName, "'");
  }

  public OClass getEdgeBaseType() {
    return getRawGraph().getMetadata().getSchema()
        .getClass(OrientEdge.CLASS_NAME);
  }

  public final OClass getEdgeType(final String iTypeName) {
    final OClass cls = getRawGraph().getMetadata().getSchema()
        .getClass(iTypeName);
    if (cls != null)
      checkEdgeType(cls);
    return cls;
  }

  public OClass createEdgeType(final String iClassName) {
    return createEdgeType(iClassName, (String) null);
  }

  public OClass createEdgeType(final String iClassName,
      final String iSuperClassName) {
    return createEdgeType(iClassName,
        iSuperClassName == null ? getEdgeBaseType()
            : getEdgeType(iSuperClassName));
  }

  public OClass createEdgeType(final String iClassName,
      final OClass iSuperClass) {
    checkEdgeType(iSuperClass);
    return executeOutsideTx(
        new OCallable<OClass, OrientBaseGraph>() {
          @Override
          public OClass call(final OrientBaseGraph g) {
            return getRawGraph().getMetadata().getSchema()
                .createClass(iClassName, iSuperClass);
          }
        }, "create edge type '", iClassName, "' as subclass of '",
        iSuperClass.getName(), "'");
  }

  public final void dropEdgeType(final String iTypeName) {
    executeOutsideTx(new OCallable<OClass, OrientBaseGraph>() {
      @Override
      public OClass call(final OrientBaseGraph g) {
        getRawGraph().getMetadata().getSchema().dropClass(iTypeName);
        return null;
      }
    }, "drop edge type '", iTypeName, "'");
  }

  protected final void checkVertexType(final OClass iType) {
    if (iType == null)
      throw new IllegalArgumentException("Vertex class is null");

    if (!iType.isSubClassOf(OrientVertex.CLASS_NAME))
      throw new IllegalArgumentException("Type error. The class " + iType
          + " does not extend class '" + OrientVertex.CLASS_NAME
          + "' and therefore cannot be considered a Vertex");
  }

  protected final void checkEdgeType(final OClass iType) {
    if (iType == null)
      throw new IllegalArgumentException("Edge class is null");

    if (!iType.isSubClassOf(OrientEdge.CLASS_NAME))
      throw new IllegalArgumentException("Type error. The class " + iType
          + " does not extend class '" + OrientEdge.CLASS_NAME
          + "' and therefore cannot be considered an Edge");
  }

  /**
   * Returns a graph element, vertex or edge, starting from an ID.
   *
   * @param id
   *            element id
   * @return OrientElement subclass such as OrientVertex or OrientEdge
   */
  public OrientElement getElement(final Object id) {
    if (null == id)
      throw ExceptionFactory.vertexIdCanNotBeNull();

    if (id instanceof OrientElement)
      return (OrientElement) id;

    OIdentifiable rec;
    if (id instanceof OIdentifiable)
      rec = (OIdentifiable) id;
    else
      try {
        rec = new ORecordId(id.toString());
      } catch (IllegalArgumentException iae) {
        // orientdb throws IllegalArgumentException: Argument 'xxxx' is
        // not a RecordId in form of string. Format must be:
        // <cluster-id>:<cluster-position>
        return null;
      }

    final ODocument doc = rec.getRecord();
    if (doc != null) {
      final OClass schemaClass = doc.getSchemaClass();
      if (schemaClass.isSubClassOf(OrientVertex.CLASS_NAME)) {
        return new OrientVertex(this, doc);
      } else if (schemaClass.isSubClassOf(OrientEdge.CLASS_NAME)) {
        return new OrientEdge(this, doc);
      } else
        throw new IllegalArgumentException("Type error. The class "
            + schemaClass + " does not extend class neither '"
            + OrientVertex.CLASS_NAME + "' nor '"
            + OrientEdge.CLASS_NAME + "'");
    }

    return null;
  }

  protected void autoStartTransaction() {
    setCurrentGraphInThreadLocal();
  }

  protected void saveIndexConfiguration() {
    getRawGraph().getMetadata().getIndexManager().getConfiguration().save();
  }

  protected OrientGraphContext getContext(final boolean create) {
    OrientGraphContext context = threadContext.get();
    if (context == null || !context.rawGraph.getURL().equals(url)) {
      if (create)
        context = openOrCreate();
    }
    return context;
  }

  private OrientGraphContext openOrCreate() {
    if (url == null)
      throw new IllegalStateException("Database is closed");

    synchronized (this) {
      OrientGraphContext context = threadContext.get();
      if (context != null)
        removeContext();

      context = new OrientGraphContext();
      threadContext.set(context);

      synchronized (contexts) {
        contexts.add(context);
      }

      context.rawGraph = new ODatabaseDocumentTx(url);

      if (url.startsWith("remote:") || context.rawGraph.exists()) {
        context.rawGraph.open(username, password);

        // LOAD THE INDEX CONFIGURATION FROM INTO THE DICTIONARY
        // final ODocument indexConfiguration =
        // context.rawGraph.getMetadata().getIndexManager().getConfiguration();

        for (OIndex<?> idx : context.rawGraph.getMetadata()
            .getIndexManager().getIndexes()) {
                    ODocument metadata = idx.getMetadata();
          if (metadata != null && metadata.field(
              OrientIndex.CONFIG_CLASSNAME) != null)
            // LOAD THE INDEXES
            loadIndex(idx);
        }

      } else
        context.rawGraph.create();

      checkForGraphSchema(context.rawGraph);

      return context;
    }
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  private OrientIndex<?> loadIndex(final OIndex<?> rawIndex) {
    final OrientIndex<?> index = new OrientIndex(this, rawIndex);

    // REGISTER THE INDEX
    getContext(true).manualIndices.put(index.getIndexName(), index);
    return index;
  }

  private void removeContext() {
    final OrientGraphContext context = getContext(false);

    if (context != null) {
      for (Index<? extends Element> idx : context.manualIndices.values())
        ((OrientIndex<?>) idx).close();
      context.manualIndices.clear();

      if (!context.rawGraph.isClosed()) {
        context.rawGraph.commit();
        context.rawGraph.close();
      }

      synchronized (contexts) {
        contexts.remove(context);
      }

      threadContext.set(null);
    }
  }

  public <T extends Element> void dropKeyIndex(final String key,
      final Class<T> elementClass) {
    if (elementClass == null)
      throw ExceptionFactory.classForElementCannotBeNull();

    executeOutsideTx(new OCallable<OClass, OrientBaseGraph>() {
      @Override
      public OClass call(final OrientBaseGraph g) {
        final String className = getClassName(elementClass);
        getRawGraph().getMetadata().getIndexManager()
            .dropIndex(className + "." + key);
        return null;
      }
    }, "drop key index '", elementClass.getSimpleName(), ".", key, "'");

  }

  /**
   * Create an automatic indexing structure for indexing provided key for
   * element class.
   *
   * @param key
   *            the key to create the index for
   * @param elementClass
   *            the element class that the index is for
   * @param indexParameters
   *            a collection of parameters for the underlying index
   *            implementation:
   *            <ul>
   *            <li>"type" is the index type between the supported types
   *            (UNIQUE, NOTUNIQUE, FULLTEXT). The default type is NOT_UNIQUE
   *            <li>"class" is the class to index when it's a custom type
   *            derived by Vertex (V) or Edge (E)
   *            <li>"keytype" to use a key type different by OType.STRING,</li>
   *            </li>
   *            </ul>
   * @param <T>
   *            the element class specification
   */
  @SuppressWarnings({ "rawtypes" })
  public <T extends Element> void createKeyIndex(final String key,
      final Class<T> elementClass, final Parameter... indexParameters) {
    if (elementClass == null)
      throw ExceptionFactory.classForElementCannotBeNull();

    executeOutsideTx(new OCallable<OClass, OrientBaseGraph>() {
      @Override
      public OClass call(final OrientBaseGraph g) {

        String indexType = OClass.INDEX_TYPE.NOTUNIQUE.name();
        OType keyType = OType.STRING;
        String className = null;

        final String ancestorClassName = getClassName(elementClass);

        // READ PARAMETERS
        for (Parameter<?, ?> p : indexParameters) {
          if (p.getKey().equals("type"))
            indexType = p.getValue().toString().toUpperCase();
          else if (p.getKey().equals("keytype"))
            keyType = OType.valueOf(p.getValue().toString()
                .toUpperCase());
          else if (p.getKey().equals("class"))
            className = p.getValue().toString();
        }

        if (className == null)
          className = ancestorClassName;

        final ODatabaseDocumentTx db = getRawGraph();
        final OSchema schema = db.getMetadata().getSchema();

        final OClass cls = schema.getOrCreateClass(className,
            schema.getClass(ancestorClassName));
        final OProperty property = cls.getProperty(key);
        if (property != null)
          keyType = property.getType();

        db.getMetadata()
            .getIndexManager()
            .createIndex(
                className + "." + key,
                indexType,
                new OPropertyIndexDefinition(className, key,
                    keyType),
                cls.getPolymorphicClusterIds(), null, null);
        return null;

      }
    }, "create key index on '", elementClass.getSimpleName(), ".", key, "'");
  }

  public <T extends Element> Set<String> getIndexedKeys(
      final Class<T> elementClass) {
    return getIndexedKeys(elementClass, false);
  }

  public <T extends Element> Set<String> getIndexedKeys(
      final Class<T> elementClass, final boolean includeClassNames) {
    if (elementClass == null)
      throw ExceptionFactory.classForElementCannotBeNull();

    final OSchema schema = getRawGraph().getMetadata().getSchema();
    final String elementOClassName = getClassName(elementClass);

    Set<String> result = new HashSet<String>();
    final Collection<? extends OIndex<?>> indexes = getRawGraph()
        .getMetadata().getIndexManager().getIndexes();
    for (OIndex<?> index : indexes) {
      String indexName = index.getName();
      int point = indexName.indexOf(".");
      if (point > 0) {
        String oClassName = indexName.substring(0, point);
        OClass oClass = schema.getClass(oClassName);
        if (oClass.isSubClassOf(elementOClassName)) {
          if (includeClassNames)
            result.add(index.getName());
          else
            result.add(index.getDefinition().getFields().get(0));
        }
      }
    }
    return result;
  }

  public GraphQuery query() {
    return new OrientGraphQuery(this);
  }

  /**
   * Returns a OTraverse object to start traversing the graph.
   */
  public OTraverse traverse() {
    return new OTraverse();
  }

  /**
   * Executes commands against the graph. Commands are executed outside
   * transaction.
   *
   * @param iCommand
   *            Command request between SQL, GREMLIN and SCRIPT commands
   */
  public OCommandRequest command(final OCommandRequest iCommand) {
    return new OrientGraphCommand(this, getRawGraph().command(iCommand));
  }

  public boolean isUseLightweightEdges() {
    return useLightweightEdges;
  }

  public void setUseLightweightEdges(final boolean useDynamicEdges) {
    this.useLightweightEdges = useDynamicEdges;
  }

  public boolean isSaveOriginalIds() {
    return saveOriginalIds;
  }

  public void setSaveOriginalIds(final boolean saveIds) {
    this.saveOriginalIds = saveIds;
  }

  public long countVertices() {
    return getRawGraph().countClass(OrientVertex.CLASS_NAME);
  }

  public long countVertices(final String iClassName) {
    return getRawGraph().countClass(iClassName);
  }

  public long countEdges() {
    if (useLightweightEdges)
      throw new UnsupportedOperationException(
          "Graph set to use Lightweight Edges, count against edges is not supported");

    return getRawGraph().countClass(OrientEdge.CLASS_NAME);
  }

  public long countEdges(final String iClassName) {
    if (useLightweightEdges)
      throw new UnsupportedOperationException(
          "Graph set to use Lightweight Edges, count against edges is not supported");

    return getRawGraph().countClass(iClassName);
  }

  public boolean isKeepInMemoryReferences() {
    return keepInMemoryReferences;
  }

  public void setKeepInMemoryReferences(boolean useReferences) {
    this.keepInMemoryReferences = useReferences;
  }

  public boolean isUseClassForEdgeLabel() {
    return useClassForEdgeLabel;
  }

  public void setUseClassForEdgeLabel(final boolean useCustomClassesForEdges) {
    this.useClassForEdgeLabel = useCustomClassesForEdges;
  }

  public boolean isUseClassForVertexLabel() {
    return useClassForVertexLabel;
  }

  public void setUseClassForVertexLabel(
      final boolean useCustomClassesForVertex) {
    this.useClassForVertexLabel = useCustomClassesForVertex;
  }

  public boolean isUseVertexFieldsForEdgeLabels() {
    return useVertexFieldsForEdgeLabels;
  }

  public void setUseVertexFieldsForEdgeLabels(
      final boolean useVertexFieldsForEdgeLabels) {
    this.useVertexFieldsForEdgeLabels = useVertexFieldsForEdgeLabels;
  }

  public boolean isStandardElementConstraints() {
    return standardElementConstraints;
  }

  public void setStandardElementConstraints(
      final boolean allowsPropertyValueNull) {
    this.standardElementConstraints = allowsPropertyValueNull;
  }

  public static void encodeClassNames(final String... iLabels) {
    if (iLabels != null)
      // ENCODE LABELS
      for (int i = 0; i < iLabels.length; ++i)
        iLabels[i] = encodeClassName(iLabels[i]);
  }

  public static String encodeClassName(String iClassName) {
    if (iClassName == null)
      return null;

    if (Character.isDigit(iClassName.charAt(0)))
      iClassName = "-" + iClassName;

    try {
      return URLEncoder.encode(iClassName, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
      return iClassName;
    }
  }

  public static String decodeClassName(String iClassName) {
    if (iClassName == null)
      return null;

    if (iClassName.charAt(0) == '-')
      iClassName = iClassName.substring(1);

    try {
      return URLDecoder.decode(iClassName, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
      return iClassName;
    }
  }

  protected <T> String getClassName(final Class<T> elementClass) {
    if (elementClass.isAssignableFrom(Vertex.class))
      return OrientVertex.CLASS_NAME;
    else if (elementClass.isAssignableFrom(Edge.class))
      return OrientEdge.CLASS_NAME;
    throw new IllegalArgumentException("Class '" + elementClass
        + "' is neither a Vertex, nor an Edge");
  }

  protected <RET> RET executeOutsideTx(
      final OCallable<RET, OrientBaseGraph> iCallable,
      final String... iOperationStrings) {
    final boolean committed;
    final ODatabaseDocumentTx raw = getRawGraph();
    if (raw.getTransaction().isActive()) {
      if (warnOnForceClosingTx && OLogManager.instance().isWarnEnabled()) {
        // COMPOSE THE MESSAGE
        final StringBuilder msg = new StringBuilder();
        for (String s : iOperationStrings)
          msg.append(s);

        // ASSURE PENDING TX IF ANY IS COMMITTED
        OLogManager
            .instance()
            .warn(this,
                "Committing the active transaction to %s. To avoid this behavior do it outside the transaction",
                msg.toString());
      }
      raw.commit();
      committed = true;
    } else
      committed = false;

    try {
      return iCallable.call(this);
    } catch (Exception e) {
      if (e instanceof RuntimeException)
        throw (RuntimeException) e;
      else
        throw new OException(e);
    } finally {
      if (committed)
        autoStartTransaction();
    }
  }

  protected Object convertKey(final OIndex<?> idx, Object iValue) {
    if (iValue != null) {
      final OType[] types = idx.getKeyTypes();
      if (types.length == 0)
        iValue = iValue.toString();
      else
        iValue = OType.convert(iValue, types[0].getDefaultJavaType());
    }
    return iValue;
  }

  /**
   * Returns the current thread mode:
   * <ul>
   * <li><b>MANUAL</b> the user has to manually invoke the current database in
   * Thread Local:
   * ODatabaseRecordThreadLocal.INSTANCE.set(graph.getRawGraph());</li>
   * <li><b>AUTOSET_IFNULL</b> (default) each call assures the current graph
   * instance is set in the Thread Local only if no one was set before</li>
   * <li><b>ALWAYS_AUTOSET</b> each call assures the current graph instance is
   * set in the Thread Local</li>
   * </ul>
   *
   * @see #setThreadMode(THREAD_MODE)
   * @return Current Graph instance to allow calls in chain (fluent interface)
   */

  public THREAD_MODE getThreadMode() {
    return threadMode;
  }

  /**
   * Changes the thread mode:
   * <ul>
   * <li><b>MANUAL</b> the user has to manually invoke the current database in
   * Thread Local:
   * ODatabaseRecordThreadLocal.INSTANCE.set(graph.getRawGraph());</li>
   * <li><b>AUTOSET_IFNULL</b> (default) each call assures the current graph
   * instance is set in the Thread Local only if no one was set before</li>
   * <li><b>ALWAYS_AUTOSET</b> each call assures the current graph instance is
   * set in the Thread Local</li>
   * </ul>
   *
   * @param iControl
   *            Value to set
   * @see #getThreadMode()
   * @return Current Graph instance to allow calls in chain (fluent interface)
   */
  public OrientBaseGraph setThreadMode(final THREAD_MODE iControl) {
    this.threadMode = iControl;
    return this;
  }

  protected void setCurrentGraphInThreadLocal() {
    if (threadMode == THREAD_MODE.MANUAL)
      return;

    final ODatabaseRecord tlDb = ODatabaseRecordThreadLocal.INSTANCE
        .getIfDefined();
    if (threadMode == THREAD_MODE.ALWAYS_AUTOSET || tlDb == null) {
      final OrientGraphContext ctx = getContext(true);

      if (ctx != null && (tlDb == null || tlDb != ctx.rawGraph))
        // SET IT
        ODatabaseRecordThreadLocal.INSTANCE.set(getRawGraph());
    }
  }

  public boolean isWarnOnForceClosingTx() {
    return warnOnForceClosingTx;
  }

  public OrientBaseGraph setWarnOnForceClosingTx(
      final boolean warnOnSchemaChangeInTx) {
    this.warnOnForceClosingTx = warnOnSchemaChangeInTx;
    return this;
  }
}
TOP

Related Classes of com.tinkerpop.blueprints.impls.orient.OrientBaseGraph

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.