public class AdjacencyAnnotationHandler implements AnnotationHandler<Adjacency> {

    @Override
    public Class<Adjacency> getAnnotationType() {
        return Adjacency.class;
    }

    @Override
    public Object processElement(final Adjacency annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph,
            final Element element, final Direction direction) {
        if (element instanceof Vertex) {
            return processVertex(annotation, method, arguments, framedGraph, (Vertex) element);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    public Object processVertex(final Adjacency adjacency, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Vertex vertex) {
        if (ClassUtilities.isGetMethod(method)) {
            final FramedVertexIterable r = new FramedVertexIterable(framedGraph, vertex.getVertices(adjacency.direction(), adjacency.label()),
                    ClassUtilities.getGenericClass(method));
            if (ClassUtilities.returnsIterable(method)) {
                return r;
            } else {
                return r.iterator().hasNext() ? r.iterator().next() : null;
            }
        } else if (ClassUtilities.isAddMethod(method)) {
            Class<?> returnType = method.getReturnType();
            Vertex newVertex;
            Object returnValue = null;
            if (arguments == null) {
                // Use this method to get the vertex so that the vertex
                // initializer is called.
                returnValue = framedGraph.addVertex(null, returnType);
                newVertex = ((VertexFrame) returnValue).asVertex();
            } else {
                newVertex = ((VertexFrame) arguments[0]).asVertex();
            }
            addEdges(adjacency, framedGraph, vertex, newVertex);

            if (returnType.isPrimitive()) {
                return null;
            } else {
                return returnValue;
            }

        } else if (ClassUtilities.isRemoveMethod(method)) {
            removeEdges(adjacency.direction(), adjacency.label(), vertex, ((VertexFrame) arguments[0]).asVertex(), framedGraph);
            return null;
        } else if (ClassUtilities.isSetMethod(method)) {
            removeEdges(adjacency.direction(), adjacency.label(), vertex, null, framedGraph);
            if (ClassUtilities.acceptsIterable(method)) {
                for (Object o : (Iterable) arguments[0]) {
                    Vertex v = ((VertexFrame) o).asVertex();
                    addEdges(adjacency, framedGraph, vertex, v);
                }
                return null;
            } else {
                if (null != arguments[0]) {
                    Vertex newVertex = ((VertexFrame) arguments[0]).asVertex();
                    addEdges(adjacency, framedGraph, vertex, newVertex);
                }
                return null;
            }
        }

        return null;
    }

    private void addEdges(final Adjacency adjacency, final FramedGraph framedGraph, final Vertex vertex, Vertex newVertex) {
        switch(adjacency.direction()) {
        case OUT:
            framedGraph.addEdge(null, vertex, newVertex, adjacency.label());
            break;
        case IN:
            framedGraph.addEdge(null, newVertex, vertex, adjacency.label());
            break;
        case BOTH:
            throw new UnsupportedOperationException("Direction.BOTH it not supported on 'add' or 'set' methods");
        }
    }

    private void removeEdges(final Direction direction, final String label, final Vertex element, final Vertex otherVertex, final FramedGraph framedGraph) {
        for (final Edge edge : element.getEdges(direction, label)) {
            if (null == otherVertex || edge.getVertex(direction.opposite()).equals(otherVertex)) {
                framedGraph.removeEdge(edge);
            }
        }
    }
} public interface AnnotationHandler<T extends Annotation> {
    /**
     * @return The annotation type that this handler responds to. 
     */
    public Class<T> getAnnotationType();

    /**
     * @param annotation The annotation
     * @param method The method being called on the frame.
     * @param arguments The arguments to the method.
     * @param framedGraph The graph being called. 
     * @param element The underlying element.
     * @param direction The direction of the edge.
     * @return A return value for the method.
     */
    public Object processElement(final T annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction);
} public class DomainAnnotationHandler implements AnnotationHandler<Domain> {

    @Override
    public Class<Domain> getAnnotationType() {
        return Domain.class;
    }

    @Override
    public Object processElement(final Domain annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (element instanceof Edge) {
            return processEdge(annotation, method, arguments, framedGraph, (Edge) element, direction);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    public Object processEdge(final Domain annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Edge edge, final Direction direction) {
        return framedGraph.frame(edge.getVertex(direction), method.getReturnType());
    }

} public class InVertexAnnotationHandler implements AnnotationHandler<InVertex> {
    @Override
    public Class<InVertex> getAnnotationType() {
        return InVertex.class;
    }

    @Override
    public Object processElement(final InVertex annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (element instanceof Edge) {
            return framedGraph.frame(((Edge)element).getVertex(Direction.IN), method.getReturnType());
        } else {
            throw new UnsupportedOperationException();
        }
    }
} public class IncidenceAnnotationHandler implements AnnotationHandler<Incidence> {

    @Override
    public Class<Incidence> getAnnotationType() {
        return Incidence.class;
    }

    @Override
    public Object processElement(final Incidence annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (element instanceof Vertex) {
            return processVertex(annotation, method, arguments, framedGraph, (Vertex) element);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    public Object processVertex(final Incidence incidence, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Vertex element) {
        if (ClassUtilities.isGetMethod(method)) {
            return new FramedEdgeIterable(framedGraph, element.getEdges(incidence.direction(), incidence.label()), incidence.direction(), ClassUtilities.getGenericClass(method));
        } else if (ClassUtilities.isAddMethod(method)) {
            
            switch(incidence.direction()) {
            case OUT:
                return framedGraph.addEdge(null, element, ((VertexFrame) arguments[0]).asVertex(), incidence.label(), Direction.OUT, method.getReturnType());
            case IN:
                return framedGraph.addEdge(null, ((VertexFrame) arguments[0]).asVertex(), element, incidence.label(), Direction.IN, method.getReturnType());
            case BOTH:
                throw new UnsupportedOperationException("Direction.BOTH it not supported on 'add' or 'set' methods");
            }
                
        } else if (ClassUtilities.isRemoveMethod(method)) {
            framedGraph.removeEdge(((EdgeFrame) arguments[0]).asEdge());
            return null;
        }

        return null;
    }

} public class OutVertexAnnotationHandler implements AnnotationHandler<OutVertex> {
    @Override
    public Class<OutVertex> getAnnotationType() {
        return OutVertex.class;
    }

    @Override
    public Object processElement(final OutVertex annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (element instanceof Edge) {
        	return framedGraph.frame(((Edge)element).getVertex(Direction.OUT), method.getReturnType());
        } else {
            throw new UnsupportedOperationException();
        }
    }
} public class PropertyAnnotationHandler implements AnnotationHandler<Property> {

    @Override
    public Class<Property> getAnnotationType() {
        return Property.class;
    }

    @Override
    public Object processElement(final Property annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (ClassUtilities.isGetMethod(method)) {
            Object value = element.getProperty(annotation.value());
            if (method.getReturnType().isEnum())
                return getValueAsEnum(method, value);
            else
                return value;
        } else if (ClassUtilities.isSetMethod(method)) {
            Object value = arguments[0];
            if (null == value) {
                element.removeProperty(annotation.value());
            } else {
                if (value.getClass().isEnum()) {
                    element.setProperty(annotation.value(), ((Enum<?>) value).name());
                } else {
                    element.setProperty(annotation.value(), value);
                }
            }
            return null;
        } else if (ClassUtilities.isRemoveMethod(method)) {
            element.removeProperty(annotation.value());
            return null;
        }

        return null;
    }

    private Enum getValueAsEnum(final Method method, final Object value) {
        Class<Enum> en = (Class<Enum>) method.getReturnType();
        if (value != null)
            return Enum.valueOf(en, value.toString());

        return null;
    }
} public class RangeAnnotationHandler implements AnnotationHandler<Range> {

    @Override
    public Class<Range> getAnnotationType() {
        return Range.class;
    }

    @Override
    public Object processElement(final Range annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Element element, final Direction direction) {
        if (element instanceof Edge) {
            return processEdge(annotation, method, arguments, framedGraph, (Edge) element, direction);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    public Object processEdge(final Range annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph, final Edge edge, final Direction direction) {
        return framedGraph.frame(edge.getVertex(direction.opposite()), method.getReturnType());
    }

} public class GremlinGroovyAnnotationHandler implements AnnotationHandler<GremlinGroovy>, MethodHandler<GremlinGroovy> {
    private static final String PIPE = "_()";
    private final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine();
    private static final String IT = "it";
    private static final String G = "g";

    private static final Logger LOGGER = Logger.getLogger(GremlinGroovyAnnotationHandler.class.getName());

    public GremlinGroovyScriptEngine getGremlinScriptEngine() {
        return this.engine;
    }

    @Override
    public Class<GremlinGroovy> getAnnotationType() {
        return GremlinGroovy.class;
    }

    @Override
    public Object processElement(final GremlinGroovy annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph,
                                 final Element element, final Direction direction) {
        if (element instanceof Vertex) {
            return processVertex(annotation, method, arguments, framedGraph, (Vertex) element);
        } else {
            throw new UnsupportedOperationException("This method only works for vertices");
        }
    }

    public Object processVertex(final GremlinGroovy annotation, final Method method, final Object[] arguments, final FramedGraph framedGraph,
                                final Vertex vertex) {
        try {
            final CompiledScript script = this.engine.compile(annotation.value());
            final Bindings bindings = getBindings(method, arguments);
            bindings.put(IT, vertex);
            bindings.put(G, framedGraph);
            final Object result = script.eval(bindings);

            // TODO: Deprecate the use of _() and replace with it
            if (result instanceof Pipe & annotation.value().startsWith(PIPE)) {
                LOGGER.warning("_() is deprecated in favor of using 'it' to represent the framed vertex");
                ((Pipe) result).setStarts(new SingleIterator<Element>(vertex));
            }

            if (annotation.frame()) {
                if (result instanceof Iterable) {
                    final FramedVertexIterable r = new FramedVertexIterable(framedGraph, (Iterable) result, ClassUtilities.getGenericClass(method));
                    return (ClassUtilities.returnsIterable(method)) ? r : r.iterator().hasNext() ? r.iterator().next() : null;
                } else if (ClassUtilities.returnsMap(method)) {
                    return new FramedVertexMap(framedGraph, (Map) result, ClassUtilities.getGenericClass(method));
                } else if (result instanceof Vertex) {
                    return framedGraph.frame((Vertex) result, ClassUtilities.getGenericClass(method));
                } else {
                    throw new IllegalStateException("The returned object can not be framed: " + result.getClass());
                }
            } else {
                return result;
            }

        } catch (ScriptException e) {
        	ExceptionUtils.sneakyThrow(e); //Preserve original exception functionality.
            return null;
        }
    }

    private Bindings getBindings(final Method method, final Object[] arguments) {
        Bindings bindings = engine.createBindings();
        Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
        for (int pCount = 0; pCount < allParameterAnnotations.length; pCount++) {
            Annotation parameterAnnotations[] = allParameterAnnotations[pCount];
            for (int aCount = 0; aCount < parameterAnnotations.length; aCount++) {
                Annotation paramAnnotation = parameterAnnotations[aCount];
                if (paramAnnotation instanceof GremlinParam) {
                    bindings.put(((GremlinParam) paramAnnotation).value(), arguments[pCount]);
                    break;
                }
            }
        }
        return bindings;
    }

	@Override
	public Object processElement(Object framedElement, Method method,
			Object[] arguments, GremlinGroovy annotation,
			FramedGraph<?> framedGraph, Element element) {
		if (element instanceof Vertex) {
            return processVertex(annotation, method, arguments, framedGraph, (Vertex) element);
        } else {
            throw new UnsupportedOperationException("This method only works for vertices");
        }
	}


} public interface Person extends NamedObject {
    enum Gender {FEMALE, MALE};

    @Property("age")
    public Integer getAge();

    @Property("age")
    public void setAge(Integer age);

    @Property("age")
    public void removeAge();

    @Property("gender")
    public void setGender(Gender gender);

    @Property("gender")
    public Gender getGender();

    @Property("gender")
    public void removeGender();

    @Incidence(label = "knows")
    public Iterable<Knows> getKnows();

    @Adjacency(label = "knows")
    public Iterable<Person> getKnowsPeople();

    @Adjacency(label = "knows")
    public void setKnowsPeople(final Iterable<Person> knows);

    @Incidence(label = "created")
    public Iterable<Created> getCreated();
    
    @Incidence(label = "created")
    public Iterable<CreatedInfo> getCreatedInfo();

    @Adjacency(label = "created")
    public Iterable<Project> getCreatedProjects();

    @Adjacency(label = "knows")
    public void addKnowsPerson(final Person person);

    @Adjacency(label = "knows")
    public Person addKnowsNewPerson();

    @Incidence(label = "knows")
    public Knows addKnows(final Person person);

    @Adjacency(label = "created")
    public void addCreatedProject(final Project project);

    @Incidence(label = "created")
    public Created addCreated(final Project project);
    
    @Incidence(label = "created")
    public CreatedInfo addCreatedInfo(final Project project);

    @Adjacency(label = "knows")
    public void removeKnowsPerson(final Person person);

    @Incidence(label = "knows")
    public void removeKnows(final Knows knows);

    @Adjacency(label = "latestProject")
    public Project getLatestProject();

    @Adjacency(label = "latestProject")
    public void setLatestProject(final Project latestProject);

    @GremlinGroovy("it.as('x').out('created').in('created').except('x')")
    public Iterable<Person> getCoCreators();

    @GremlinGroovy("_().as('x').out('created').in('created').except('x').shuffle")
    public Person getRandomCoCreators();

    @GremlinGroovy("_().as('x').out('created').in('created').except('x').has('age',age)")
    public Person getCoCreatorOfAge(@GremlinParam("age") int age);

    @GremlinGroovy(value = "'aStringProperty'", frame = false)
    public String getAStringProperty();

    @GremlinGroovy(value = "['a','b','c']", frame = false)
    public Iterable<String> getListOfStrings();

    @GremlinGroovy("it.as('x').out('created').in('created').except('x').groupCount.cap.next()")
    public Map<Person, Long> getRankedCoauthors();
    
    @GremlinGroovy("person.asVertex().in('knows')")
    public Iterable<Person> getKnownRootedFromParam(@GremlinParam("person") Person person);

    @Deprecated
    @GremlinGroovy("_().out('knows')")
    public Iterable<Person> getDeprecatedKnowsPeople();

    @Property("boolean")
    public void setBoolean(boolean b);

    @Property("boolean")
    public boolean isBooleanPrimitive();

    @Property("boolean")
    public Boolean isBoolean();

    @Property("boolean")
    public boolean canBooleanPrimitive();

    @Property("boolean")
    public Boolean canBoolean();
    
    @Adjacency(label = "knows", direction=Direction.BOTH)
    public void addKnowsPersonDirectionBothError(final Person person);
    
    @Adjacency(label = "knows", direction=Direction.BOTH)
    public void setKnowsPersonDirectionBothError(final Person person);
    
    @Incidence(label = "created", direction=Direction.BOTH)
    public Created addCreatedDirectionBothError(final Project project);
    
    @JavaHandler
    public String getNameAndAge();
    
    @JavaHandler
    public Iterable<Person> getCoCreatorsJava();
    
    @JavaHandler
    public void notImplemented();
    
    
    abstract class Impl implements JavaHandlerContext<Vertex>, Person {

    	@Override
    	@JavaHandler
    	public String getNameAndAge() {
    		return getName() + " (" + getAge() + ")";
    	}
    	
    	@Override
    	@JavaHandler
    	public Iterable<Person> getCoCreatorsJava() {
    		return frameVertices(gremlin().as("x").out("created").in("created").except("x"));
    		
    	}
    }


	public void unhandledNoAnnotation();

	@GremlinGroovy("")
	public void unhandledNoHandler();
	
	

} @JavaHandlerClass(ProjectImpl.class)
public interface Project extends NamedObject {

    @Property("lang")
    public String getLanguage();

    @Adjacency(label = "created", direction = Direction.IN)
    public Iterable<Person> getCreatedByPeople();

    @Incidence(label = "created", direction = Direction.IN)
    public Iterable<CreatedBy> getCreatedBy();
    
    @Incidence(label = "created", direction = Direction.IN)
    public Iterable<CreatedInfo> getCreatedInfo();

    @Adjacency(label = "created", direction = Direction.IN)
    public void removeCreatedByPerson(Person person);

    @Incidence(label = "created", direction = Direction.IN)
    public void removeCreatedBy(CreatedBy createdBy);
    
    @Incidence(label = "created", direction = Direction.IN)
    public void removeCreatedInfo(CreatedInfo createdBy);
    
    @Incidence(label = "created", direction = Direction.IN)
    public CreatedBy addCreatedByPersonIncidence(Person person);
    
    @Incidence(label = "created", direction = Direction.IN)
    public CreatedInfo addCreatedByPersonInfo(Person person);
    
    @Incidence(label = "created", direction = Direction.IN)
    public CreatedBy addCreatedInfo(CreatedInfo person);

    @Adjacency(label = "created", direction = Direction.IN)
    public void addCreatedByPersonAdjacency(Person person);
    
    @JavaHandler
    public String getLanguageUsingMixin();
    
} public interface Knows {

    @Property("weight")
    public Float getWeight();

    @Property("weight")
    public Float setWeight(float weight);

    @OutVertex
    public Person getOut();


    @InVertex
    public Person getIn();

    @Domain
    public Person getDomain();
    
    @Range
    public Person getRange();
    
    @JavaHandler
    public String getNames();

    abstract class Impl implements Knows, JavaHandlerContext<Edge> {

    	@Override
    	@JavaHandler
    	public String getNames() {
    		return getDomain().getName() + "<->" + getRange().getName();
    	}
    }


}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import org.junit.Test;
import junit.framework.Assert;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.EdgeTestSuite;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.GraphTestSuite;
import com.tinkerpop.blueprints.TestSuite;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.VertexTestSuite;
import com.tinkerpop.blueprints.impls.GraphTest;
import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory;
import com.tinkerpop.blueprints.util.io.gml.GMLReaderTestSuite;
import com.tinkerpop.blueprints.util.io.graphml.GraphMLReaderTestSuite;
import com.tinkerpop.blueprints.util.io.graphson.GraphSONReaderTestSuite;
import com.tinkerpop.frames.annotations.AdjacencyAnnotationHandler;
import com.tinkerpop.frames.annotations.AnnotationHandler;
import com.tinkerpop.frames.annotations.DomainAnnotationHandler;
import com.tinkerpop.frames.annotations.InVertexAnnotationHandler;
import com.tinkerpop.frames.annotations.IncidenceAnnotationHandler;
import com.tinkerpop.frames.annotations.OutVertexAnnotationHandler;
import com.tinkerpop.frames.annotations.PropertyAnnotationHandler;
import com.tinkerpop.frames.annotations.RangeAnnotationHandler;
import com.tinkerpop.frames.annotations.gremlin.GremlinGroovyAnnotationHandler;
import com.tinkerpop.frames.domain.classes.Person;
import com.tinkerpop.frames.domain.classes.Project;
import com.tinkerpop.frames.domain.incidences.Knows;
package com.tinkerpop.frames;






/**
 * @author Marko A. Rodriguez (http://markorodriguez.com)
 */
public class FramedGraphTest extends GraphTest {

    public void testDeprecatedAnnotationUnregister() {
        Graph graph = TinkerGraphFactory.createTinkerGraph();
        FramedGraph<Graph> framedGraph = new FramedGraph<Graph>(graph);

        int counter = framedGraph.getAnnotationHandlers().size();
        for (AnnotationHandler a : new HashSet<AnnotationHandler>(framedGraph.getAnnotationHandlers())) {
            assertTrue(framedGraph.hasAnnotationHandler(a.getAnnotationType()));
            counter--;
            framedGraph.unregisterAnnotationHandler(a.getAnnotationType());
            assertEquals(framedGraph.getAnnotationHandlers().size(), counter);

        }
        assertEquals(framedGraph.getAnnotationHandlers().size(), 0);
    }
    
    
	public void testDeprecatedConfigContainsCoreAnnotationHandlers() {
        Graph graph = TinkerGraphFactory.createTinkerGraph();
        FramedGraph<Graph> framedGraph = new FramedGraph<Graph>(graph);
        Collection<Class<?>> collections = Collections2.transform(framedGraph.getAnnotationHandlers(), new Function<AnnotationHandler<? extends Annotation>, Class<?>>() {
			@Override
			public Class<?> apply(AnnotationHandler<? extends Annotation> handler) {
				return handler.getClass();
			}
        });
        Assert.assertTrue(collections.containsAll(Arrays.asList(PropertyAnnotationHandler.class, 
        		AdjacencyAnnotationHandler.class, 
        		IncidenceAnnotationHandler.class, 