Package com.dooapp.gaedo.blueprints

Source Code of com.dooapp.gaedo.blueprints.GraphUtils

package com.dooapp.gaedo.blueprints;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.CascadeType;
import javax.persistence.Column;

import com.dooapp.gaedo.blueprints.annotations.GraphProperty;
import com.dooapp.gaedo.blueprints.strategies.GraphMappingStrategy;
import com.dooapp.gaedo.blueprints.strategies.PropertyMappingStrategy;
import com.dooapp.gaedo.blueprints.transformers.LiteralTransformer;
import com.dooapp.gaedo.blueprints.transformers.Literals;
import com.dooapp.gaedo.blueprints.transformers.TupleTransformer;
import com.dooapp.gaedo.blueprints.transformers.Tuples;
import com.dooapp.gaedo.finders.repository.ServiceRepository;
import com.dooapp.gaedo.properties.Property;
import com.tinkerpop.blueprints.pgm.Edge;
import com.tinkerpop.blueprints.pgm.Graph;
import com.tinkerpop.blueprints.pgm.IndexableGraph;
import com.tinkerpop.blueprints.pgm.Vertex;
import com.tinkerpop.blueprints.pgm.oupls.sail.GraphSail;

public class GraphUtils {
  private static final String GAEDO_PREFIX = "https://github.com/Riduidel/gaedo/";

  /**
   * Ontologic context used by all gaedo graph elements.
   */
  public static final String GAEDO_CONTEXT = GAEDO_PREFIX+"visible";
 
  /**
   * Ontologic context used by gaedo graph elements that we want to keep hidden. Those elements should never be exported.
   * To make sure this works well, this context is set to null. Crazy no ?
   */
  public static final String GAEDO_HIDDEN_CONTEXT = GAEDO_PREFIX+"hidden";

  private static final Logger logger = Logger.getLogger(GraphUtils.class.getName());
 
  public static String asSailProperty(String context) {
    if(GraphSail.NULL_CONTEXT_NATIVE.equals(context))
      return context;
    return GraphSail.URI_PREFIX+" "+context;
  }

  /**
   * Generate edge name from property infos. Notice generated edge name will first be searched in property annotations, and only
   * if none compatile found by generating a basic property name
   * @param p source property
   * @return an edge name (by default property container class name + "." + property name
   */
  public static String getEdgeNameFor(Property p) {
    if(p.getAnnotation(GraphProperty.class)!=null) {
      GraphProperty graph = p.getAnnotation(GraphProperty.class);
      // Test added to avoid default value (which defaults name to "")
      if(graph.name()!=null && graph.name().trim().length()>0)
        return graph.name();
    }
    if(p.getAnnotation(Column.class)!=null) {
      Column column = p.getAnnotation(Column.class);
      if(column.name()!=null && column.name().trim().length()>0)
        return column.name();
    }
    return getDefaultEdgeNameFor(p);
  }

  public static String getDefaultEdgeNameFor(Property p) {
    return p.getDeclaringClass().getName()+":"+p.getName();
  }

  /**
   * Create a vertex out of a basic object. if object is of a simple type, we'll use value as id. Elsewhere, we will generate an id for object
   * @param database database in which vertex will be stored
   * @param value
   * @return
   */
  public static Vertex getVertexForLiteral(GraphDatabaseDriver database, Object value) {
    Vertex returned = null;
    // Now distinct behaviour between known objects and unknown ones
    Class<? extends Object> valueClass = value.getClass();
    if(Literals.containsKey(valueClass)) {
      LiteralTransformer transformer = Literals.get(valueClass);
      returned = transformer.getVertexFor(database, valueClass.cast(value));
    } else {
      throw new ObjectIsNotARealLiteralException(value, valueClass);
      // TODO do not forget to set id property
    }
    return returned;
  }

  /**
   * Create an object instance from a literal vertex compatible with this service contained class
   * @param driver driver used to load data
   * @param strategy TODO
   * @param classLoader class loader used to find class
   * @param key vertex containing object id
   * @param repository service repository, used to disambiguate subclass of literal and managed class
   * @param objectsBeingAccessed
   * @param property property used to navigate to this value. it allows disambiguation for literal values (which may be linked to more than one type, the typical example being
   * a saved float, say "3.0", which may also be refered as the string "3.0").
   * @return a fresh instance, with only id set
   */
  public static Object createInstance(GraphDatabaseDriver driver, GraphMappingStrategy strategy, ClassLoader classLoader, Vertex key, Class<?> defaultType, ServiceRepository repository, Map<String, Object> objectsBeingAccessed) {
    String effectiveType = null;
    Kind kind = getKindOf(key);
    if(Kind.literal==kind) {
      /* One literal node may be used according to different types. To disambiguate, we check if effective type matches default one. If not (typically
       * type returns string and user wants number), prefer default type.
       */
      effectiveType = driver.getEffectiveType(key);
      try {
        if(!Collection.class.isAssignableFrom(defaultType) && !defaultType.isAssignableFrom(Class.forName(effectiveType))) {
          effectiveType = defaultType.getName();
        }
      } catch(Exception e) {
        // nothing to do : we use effective type - or try to
      }
    } else {
      effectiveType = driver.getEffectiveType(key);
    }
    if(classLoader==null) {
      throw new UnspecifiedClassLoader();
    }
    try {
      if(Literals.containsKey(classLoader, effectiveType) && !repository.containsKey(effectiveType)) {
        LiteralTransformer transformer = Literals.get(classLoader, effectiveType);
        return transformer.loadObject(driver, classLoader, effectiveType, key);
      } else {
        Class<?> type = classLoader.loadClass(effectiveType);
        if(Tuples.containsKey(type) && !repository.containsKey(type)) {
          // Tuples are handled the object way (easier, but more dangerous
          TupleTransformer transformer = Tuples.get(type);
          return transformer.loadObject(driver, strategy, classLoader, type, key, repository, objectsBeingAccessed);
        } else {
          return  type.newInstance();
        }
      }
    } catch(Exception e) {
      throw new UnableToCreateException(effectiveType, e);
    }
  }

  public static Kind getKindOf(Vertex key) {
    String kindName = key.getProperty(Properties.kind.name()).toString();
    Kind kind = Kind.valueOf(kindName);
    return kind;
  }

  /**
   * get an id value for the given object whatever the object is
   * @param repository
   * @param value
   * @return
   */
  public static <DataType> String getIdOf(ServiceRepository repository, DataType value) {
    Class<? extends Object> valueClass = value.getClass();
    if(repository.containsKey(valueClass)) {
      AbstractBluePrintsBackedFinderService<IndexableGraph, DataType, ?> service = (AbstractBluePrintsBackedFinderService<IndexableGraph, DataType, ?>) repository.get(valueClass);
      // All ids are string, don't worry about it
      return service.getIdOf(value).toString();
    } else if(Literals.containsKey(valueClass)) {
      return getIdOfLiteral(valueClass, null, value);
    } else if(Tuples.containsKey(valueClass)) {
      return getIdOfTuple(repository, valueClass, value);
    } else {
      throw new ImpossibleToGetIdOfUnknownType(valueClass);
    }
  }

  /**
   * Get the value of the vertex id for the given literal
   * @param database used graph
   * @param declaredClass declared object class
   * @param idProperty gives the declared type of id (which may differ from primitive types, where user may give an integer instead of a long, as an example). Notice that,
   * contrary to most of gaedo code, this field can be null
   * @param objectId object id value
   * @return the value used by {@link Properties#vertexId} to identify the vertex associated to that object
   */
  public static String getIdOfLiteral(Class<?> declaredClass, Property idProperty, Object objectId) {
    PropertyMappingStrategy strategy = PropertyMappingStrategy.prefixed;
    if(idProperty!=null && idProperty.getAnnotation(GraphProperty.class)!=null) {
      strategy = idProperty.getAnnotation(GraphProperty.class).mapping();
    }
    return strategy.literalToId(declaredClass, idProperty, objectId);
  }

  /**
   * Get the value of the vertex id for the given object
   * @param database used graph
   * @param declaredClass declared object class
   * @param idProperty gives the declared type of id (which may differ from primitive types, where user may give an integer instead of a long, as an example). Notice that,
   * contrary to most of gaedo code, this field can be null
   * @param value object id value
   * @return the value used by {@link Properties#vertexId} to identify the vertex associated to that object
   */
  public static String getIdOfTuple(ServiceRepository repository, Class<?> declaredClass, Object value) {
    return Tuples.get(declaredClass).getIdOfTuple(repository, value);
  }

  /**
   * Generates a vertex for the given tuple
   * @param bluePrintsBackedFinderService source service, some informations may be extracted from it
   * @param repository service repository for non literal values
   * @param value tuple to persist
   * @param objectsBeingUpdated map of objects already being accessed. Links object id to object
   * @return the
   */
  public static Vertex getVertexForTuple(AbstractBluePrintsBackedFinderService<? extends Graph, ?, ?> service, ServiceRepository repository, Object value, Map<String, Object> objectsBeingUpdated) {
    Vertex returned = null;
    // Now distinct behaviour between known objects and unknown ones
    Class<? extends Object> valueClass = value.getClass();
    if(Tuples.containsKey(valueClass)) {
      TupleTransformer transformer = Tuples.get(valueClass);
      returned = transformer.getVertexFor(service, valueClass.cast(value), objectsBeingUpdated);
    } else {
      throw new ObjectIsNotARealTupleException(value, valueClass);
      // TODO do not forget to set id property
    }
    return returned;
  }

  public static Collection<CascadeType> extractCascadeOf(CascadeType[] cascade) {
    Set<CascadeType> returned = new HashSet<CascadeType>();
    returned.addAll(Arrays.asList(cascade));
    if(returned.contains(CascadeType.ALL)) {
      returned.remove(CascadeType.ALL);
      returned.add(CascadeType.MERGE);
      returned.add(CascadeType.PERSIST);
      returned.add(CascadeType.REFRESH);
      returned.add(CascadeType.REMOVE);
    }
    return returned;
  }

  /**
   * Converts a vertex to a string by outputing all its properties values
   * @param objectVertex
   * @return
   */
  public static String toString(Vertex objectVertex) {
    StringBuilder sOut = new StringBuilder("{");
    for(String s : objectVertex.getPropertyKeys()) {
      if(sOut.length()>1)
        sOut.append("; ");
      sOut.append(s).append("=").append(objectVertex.getProperty(s));
    }
    return sOut.append("}").toString();
  }

  /**
   * Find all contexts in given edge by looking, in {@link GraphSail#CONTEXT_PROP} property, what are the contexts. These contexts are extracted by iteratively calling
   * {@link #CONTEXTS_MATCHER} and {@link Matcher#find(int)} method.
   * @param edge input edge
   * @return collection of declared contexts.
   */
  public static Collection<String> getContextsOf(Edge edge) {
    String contextsString = edge.getProperty(GraphSail.CONTEXT_PROP).toString();
    Matcher matcher = CONTEXTS_MATCHER.matcher(contextsString);
    Collection<String> output = new LinkedList<String>();
    int character = 0;
    while(matcher.find(character)) {
      if(GraphSail.NULL_CONTEXT_NATIVE.equals(matcher.group(1))) {
        // the null context is a low-level view. It should be associated with "no named graph" (that's to say an empty collection).
        return output;
      } else if(matcher.group(1).startsWith(GraphSail.URI_PREFIX+"")){
        output.add(matcher.group(2));
      }
      character = matcher.end();
    }
    return output;
  }

  /**
   * Compiled pattern used to match strings such as
   * <pre>U https://github.com/Riduidel/gaedo/visible  U http://purl.org/dc/elements/1.1/description</pre>
   * or
   * <pre>N U http://purl.org/dc/elements/1.1/description</pre>
   * or even
   * <pre>N</pre>
   *
   * You know why I do such a pattern matching ? Because sail graph named graph definintion goes by concatenaing contexts URI in edges properties.
   * This is really douchebag code !
   */
  public static final Pattern CONTEXTS_MATCHER = Pattern.compile("(N|U ([\\S]+))+");

  /**
   * Check if edge has the required named graphs list
   * @param e edge to test
   * @param namedGraphs named graphs the edge must have
   * @return true if edge contexts are the given collection of named graphs
   */
  public static boolean isInNamedGraphs(Edge e, Collection<String> namedGraphs) {
    Collection<String> contexts = getContextsOf(e);
    // Only analyse edge if it is in named graph, and only in named graphs
    boolean isInNamedGraphs = contexts.size()==namedGraphs.size() && contexts.containsAll(namedGraphs);
    return isInNamedGraphs;
  }
}
TOP

Related Classes of com.dooapp.gaedo.blueprints.GraphUtils

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.