Package com.dooapp.gaedo.blueprints.strategies

Source Code of com.dooapp.gaedo.blueprints.strategies.GraphBasedMappingStrategy

package com.dooapp.gaedo.blueprints.strategies;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

import javax.persistence.CascadeType;

import com.dooapp.gaedo.blueprints.AbstractBluePrintsBackedFinderService;
import com.dooapp.gaedo.blueprints.Kind;
import com.dooapp.gaedo.blueprints.Properties;
import com.dooapp.gaedo.blueprints.dynabeans.BagProperty;
import com.dooapp.gaedo.blueprints.dynabeans.PropertyMapPropertyAccess;
import com.dooapp.gaedo.blueprints.queries.tests.AndVertexTest;
import com.dooapp.gaedo.blueprints.queries.tests.CompoundVertexTest;
import com.dooapp.gaedo.blueprints.queries.tests.VertexPropertyTest;
import com.dooapp.gaedo.blueprints.strategies.graph.GraphBasedPropertyBuilder;
import com.dooapp.gaedo.blueprints.strategies.graph.GraphFieldLocator;
import com.dooapp.gaedo.blueprints.strategies.graph.NoEdgeInNamedGraphsException;
import com.dooapp.gaedo.blueprints.transformers.Literals;
import com.dooapp.gaedo.blueprints.transformers.TypeUtils;
import com.dooapp.gaedo.extensions.migrable.Migrator;
import com.dooapp.gaedo.finders.root.CumulativeFieldInformerLocator;
import com.dooapp.gaedo.finders.root.DelegatingInformerLocator;
import com.dooapp.gaedo.finders.root.FieldInformerLocator;
import com.dooapp.gaedo.finders.root.InformerFactory;
import com.dooapp.gaedo.finders.root.ProxyBackedInformerFactory;
import com.dooapp.gaedo.finders.root.ReflectionBackedInformerFactory;
import com.dooapp.gaedo.properties.Property;
import com.dooapp.gaedo.properties.PropertyProvider;
import com.tinkerpop.blueprints.pgm.Edge;
import com.tinkerpop.blueprints.pgm.Vertex;

/**
* When using this strategy, properties will be directly determined from edges
* exiting from edited node.
*
* @author ndx
*
* @param <DataType>
*/
public class GraphBasedMappingStrategy<DataType> extends AbstractMappingStrategy<DataType> implements GraphMappingStrategy<DataType> {
  private static final Logger logger = Logger.getLogger(GraphBasedMappingStrategy.class.getName());

    /**
   * Default mapped properties
   */
  private Map<Property, Collection<CascadeType>> beanPropertiesForServiceClass;
 
  /**
   * Obejct storing the currently loaded id. Most of this time this value will be null. But when a loading operation starts
   * a value will be stored there.
   */
  private ThreadLocal<String> loadedByThisThread = new ThreadLocal<String>();

  public GraphBasedMappingStrategy(Class<DataType> serviceContainedClass, PropertyProvider propertyProvider, Migrator migrator) {
    super(serviceContainedClass, propertyProvider, migrator);
    beanPropertiesForServiceClass = StrategyUtils.getBeanPropertiesFor(propertyProvider, serviceContainedClass, migrator);
    // an auxiliary list is created to navigate properties, as we will
    // remove some of them from beanProperties,
    // and that is not something iterators are good at
    List<Property> propertyNavigator = new LinkedList<Property>(beanPropertiesForServiceClass.keySet());
    // Replace id property by one allowing zero navigation (for findById and
    // other methods)
    for (Property p : propertyNavigator) {
      if (p.getAnnotation(BagProperty.class) != null) {
        beanPropertiesForServiceClass.remove(p);
      }
    }

  }

  /**
   * To get contained property, we simply build a map of edges outgoing from
   * vertex and associate to them cascade information based on ... what ?
   * magic ?
   *
   * @param object
   *            object to load data in
   * @param vertex
   *            associated vertew which will never be left null
   * @return mapping between properties representing edges and cascade for
   *         these properties
   * @see com.dooapp.gaedo.blueprints.strategies.GraphMappingStrategy#getContainedProperties(java.lang.Object,
   *      com.tinkerpop.blueprints.pgm.Vertex, CascadeType)
   */
  @Override
  public Map<Property, Collection<CascadeType>> getContainedProperties(DataType object, Vertex vertex, CascadeType cascadeType) {
    // notice that, as default, properties only cascade creation
    Map<Property, Collection<CascadeType>> returned = new HashMap<Property, Collection<CascadeType>>();
    // During persist operation, we only persist graph properties, and NOT
    // object ones (that would polute the graph)
    if (!CascadeType.MERGE.equals(cascadeType) && !CascadeType.PERSIST.equals(cascadeType))
      returned.putAll(beanPropertiesForServiceClass);
    // extract edges from persistable object infos
    if (object instanceof PropertyMapPropertyAccess) {
      PropertyMapPropertyAccess propertyBag = (PropertyMapPropertyAccess) object;
      for (Property uri : propertyBag.propertyUris()) {
        returned.put(uri, StrategyUtils.extractCascadeOfJPAAnnotations(uri));
      }
    }
    if (vertex == null) {
    } else {
      Map<String, GraphBasedPropertyBuilder<DataType>> edgeLabelToProperty = new TreeMap<String, GraphBasedPropertyBuilder<DataType>>();
      for (Edge e : vertex.getOutEdges()) {
        String edgeLabel = e.getLabel();
        if (!edgeLabelToProperty.containsKey(edgeLabel)) {
          edgeLabelToProperty.put(edgeLabel, new GraphBasedPropertyBuilder<DataType>(serviceContainedClass, service.getDriver(), service.getLens()));
        }
        edgeLabelToProperty.get(edgeLabel).add(e);
      }
      for (GraphBasedPropertyBuilder<DataType> builder : edgeLabelToProperty.values()) {
        try {
          Property built = builder.build();
          // only add property if absent from properties laoded from the bean, as properties loaded from the bean are respectfull to initial data type
          if(!returned.containsKey(built))
            returned.put(built, StrategyUtils.extractCascadeOfJPAAnnotations(built));
        } catch(NoEdgeInNamedGraphsException e) {
          logger.info(e.getMessage());
        }
      }
    }
    return returned;
  }

  /**
   * When loading that strategy, we also customize the informer factory to
   * make sure non-existing properties
   *
   * @param service
   * @see com.dooapp.gaedo.blueprints.strategies.AbstractMappingStrategy#reloadWith(com.dooapp.gaedo.blueprints.AbstractBluePrintsBackedFinderService)
   */
  @Override
  public void reloadWith(AbstractBluePrintsBackedFinderService<?, DataType, ?> service) {
    super.reloadWith(service);
    InformerFactory informerFactory = service.getInformerFactory();
    if (informerFactory instanceof ProxyBackedInformerFactory) {
      ProxyBackedInformerFactory initialProxy = (ProxyBackedInformerFactory) informerFactory;
      ReflectionBackedInformerFactory initialReflective = initialProxy.getReflectiveInformerFactory();
     
      FieldInformerLocator initialLocator = initialReflective.getFieldLocator();
      // nothing has been loaded yet
      if(initialLocator instanceof CumulativeFieldInformerLocator) {
        ReflectionBackedInformerFactory usedReflective = new ReflectionBackedInformerFactory(
                new DelegatingInformerLocator(initialLocator, createLocator(initialLocator)),
                initialReflective.getPropertyProvider()
                );
       
        ProxyBackedInformerFactory usedProxy = new ProxyBackedInformerFactory(usedReflective);
        service.setInformerFactory(usedProxy);
      } else if(initialLocator instanceof DelegatingInformerLocator) {
        DelegatingInformerLocator delegating = (DelegatingInformerLocator) initialLocator;
        delegating.setSecond(createLocator(delegating.getFirst()));
      }
    }
  }

  private GraphFieldLocator<DataType> createLocator(FieldInformerLocator cumulativeLocator) {
    return new GraphFieldLocator<DataType>(
            serviceContainedClass,
            service.getInformer().getClass(),
            service.getDatabase(), service
            .getDriver(),
    /*
     * yup, there is a loop here, but for a good reason :
     * graphFieldLocator will generate a Property out of an edge, then
     * cumulative locator will create a FieldInformer out of that
     * property
     */
    cumulativeLocator,
    service.getLens());
  }

  @Override
  protected CompoundVertexTest addDefaultSearchToAndTest(AndVertexTest vertexTest) {
    vertexTest.add(new VertexPropertyTest(service.getDriver(), new LinkedList<Property>(), Properties.kind.name(), Kind.uri.name()));
    return vertexTest;
  }

  /**
   * Any vertex uses as default the service contained class type
   *
   * @param vertex
   * @return
   * @see com.dooapp.gaedo.blueprints.strategies.GraphMappingStrategy#getEffectiveType(com.tinkerpop.blueprints.pgm.Vertex)
   */
  @Override
  public String getEffectiveType(Vertex vertex) {
    Kind kind = Kind.valueOf(vertex.getProperty(Properties.kind.name()).toString());
    switch (kind) {
    case literal:
      Object typeProperty = vertex.getProperty(Properties.type.name());
      if (typeProperty == null)
        return STRING_CLASS;
      return TypeUtils.getClass(typeProperty.toString());
    case uri:
      return serviceContainedClass.getName();
    case bnode:
    default:
      return STRING_CLASS;
    }
  }

  @Override
  public String getAsId(Object object) {
    return Literals.get(object.getClass()).getVertexId(object);
  }
 
  @Override
  public void loaded(String fromId, Vertex from, DataType into, Map<String, Object> objectsBeingAccessed) {
    // when this happens, it means we finally loaded the initial object
    if(fromId.equals(loadedByThisThread.get()))
      loadedByThisThread.remove();
    idProperty.set(into, from.getProperty(Properties.value.name()));
  }

  /**
   * What are the objects requiring loading ? The ones that are directly accessed.
   * As a consequence, we use a {@link ThreadLocal} mechanism to store objects ids being loaded.
   * In other words, if thread local contains no object id, put current one.
   * If there is one (which is not current one, return false).
   * Notice the {@link #loaded(String, Vertex, Object, Map)} method, when invoked, will clear that info for queries to run smoothly
   * @param objectVertexId
   * @param objectVertex
   * @param objectsBeingAccessed
   * @return
   * @see com.dooapp.gaedo.blueprints.strategies.GraphMappingStrategy#shouldLoadPropertiesOf(java.lang.String, com.tinkerpop.blueprints.pgm.Vertex, java.util.Map)
   */
  @Override
  public boolean shouldLoadPropertiesOf(String objectVertexId, Vertex objectVertex, Map<String, Object> objectsBeingAccessed) {
    if(loadedByThisThread.get()==null) {
      loadedByThisThread.set(objectVertexId);
    }
    return loadedByThisThread.get().equals(objectVertexId);
  }
}
TOP

Related Classes of com.dooapp.gaedo.blueprints.strategies.GraphBasedMappingStrategy

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.