Unverified Commit 391d2a60 authored by Jeffrey Phillips Freeman's avatar Jeffrey Phillips Freeman 💥
Browse files

feat: improved AnnotationFrameFactory to be useful for subclassing.

ISSUES CLOSED: #22
parent 6adf8a64
......@@ -23,6 +23,10 @@
* Removed the upper bound on traversal generics. The travese function's argument has changed from
`GraphTraversal<? extends Vertex, ? extends Vertex>` to `GraphTraversal<Vertex, Vertex>`.
* Expanded documentation to cover all the annotations availible in greater detail, including recent changes.
* `AnnotationFrameFactory` was redesigned to make it easier to inhereit from the class and add support for additional
custom annotations.
* `AbstractAnnotationFrameFactory` was created to help create custom annotations that replace the existing standard
annotations provided by `AnnotationFrameFactory`.
* Updated the following dependencies
* gson: 2.8.1 -> 2.8.2
......
/**
* Copyright 2004 - 2016 Syncleus, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncleus.ferma.framefactories.annotation;
import com.syncleus.ferma.*;
import com.syncleus.ferma.framefactories.FrameFactory;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.FieldManifestation;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class AbstractAnnotationFrameFactory implements FrameFactory {
protected final Map<Class<? extends Annotation>, MethodHandler> methodHandlers = new HashMap<>();
private final ReflectionCache reflectionCache;
private final Map<Class, Class> constructedClassCache = new HashMap<>();
public AbstractAnnotationFrameFactory(final ReflectionCache reflectionCache, Set<MethodHandler> handlers) {
this.reflectionCache = reflectionCache;
for(MethodHandler handler : handlers)
this.methodHandlers.put(handler.getAnnotationType(), handler);
}
private static boolean isAbstract(final Class<?> clazz) {
return Modifier.isAbstract(clazz.getModifiers());
}
private static boolean isAbstract(final Method method) {
return Modifier.isAbstract(method.getModifiers());
}
@Override
public <T> T create(final Element e, final Class<T> kind) {
Class<? extends T> resolvedKind = kind;
if (isAbstract(resolvedKind))
resolvedKind = constructClass(e, kind);
try {
final T object = resolvedKind.newInstance();
if (object instanceof CachesReflection)
((CachesReflection) object).setReflectionCache(this.reflectionCache);
return object;
}
catch (final InstantiationException | IllegalAccessException caught) {
throw new IllegalArgumentException("kind could not be instantiated", caught);
}
}
private <E> Class<? extends E> constructClass(final Element element, final Class<E> clazz) {
Class constructedClass = constructedClassCache.get(clazz);
if (constructedClass != null)
return constructedClass;
DynamicType.Builder<? extends E> classBuilder;
if (clazz.isInterface())
if (element instanceof Vertex)
classBuilder = (DynamicType.Builder<? extends E>) new ByteBuddy().subclass(AbstractVertexFrame.class).implement(clazz);
else if (element instanceof Edge)
classBuilder = (DynamicType.Builder<? extends E>) new ByteBuddy().subclass(AbstractEdgeFrame.class).implement(clazz);
else
throw new IllegalStateException("class is neither an Edge or a vertex!");
else {
if (element instanceof Vertex && !VertexFrame.class.isAssignableFrom(clazz))
throw new IllegalStateException(clazz.getName() + " Class is not a type of VertexFrame");
if (element instanceof Edge && !EdgeFrame.class.isAssignableFrom(clazz))
throw new IllegalStateException(clazz.getName() + " Class is not a type of EdgeFrame");
classBuilder = new ByteBuddy().subclass(clazz);
}
classBuilder = classBuilder.defineField("reflectionCache", ReflectionCache.class, Visibility.PRIVATE, FieldManifestation.PLAIN).implement(CachesReflection.class).intercept(FieldAccessor.
ofBeanProperty());
//try and construct any abstract methods that are left
for (final Method method : clazz.getMethods())
if (isAbstract(method))
annotation_loop:
for (final Annotation annotation : method.getAnnotations()) {
final MethodHandler handler = methodHandlers.get(annotation.annotationType());
if (handler != null) {
classBuilder = handler.processMethod(classBuilder, method, annotation);
break;
}
}
constructedClass = classBuilder.make().load(AnnotationFrameFactory.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();
this.constructedClassCache.put(clazz, constructedClass);
return constructedClass;
}
}
/**
* Copyright 2004 - 2016 Syncleus, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncleus.ferma.framefactories.annotation;
public abstract class AbstractMethodHandler implements MethodHandler {
@Override
public int hashCode() {
return this.getAnnotationType().hashCode();
}
@Override
public boolean equals(Object obj) {
return this.getAnnotationType().equals(obj);
}
}
......@@ -41,7 +41,7 @@ import java.util.function.Consumer;
*
* @since 2.0.0
*/
public class AdjacencyMethodHandler implements MethodHandler {
public class AdjacencyMethodHandler extends AbstractMethodHandler {
@Override
public Class<Adjacency> getAnnotationType() {
......
......@@ -32,94 +32,44 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
public class AnnotationFrameFactory implements FrameFactory {
private final Map<Class<? extends Annotation>, MethodHandler> methodHandlers = new HashMap<>();
private final Map<Class, Class> constructedClassCache = new HashMap<>();
private final ReflectionCache reflectionCache;
public class AnnotationFrameFactory extends AbstractAnnotationFrameFactory {
public AnnotationFrameFactory(final ReflectionCache reflectionCache) {
this.reflectionCache = reflectionCache;
super(reflectionCache, collectHandlers(null));
}
/**
* Subclasses can use this constructor to add additional custom method handlers.
*
* @param reflectionCache The reflection cache used to inspect annotations.
* @param handlers The handlers used to generate new annotation support.
*/
protected AnnotationFrameFactory(final ReflectionCache reflectionCache, Set<MethodHandler> handlers) {
super(reflectionCache, collectHandlers(handlers));
}
private static final Set<MethodHandler> collectHandlers(Set<MethodHandler> additionalHandlers) {
final Set<MethodHandler> methodHandlers = new HashSet<>();
final PropertyMethodHandler propertyHandler = new PropertyMethodHandler();
methodHandlers.put(propertyHandler.getAnnotationType(), propertyHandler);
methodHandlers.add(propertyHandler);
final InVertexMethodHandler inVertexHandler = new InVertexMethodHandler();
methodHandlers.put(inVertexHandler.getAnnotationType(), inVertexHandler);
methodHandlers.add(inVertexHandler);
final OutVertexMethodHandler outVertexHandler = new OutVertexMethodHandler();
methodHandlers.put(outVertexHandler.getAnnotationType(), outVertexHandler);
methodHandlers.add(outVertexHandler);
final AdjacencyMethodHandler adjacencyHandler = new AdjacencyMethodHandler();
methodHandlers.put(adjacencyHandler.getAnnotationType(), adjacencyHandler);
methodHandlers.add(adjacencyHandler);
final IncidenceMethodHandler incidenceHandler = new IncidenceMethodHandler();
methodHandlers.put(incidenceHandler.getAnnotationType(), incidenceHandler);
}
methodHandlers.add(incidenceHandler);
@Override
public <T> T create(final Element e, final Class<T> kind) {
Class<? extends T> resolvedKind = kind;
if (isAbstract(resolvedKind))
resolvedKind = constructClass(e, kind);
try {
final T object = resolvedKind.newInstance();
if (object instanceof CachesReflection)
((CachesReflection) object).setReflectionCache(this.reflectionCache);
return object;
}
catch (final InstantiationException | IllegalAccessException caught) {
throw new IllegalArgumentException("kind could not be instantiated", caught);
}
}
if(additionalHandlers != null)
methodHandlers.addAll(additionalHandlers);
private static boolean isAbstract(final Class<?> clazz) {
return Modifier.isAbstract(clazz.getModifiers());
return methodHandlers;
}
private static boolean isAbstract(final Method method) {
return Modifier.isAbstract(method.getModifiers());
}
private <E> Class<? extends E> constructClass(final Element element, final Class<E> clazz) {
Class constructedClass = constructedClassCache.get(clazz);
if (constructedClass != null)
return constructedClass;
DynamicType.Builder<? extends E> classBuilder;
if (clazz.isInterface())
if (element instanceof Vertex)
classBuilder = (DynamicType.Builder<? extends E>) new ByteBuddy().subclass(AbstractVertexFrame.class).implement(clazz);
else if (element instanceof Edge)
classBuilder = (DynamicType.Builder<? extends E>) new ByteBuddy().subclass(AbstractEdgeFrame.class).implement(clazz);
else
throw new IllegalStateException("class is neither an Edge or a vertex!");
else {
if (element instanceof Vertex && !VertexFrame.class.isAssignableFrom(clazz))
throw new IllegalStateException(clazz.getName() + " Class is not a type of VertexFrame");
if (element instanceof Edge && !EdgeFrame.class.isAssignableFrom(clazz))
throw new IllegalStateException(clazz.getName() + " Class is not a type of EdgeFrame");
classBuilder = new ByteBuddy().subclass(clazz);
}
classBuilder = classBuilder.defineField("reflectionCache", ReflectionCache.class, Visibility.PRIVATE, FieldManifestation.PLAIN).implement(CachesReflection.class).intercept(FieldAccessor.
ofBeanProperty());
//try and construct any abstract methods that are left
for (final Method method : clazz.getMethods())
if (isAbstract(method))
annotation_loop:
for (final Annotation annotation : method.getAnnotations()) {
final MethodHandler handler = methodHandlers.get(annotation.annotationType());
if (handler != null) {
classBuilder = handler.processMethod(classBuilder, method, annotation);
break;
}
}
constructedClass = classBuilder.make().load(AnnotationFrameFactory.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();
this.constructedClassCache.put(clazz, constructedClass);
return constructedClass;
}
}
......@@ -39,7 +39,7 @@ import javax.annotation.Nullable;
*
* @since 2.0.0
*/
public class InVertexMethodHandler implements MethodHandler {
public class InVertexMethodHandler extends AbstractMethodHandler {
@Override
public Class<InVertex> getAnnotationType() {
......
......@@ -38,7 +38,7 @@ import org.apache.tinkerpop.gremlin.structure.Direction;
*
* @since 2.0.0
*/
public class IncidenceMethodHandler implements MethodHandler {
public class IncidenceMethodHandler extends AbstractMethodHandler {
@Override
public Class<Incidence> getAnnotationType() {
......
......@@ -39,7 +39,7 @@ import javax.annotation.Nullable;
*
* @since 2.0.0
*/
public class OutVertexMethodHandler implements MethodHandler {
public class OutVertexMethodHandler extends AbstractMethodHandler {
@Override
public Class<OutVertex> getAnnotationType() {
......
......@@ -32,7 +32,7 @@ import net.bytebuddy.matcher.ElementMatchers;
*
* @since 2.0.0
*/
public class PropertyMethodHandler implements MethodHandler {
public class PropertyMethodHandler extends AbstractMethodHandler {
@Override
public Class<Property> getAnnotationType() {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment