org.openrdf.repository.object.composition.AbstractClassFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alibaba-composition-object Show documentation
Show all versions of alibaba-composition-object Show documentation
The Object Composition library merges multiple Java objects into a single multi-subject object.
/*
* Copyright (c) 2009-2010, James Leigh and Zepheira LLC Some rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the openrdf.org nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.openrdf.repository.object.composition;
import static java.lang.reflect.Modifier.isAbstract;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isProtected;
import static org.openrdf.repository.object.traits.RDFObjectBehaviour.GET_ENTITY_METHOD;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openrdf.repository.object.composition.helpers.BehaviourConstructor;
import org.openrdf.repository.object.exceptions.ObjectCompositionException;
import org.openrdf.repository.object.managers.PropertyMapper;
import org.openrdf.repository.object.traits.RDFObjectBehaviour;
/**
* Creates subclasses of abstract behaviours that can be instaniated.
*
* @author James Leigh
*
*/
public class AbstractClassFactory implements BehaviourProvider {
private static final String BEAN_FIELD_NAME = "_$bean";
private static final String CLASS_PREFIX = "object.behaviours.";
private ClassFactory cp;
private Set> bases;
public void setClassDefiner(ClassFactory definer) {
this.cp = definer;
}
public void setBaseClasses(Set> bases) {
this.bases = bases;
}
public void setPropertyMapper(PropertyMapper mapper) {
// don't need it
}
public Collection extends BehaviourFactory> getBehaviourFactories(
Collection> classes) throws ObjectCompositionException {
Set> faces = new HashSet>();
for (Class> i : classes) {
faces.add(i);
faces = getImplementingClasses(i, faces);
}
List result = new ArrayList(faces.size());
for (Class> concept : faces) {
result.addAll(getBehaviourFactories(concept));
}
return result;
}
private Collection getBehaviourFactories(Class> concept) throws ObjectCompositionException {
try {
List result = new ArrayList();
if (isEnhanceable(concept)) {
for (Class> mapper : findImplementations(concept)) {
result.add(new BehaviourConstructor(mapper));
}
}
return result;
} catch (ObjectCompositionException e) {
throw e;
} catch (Exception e) {
throw new ObjectCompositionException(e);
}
}
private Collection extends Class>> findImplementations(
Class> concept) throws Exception {
return Collections.singleton(findBehaviour(concept));
}
private boolean isBaseClass(Class> role) {
return bases != null && bases.contains(role);
}
private final Class> findBehaviour(Class> concept) throws Exception {
String className = getJavaClassName(concept);
synchronized (cp) {
try {
return cp.classForName(className);
} catch (ClassNotFoundException e2) {
return implement(className, concept);
}
}
}
private ClassTemplate createBehaviourTemplate(String className,
Class> concept) {
ClassTemplate cc = createClassTemplate(className, concept);
cc.addInterface(RDFObjectBehaviour.class);
addNewConstructor(cc, concept);
addRDFObjectBehaviourMethod(cc);
return cc;
}
private boolean isOverridden(Method m) {
if (m.getParameterTypes().length > 0)
return false;
if (RDFObjectBehaviour.GET_ENTITY_METHOD.equals(m.getName()))
return true;
return false;
}
private String getJavaClassName(Class> concept) {
String suffix = getClass().getSimpleName().replaceAll("Factory$", "");
return CLASS_PREFIX + concept.getName() + suffix;
}
private Class> implement(String className, Class> concept)
throws Exception {
ClassTemplate cc = createBehaviourTemplate(className, concept);
enhance(cc, concept);
return cp.createClass(cc);
}
private void addNewConstructor(ClassTemplate cc, Class> concept) {
if (!concept.isInterface()) {
try {
concept.getConstructor(); // must have a default constructor
} catch (NoSuchMethodException e) {
throw new ObjectCompositionException(concept.getSimpleName()
+ " must have a default constructor");
}
}
cc.createField(Object.class, BEAN_FIELD_NAME);
cc.addConstructor(new Class>[] { Object.class },
BEAN_FIELD_NAME + " = $1;");
}
private void addRDFObjectBehaviourMethod(ClassTemplate cc) {
cc.createMethod(Object.class,
RDFObjectBehaviour.GET_ENTITY_METHOD).code("return ").code(
BEAN_FIELD_NAME).code(";").end();
}
private Set> getImplementingClasses(Class> role,
Set> implementations) {
// don't consider interfaces or super classes
return implementations;
}
private ClassTemplate createClassTemplate(String className, Class> role) {
ClassTemplate cc = cp.createClassTemplate(className, role);
cc.copyAnnotationsFrom(role);
return cc;
}
private boolean isEnhanceable(Class> role) {
return !role.isInterface() && isAbstract(role.getModifiers())
&& !isBaseClass(role);
}
private void enhance(ClassTemplate cc, Class> c) throws Exception {
if (Object.class.equals(c.getMethod("toString").getDeclaringClass())) {
overrideToStringMethod(cc);
}
if (Object.class.equals(c.getMethod("equals", Object.class)
.getDeclaringClass())
&& Object.class.equals(c.getMethod("hashCode")
.getDeclaringClass())) {
overrideEqualsMethod(cc);
}
for (Method m : getMethods(c)) {
if (isFinal(m.getModifiers()))
continue;
if (!isAbstract(m.getModifiers()))
continue;
if (isOverridden(m))
continue;
Class> r = m.getReturnType();
Class>[] types = m.getParameterTypes();
CodeBuilder code = cc.createInstancePrivateMethod(m);
boolean isInterface = m.getDeclaringClass().isInterface();
if (!isInterface) {
code.code("try {");
}
if (!Void.TYPE.equals(r)) {
code.code("return ($r) ");
}
if (isInterface) {
code.code("(").castObject(m.getDeclaringClass()).code(BEAN_FIELD_NAME);
code.code(").").code(m.getName()).code("($$);");
} else {
code.code(BEAN_FIELD_NAME).code(".getClass().getMethod(");
code.insert(m.getName()).code(", ").insert(types).code(")")
.code(".invoke(");
code.code(BEAN_FIELD_NAME).code(", $args);");
}
if (!isInterface) {
code.code("} catch (").code(
InvocationTargetException.class.getName());
code.code(" e) {throw e.getCause();}");
}
code.end();
}
}
private Collection getMethods(Class> c) {
List methods = new ArrayList();
methods.addAll(Arrays.asList(c.getMethods()));
HashMap