org.nakedobjects.metamodel.specloader.NakedObjectReflectorAbstract Maven / Gradle / Ivy
package org.nakedobjects.metamodel.specloader;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.nakedobjects.metamodel.commons.ensure.Ensure.ensureThatArg;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.NakedObjectList;
import org.nakedobjects.metamodel.commons.debug.DebugInfo;
import org.nakedobjects.metamodel.commons.debug.DebugString;
import org.nakedobjects.metamodel.commons.ensure.Assert;
import org.nakedobjects.metamodel.commons.exceptions.NakedObjectException;
import org.nakedobjects.metamodel.commons.lang.JavaClassUtils;
import org.nakedobjects.metamodel.config.NakedObjectConfiguration;
import org.nakedobjects.metamodel.facetdecorator.FacetDecorator;
import org.nakedobjects.metamodel.facetdecorator.FacetDecoratorSet;
import org.nakedobjects.metamodel.facets.Facet;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContext;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContextAware;
import org.nakedobjects.metamodel.runtimecontext.noruntime.RuntimeContextNoRuntime;
import org.nakedobjects.metamodel.spec.IntrospectableSpecification;
import org.nakedobjects.metamodel.spec.JavaSpecification;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.SpecificationFacets;
import org.nakedobjects.metamodel.specloader.classsubstitutor.ClassSubstitutor;
import org.nakedobjects.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
import org.nakedobjects.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault;
import org.nakedobjects.metamodel.specloader.internal.TypeExtractorMethodReturn;
import org.nakedobjects.metamodel.specloader.internal.cache.SimpleSpecificationCache;
import org.nakedobjects.metamodel.specloader.internal.cache.SpecificationCache;
import org.nakedobjects.metamodel.specloader.internal.facetprocessor.FacetProcessor;
import org.nakedobjects.metamodel.specloader.internal.instances.InstanceCollectionSpecification;
import org.nakedobjects.metamodel.specloader.progmodelfacets.ProgrammingModelFacets;
import org.nakedobjects.metamodel.specloader.progmodelfacets.ProgrammingModelFacetsJava5;
/**
* Builds the meta-model for Java 5 programming model.
*
*
* The implementation provides for a degree of pluggability:
*
* - The most important plug-in point is {@link ProgrammingModelFacets} that
* specifies the set of {@link Facet} that make up programming model. If not
* specified then defaults to {@link ProgrammingModelFacetsJava5} (which should
* be used as a starting point for your own customizations).
*
- The only mandatory plug-in point is {@link ClassSubstitutor}, which
* allows the class to be loaded to be substituted if required. This is used in
* conjunction with some PersistenceMechanisms that do class
* enhancement.
*
- The {@link CollectionTypeRegistry} specifies the types that should be
* considered as collections. If not specified then will
* {@link CollectionTypeRegistryDefault default}. (Note: this extension point
* has not been tested, so should be considered more of a "statement of
* intent" than actual API. Also, we may use annotations (similar to the
* way in which Values are specified) as an alternative mechanism).
*
*
*
* In addition, the {@link RuntimeContext} can optionally be injected, but will
* default to {@link RuntimeContextNoRuntime} if not provided prior to
* {@link #init() initialization}. The purpose of {@link RuntimeContext} is to
* allow the metamodel to be used standalone, for example in a Maven plugin. The
* {@link RuntimeContextNoRuntime} implementation will through an exception for
* any methods (such as finding an {@link NakedObject adapter}) because there is
* no runtime session. In the case of the metamodel being used by the framework
* (that is, when there is a runtime), then the framework injects an
* implementation of {@link RuntimeContext} that acts like a bridge to its
* NakedObjectsContext.
*/
public abstract class NakedObjectReflectorAbstract implements
NakedObjectReflector, DebugInfo {
private final static Logger LOG = Logger
.getLogger(NakedObjectReflectorAbstract.class);
/**
* Injected in the constructor.
*/
private final NakedObjectConfiguration configuration;
/**
* Injected in the constructor.
*/
private final ClassSubstitutor classSubstitutor;
/**
* Injected in the constructor.
*/
private final CollectionTypeRegistry collectionTypeRegistry;
/**
* Injected in the constructor.
*/
private final ProgrammingModelFacets programmingModelFacets;
/**
* Defaulted in the constructor.
*/
private final FacetProcessor facetProcessor;
/**
* Defaulted in the constructor.
*/
private final SpecificationCache cache;
/**
* Defaulted in the constructor, so can be added to via
* {@link #setFacetDecorators(FacetDecoratorSet)} or
* {@link #addFacetDecorator(FacetDecorator)}.
*
*
* {@link FacetDecorator}s must be added prior to {@link #init()
* initialization.}
*/
private final FacetDecoratorSet facetDecoratorSet;
/**
* Can optionally be injected, but will default (to
* {@link RuntimeContextNoRuntime}) otherwise.
*
*
* Should be injected when used by framework, but will default to a no-op
* implementation if the metamodel is being used standalone (eg for a
* code-generator).
*/
private RuntimeContext runtimeContext;
// /////////////////////////////////////////////////////////////
// Constructor
// /////////////////////////////////////////////////////////////
public NakedObjectReflectorAbstract(
final NakedObjectConfiguration configuration,
final ClassSubstitutor classSubstitutor,
final CollectionTypeRegistry collectionTypeRegistry,
final ProgrammingModelFacets programmingModelFacets,
final Set facetDecorators) {
ensureThatArg(configuration, is(notNullValue()));
ensureThatArg(classSubstitutor, is(notNullValue()));
ensureThatArg(collectionTypeRegistry, is(notNullValue()));
ensureThatArg(programmingModelFacets, is(notNullValue()));
ensureThatArg(facetDecorators, is(notNullValue()));
this.configuration = configuration;
this.classSubstitutor = classSubstitutor;
this.collectionTypeRegistry = collectionTypeRegistry;
this.programmingModelFacets = programmingModelFacets;
this.facetDecoratorSet = new FacetDecoratorSet();
for (final FacetDecorator facetDecorator : facetDecorators) {
this.facetDecoratorSet.add(facetDecorator);
}
this.facetProcessor = new FacetProcessor(configuration, this,
collectionTypeRegistry, programmingModelFacets);
this.cache = new SimpleSpecificationCache();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
LOG.info("finalizing reflector factory " + this);
}
// /////////////////////////////////////////////////////////////
// init, shutdown
// /////////////////////////////////////////////////////////////
/**
* Initializes and wires up
*/
public void init() {
LOG.debug("initialising " + this);
// default subcomponents
if (runtimeContext == null) {
runtimeContext = new RuntimeContextNoRuntime();
}
injectInto(runtimeContext);
// wire subcomponents into each other
runtimeContext.injectInto(facetProcessor);
// initialize subcomponents
facetDecoratorSet.init();
classSubstitutor.init();
collectionTypeRegistry.init();
facetProcessor.init();
}
public void shutdown() {
LOG.info("shutting down " + this);
getCache().clear();
facetDecoratorSet.shutdown();
}
// /////////////////////////////////////////////////////////////
// install, load, allSpecifications
// /////////////////////////////////////////////////////////////
public NakedObjectSpecification loadSpecification(final Class> type) {
Class> substitutedType = getClassSubstitutor().getClass(type);
return substitutedType != null ? loadSpecificationFromCache(substitutedType)
: null;
}
private NakedObjectSpecification loadSpecificationFromCache(
final Class> type) {
Assert.assertNotNull(type);
final NakedObjectSpecification spec = getCache().get(type.getName());
if (spec != null) {
return spec;
}
return loadSpecificationAndCache(type);
}
private NakedObjectSpecification loadSpecificationAndCache(
final Class> type) {
final SpecificationCache specificationCache = getCache();
synchronized (specificationCache) {
NakedObjectSpecification specification = createSpecification(type);
if (specification == null) {
throw new NakedObjectException(
"Failed to create specification for class "
+ type.getName());
}
specificationCache.cache(type.getName(), specification);
if (specification instanceof IntrospectableSpecification) {
final IntrospectableSpecification introspectableSpecification = (IntrospectableSpecification) specification;
introspectableSpecification.introspect(facetDecoratorSet);
}
return specification;
}
}
// /////////////////////////////////////////////////////////////
// loadSpecificationForReturnTypes
// (not API)
// /////////////////////////////////////////////////////////////
/**
* Loads the specifications of the return types of all {@link Method}s in
* the provided {@link Vector}.
*
*
* If the provided {@link Class} is non-null, then it will not
* loaded. This prevents us going into an infinite loop.
*
* @param methods
* - the methods whose return types are to be ignored
* @param classToIgnore
* - ignore this class if is a return type (higher up the stack)
* @param objectFactory
*/
public void loadSpecificationForReturnTypes(final Set methods,
final Class> classToIgnore) {
for (final Method method : methods) {
for (Class> returnType : new TypeExtractorMethodReturn(method)) {
if (returnType != classToIgnore) {
loadSpecification(returnType);
}
}
}
}
/**
* Overridable method for language-specific subclass to create the
* appropriate type of {@link NakedObjectSpecification}.
*/
protected NakedObjectSpecification createSpecification(final Class> cls) {
if (NakedObjectList.class.isAssignableFrom(cls)) {
return new InstanceCollectionSpecification(this,
getRuntimeContext());
}
return new JavaSpecification(cls, this, getRuntimeContext());
}
/**
* Return the specification for the specified class of object.
*/
public final NakedObjectSpecification loadSpecification(
final String className) {
ensureThatArg(className, is(notNullValue()),
"specification class name must be specified");
try {
final Class> cls = loadClass(className);
return loadSpecification(cls);
} catch (final ClassNotFoundException e) {
final NakedObjectSpecification spec = getCache().get(className);
if (spec == null) {
throw new NakedObjectException(
"No such class available: " + className);
}
return spec;
}
}
private Class> loadClass(final String className)
throws ClassNotFoundException {
Class> builtIn = JavaClassUtils.getBuiltIn(className);
if (builtIn != null) {
return builtIn;
}
return Class.forName(className);
}
/**
* Return all the loaded specifications.
*/
public NakedObjectSpecification[] allSpecifications() {
return getCache().allSpecifications();
}
public boolean loaded(Class> cls) {
return loaded(cls.getName());
}
public boolean loaded(String fullyQualifiedClassName) {
return getCache().get(fullyQualifiedClassName) != null;
}
// ////////////////////////////////////////////////////////////////////
// injectInto
// ////////////////////////////////////////////////////////////////////
/**
* Injects self into candidate if required, and instructs its subcomponents
* to do so also.
*/
public void injectInto(Object candidate) {
Class> candidateClass = candidate.getClass();
if (SpecificationLoaderAware.class.isAssignableFrom(candidateClass)) {
SpecificationLoaderAware cast = SpecificationLoaderAware.class
.cast(candidate);
cast.setSpecificationLoader(this);
}
getClassSubstitutor().injectInto(candidate);
getCollectionTypeRegistry().injectInto(candidate);
}
// /////////////////////////////////////////////////////////////
// Debugging
// /////////////////////////////////////////////////////////////
public void debugData(final DebugString str) {
facetDecoratorSet.debugData(str);
str.appendln();
str.appendTitle("Specifications");
final NakedObjectSpecification[] specs = allSpecifications();
Arrays.sort(specs, new Comparator() {
public int compare(final NakedObjectSpecification s1,
final NakedObjectSpecification s2) {
return s1.getShortName().compareToIgnoreCase(s2.getShortName());
}
});
for (int i = 0; i < specs.length; i++) {
final NakedObjectSpecification specification = specs[i];
str.append(specification.isAbstract() ? "A" : ".");
str.append(specification.isService() ? "S" : ".");
str.append(SpecificationFacets.isBoundedSet(specification) ? "B"
: ".");
str.append(specification.isCollection() ? "C" : ".");
str.append(specification.isObject() ? "O" : ".");
str.append("."); // placeholder for future support of maps
str.append(specification.isParseable() ? "P" : ".");
str.append(specification.isEncodeable() ? "E" : ".");
str.append(specification.isAggregated() ? "A" : ".");
str.append(!specification.isMutableAggregated() ? "I" : ".");
str.append(" ");
str.append(specification.getShortName());
str.append(" [fqc=");
str.append(specification.getFullName());
str.append(",type=");
str.append(specification.getClass().getName());
str.appendln("]");
}
}
public String debugTitle() {
return "Reflector";
}
// /////////////////////////////////////////////////////////////
// Helpers (were previously injected, but no longer required)
// /////////////////////////////////////////////////////////////
/**
* Provides access to the registered {@link Facet}s.
*/
public FacetProcessor getFacetProcessor() {
return facetProcessor;
}
private SpecificationCache getCache() {
return cache;
}
// ////////////////////////////////////////////////////////////////////
// Dependencies (injected by setter due to *Aware)
// ////////////////////////////////////////////////////////////////////
/**
* As per {@link #setRuntimeContext(RuntimeContext)}.
*/
public RuntimeContext getRuntimeContext() {
return runtimeContext;
}
/**
* Due to {@link RuntimeContextAware}.
*/
public void setRuntimeContext(RuntimeContext runtimeContext) {
this.runtimeContext = runtimeContext;
}
// ////////////////////////////////////////////////////////////////////
// Dependencies (injected from constructor)
// ////////////////////////////////////////////////////////////////////
public NakedObjectConfiguration getNakedObjectConfiguration() {
return configuration;
}
public ClassSubstitutor getClassSubstitutor() {
return classSubstitutor;
}
public CollectionTypeRegistry getCollectionTypeRegistry() {
return collectionTypeRegistry;
}
public ProgrammingModelFacets getProgrammingModelFacets() {
return programmingModelFacets;
}
public Set getFacetDecoratorSet() {
return facetDecoratorSet.getFacetDecorators();
}
}
// Copyright (c) Naked Objects Group Ltd.