源代码如下:
package com.sap.crm.ui.core.pages;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sap.crm.ui.core.tags.ContainerElement;
import com.sap.crm.ui.core.tags.Element;
import com.sap.crm.ui.core.tags.ElementFactory;
import com.sap.crm.ui.core.tags.ParentElement;
import com.sap.crm.ui.core.tags.SubFactory;
import com.sap.crm.ui.core.tags.UISession;
public class AbstractPage {
protected final UISession session;
protected ParentElement container;
public AbstractPage(final UISession session) {
super();
this.session = session;
init();
}
protected void init() {
final List<Field> fields = new ArrayList<Field>();
final List<Field> subFields = new ArrayList<Field>();
Map<Class<? extends SubObject>, Map<String, SubFactory<? extends SubObject>>> subFactories = new HashMap<>();
Page pageAnnotation = this.getClass().getAnnotation(Page.class);
Location defaultLocation = null;
if (pageAnnotation != null) {
defaultLocation = pageAnnotation.defaultLocation();
}
// Retrieve all fields/members that are declared as
// public/protected/private.
for (final Field field : this.getClass().getDeclaredFields()) {
if (!field.isAccessible())
field.setAccessible(true);
final Class<?> type = field.getType();
if (Element.class.isAssignableFrom(type) || type == String.class) {
if (field.getAnnotation(Container.class) != null) {
fields.add(0, field);
} else {
fields.add(field);
}
} else if (AbstractPage.class.isAssignableFrom(type)) {
// element is inheriting from AbstractPage
createSubPage(field, type);
} else if (SubObject.class.isAssignableFrom(type)) {
// subFactory e.g. for table columns
subFields.add(field);
}
}
// Now consider all fields that have a PageElement annotation and create
// the concrete element instances via factory
for (final Field field : fields) {
PageElement pageElement = resolveAnnotations(field);
if (pageElement != null) {
final Class<?> type = field.getType();
if (type == String.class) {
// Just a versioned ID
setField(field, pageElement.id());
continue;
}
// Determine element type
@SuppressWarnings("unchecked")
Class<? extends Element> fieldType = (Class<? extends Element>) type;
if (pageElement.type() != Element.class) {
if (!fieldType.isAssignableFrom(pageElement.type())) {
throw new IllegalArgumentException();
}
fieldType = pageElement.type();
}
// Get factory dependent from location
Element element;
ElementFactory factory = null;
factory = resolveLocation(factory, pageElement, defaultLocation);
// Check if proxy needed
if (ElementFactory.isConcreteType(fieldType)) {
// Concrete type is known, no proxy needed
element = factory.element(fieldType, pageElement.id());
} else {
// Final Type is not known, need a proxy
element = factory.proxy(pageElement.id());
}
if (field.getAnnotation(Container.class) != null) {
container = (ContainerElement) element;
}
setField(field, element);
// Remember subFactories
if (SubFactory.class.isAssignableFrom(fieldType)) {
registerSubFactory(subFactories, field, fieldType, element);
}
}
}
// Instantiate subObject fields from factories
for (Field subField : subFields) {
PageElement pageElement = resolveAnnotations(subField);
// Get factory
Map<String, SubFactory<? extends SubObject>> subFactoriesForType = subFactories
.get(subField.getType());
if (subFactoriesForType == null) {
throw new IllegalArgumentException("Subfactory for type "
+ subField.getType() + " could not be found on page");
}
SubFactory<? extends SubObject> subFactory = subFactoriesForType
.get(pageElement.subFactoryName());
if (subFactory == null) {
throw new IllegalArgumentException("Subfactory for type "
+ subField.getType() + " with name "
+ pageElement.subFactoryName()
+ " could not be found on page");
}
// Get parametrization for generic types
ParameterizedType genericType = (ParameterizedType) subField
.getGenericType();
Type[] actualTypeArguments = genericType.getActualTypeArguments();
// Produce and assign
Object subObject = subFactory.produce(pageElement.id(),
actualTypeArguments);
try {
subField.set(this, subObject);
} catch (final IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
private void setField(final Field field, Object value) {
try {
field.set(this, value);
} catch (final IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private void createSubPage(final Field field, final Class<?> type) {
@SuppressWarnings("unchecked")
final Class<? extends AbstractPage> pageType = (Class<AbstractPage>) type;
// Create sub pages
try {
final Constructor<? extends AbstractPage> constructor = pageType
.getConstructor(UISession.class);
try {
final AbstractPage page = constructor.newInstance(session);
field.set(this, page);
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final InstantiationException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
} catch (final InvocationTargetException e) {
e.printStackTrace();
}
} catch (final NoSuchMethodException e) {
// Ignore it, has to be coded manually
}
}
private ElementFactory resolveLocation(ElementFactory factory,
PageElement pageElement, Location defaultLocation) {
Location location = pageElement.location();
if (location == Location.DEFAULT && defaultLocation != null) {
location = defaultLocation;
}
switch (location) {
case ROOT:
factory = session.ui();
break;
case CONTAINER:
factory = container.contained();
break;
case DEFAULT:
if (container != null) {
factory = container.contained();
} else {
factory = session.workArea();
}
break;
case TOOLBAR:
factory = session.getToolbarFactory();
break;
case WORK_AREA:
factory = session.workArea();
break;
}
return factory;
}
private void registerSubFactory(
Map<Class<? extends SubObject>, Map<String, SubFactory<? extends SubObject>>> subFactories,
final Field field, Class<? extends Element> fieldType,
Element element) {
@SuppressWarnings("unchecked")
SubFactory<? extends SubObject> subFactory = (SubFactory<? extends SubObject>) element;
// Determine Name
com.sap.crm.ui.core.pages.SubFactory annotation = field
.getAnnotation(com.sap.crm.ui.core.pages.SubFactory.class);
String subFactoryName = (annotation != null) ? () : "";
Class<? extends SubObject> targetType = findSubObjectFactoryTargetType(fieldType);
Map<String, SubFactory<? extends SubObject>> subFactoriesForType = subFactories
.get(targetType);
if (subFactoriesForType == null) {
subFactoriesForType = new HashMap<>();
subFactories.put(targetType, subFactoriesForType);
}
if (subFactoriesForType.put(subFactoryName, subFactory) != null) {
throw new IllegalStateException(
"Ambiguous subObjectFactories on page for type "
+ targetType + " with name " + subFactoryName);
}
}
@SuppressWarnings("unchecked")
private Class<? extends SubObject> findSubObjectFactoryTargetType(
Class<? extends Element> fieldType) {
Type[] genericInterfaces = fieldType.getGenericInterfaces();
for (Type intf : genericInterfaces) {
if (intf instanceof ParameterizedType
&& ((ParameterizedType) intf).getRawType() == SubFactory.class) {
Type[] actualTypeArguments = ((ParameterizedType) intf)
.getActualTypeArguments();
Type type = actualTypeArguments[0];
Class<? extends SubObject> targetType;
if (type instanceof ParameterizedType) {
targetType = (Class<? extends SubObject>) ((ParameterizedType) type)
.getRawType();
} else {
targetType = (Class<? extends SubObject>) type;
}
return targetType;
}
}
throw new IllegalArgumentException();
}
private PageElement resolveAnnotations(final Field field) {
// Fetch annotations
final Versioned versionedAnnotation = field
.getAnnotation(Versioned.class);
PageElement pageElement = field.getAnnotation(PageElement.class);
// Process versions
if (versionedAnnotation != null) {
int highestVersion = 0;
for (final Version version : versionedAnnotation.versions()) {
if (version.majorVersion() >= session.getMajorVersion()
&& version.majorVersion() > highestVersion) {
pageElement = version.element();
highestVersion = version.majorVersion();
}
}
}
return pageElement;
}
}