/*******************************************************************************
* Copyright (c) 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.papyrus.xwt.javabean.metadata;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.papyrus.xwt.IEventConstants;
import org.eclipse.papyrus.xwt.IEventGroup;
import org.eclipse.papyrus.xwt.IXWTLoader;
import org.eclipse.papyrus.xwt.XWT;
import org.eclipse.papyrus.xwt.XWTException;
import org.eclipse.papyrus.xwt.XWTMaps;
import org.eclipse.papyrus.xwt.core.IBinding;
import org.eclipse.papyrus.xwt.internal.utils.UserData;
import org.eclipse.papyrus.xwt.javabean.metadata.properties.BeanProperty;
import org.eclipse.papyrus.xwt.javabean.metadata.properties.DynamicProperty;
import org.eclipse.papyrus.xwt.javabean.metadata.properties.EventProperty;
import org.eclipse.papyrus.xwt.javabean.metadata.properties.FieldProperty;
import org.eclipse.papyrus.xwt.jface.JFacesHelper;
import org.eclipse.papyrus.xwt.metadata.IEvent;
import org.eclipse.papyrus.xwt.metadata.IMetaclass;
import org.eclipse.papyrus.xwt.metadata.IObjectInitializer;
import org.eclipse.papyrus.xwt.metadata.IProperty;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
/**
*
* @author xye (xiaowei.ye@soyatec.com)
*/
public abstract class AbstractMetaclass implements IMetaclass {
public static IObjectInitializer[] EMPTY_INITIALIZERS = new IObjectInitializer[0];
public static IProperty[] EMPTY_PROPERTIES = new IProperty[0];
public static IEvent[] EMPTY_ROUTED_EVENTS = new IEvent[0];
protected final Map<String, IProperty> propertyCache = new HashMap<String, IProperty>();
protected Map<String, IEvent> routedEventCache = new HashMap<String, IEvent>();
protected Map<String, IEventGroup> eventGroupCache = Collections.emptyMap();
protected Class<?> type;
protected String name;
protected IMetaclass superClass;
protected IXWTLoader xwtLoader;
protected boolean buildTypedEvents;
private boolean initialize = false;
private IObjectInitializer[] initializers = EMPTY_INITIALIZERS;
protected boolean shouldIgnored(Class<?> declaredType, String propertyName, Class<?> propertyType) {
String packageName = "";
if(type.getPackage() != null) {
packageName = declaredType.getPackage().getName();
}
if(("data".equals(propertyName) && packageName.startsWith("org.eclipse.swt."))) {
return true;
}
if("class".equals(propertyName)) {
return true;
}
if(("handle".equals(propertyName) && int.class == propertyType) || ("monitor".equals(propertyName) && Monitor.class == propertyType) || ("region".equals(propertyName) && Region.class == propertyType) || ("parent".equals(propertyName) && Composite.class == propertyType) || ("shell".equals(propertyName) && Shell.class == propertyType) || ("display".equals(propertyName) && Display.class == propertyType)) {
return true;
}
return false;
}
protected boolean isWidgetType(Class<?> type) {
Class<?> superClass = type.getSuperclass();
if(superClass != null) {
if(superClass.getName().equalsIgnoreCase(Widget.class.getName())) {
return true;
} else {
return isWidgetType(superClass);
}
}
return false;
}
protected final IXWTLoader getXWTLoader() {
return xwtLoader;
}
public IProperty addProperty(IProperty property) {
String name = normalize(property.getName());
return propertyCache.put(name, property);
}
public void removeProperty(String propertyName) {
String name = normalize(propertyName);
propertyCache.remove(name);
}
private void buildTypedEvents() {
if(buildTypedEvents) {
return;
}
if(isSubclassOf(getXWTLoader().getMetaclass(Widget.class))) {
addTypedEvent(IEventConstants.ACTIVATE, SWT.Activate);
addTypedEvent(IEventConstants.ARM, SWT.Arm);
addTypedEvent(IEventConstants.CLOSE, SWT.Close);
addTypedEvent(IEventConstants.COLLAPSE, SWT.Collapse);
addTypedEvent(IEventConstants.DEACTIVATE, SWT.Deactivate);
addTypedEvent(IEventConstants.DEFAULT_SELECTION, SWT.DefaultSelection);
addTypedEvent(IEventConstants.DEICONIFY, XWTMaps.getEvent("swt.deiconify"));
addTypedEvent(IEventConstants.DISPOSE, SWT.Dispose);
addTypedEvent(IEventConstants.DRAG_SELECT, SWT.DragDetect);
addTypedEvent(IEventConstants.ERASE_ITEM, XWTMaps.getEvent("swt.eraseitem"));
addTypedEvent(IEventConstants.EXPAND, SWT.Expand);
addTypedEvent(IEventConstants.FOCUS_IN, SWT.FocusIn);
addTypedEvent(IEventConstants.FOCUS_OUT, SWT.FocusOut);
addTypedEvent(IEventConstants.HARD_KEY_DOWN, XWTMaps.getEvent("swt.hardkeydown"));
addTypedEvent(IEventConstants.HARD_KEY_UP, XWTMaps.getEvent("swt.hardkeyup"));
addTypedEvent(IEventConstants.HELP, SWT.Help);
addTypedEvent(IEventConstants.HIDE, SWT.Hide);
addTypedEvent(IEventConstants.ICONIFY, XWTMaps.getEvent("swt.iconify"));
addTypedEvent(IEventConstants.KEY_DOWN, SWT.KeyDown);
addTypedEvent(IEventConstants.KEY_UP, SWT.KeyUp);
addTypedEvent(IEventConstants.MEASURE_ITEM, XWTMaps.getEvent("swt.measureitem"));
addTypedEvent(IEventConstants.MENU_DETECT, SWT.MenuDetect);
addTypedEvent(IEventConstants.MODIFY, SWT.Modify);
addTypedEvent(IEventConstants.MOUSE_DOUBLE_CLICK, SWT.MouseDoubleClick);
addTypedEvent(IEventConstants.MOUSE_DOWN, SWT.MouseDown);
addTypedEvent(IEventConstants.MOUSE_ENTER, XWTMaps.getEvent("swt.mouseenter"));
addTypedEvent(IEventConstants.MOUSE_EXIT, XWTMaps.getEvent("swt.mouseexit"));
addTypedEvent(IEventConstants.MOUSE_HOVER, XWTMaps.getEvent("swt.mousehover"));
addTypedEvent(IEventConstants.MOUSE_MOVE, XWTMaps.getEvent("swt.mousemove"));
addTypedEvent(IEventConstants.MOUSE_UP, SWT.MouseUp);
addTypedEvent(IEventConstants.MOUSE_WHEEL, XWTMaps.getEvent("swt.mousewheel"));
addTypedEvent(IEventConstants.MOVE, SWT.Move);
addTypedEvent(IEventConstants.PAINT, XWTMaps.getEvent("swt.paint"));
addTypedEvent(IEventConstants.PAINT_ITEM, XWTMaps.getEvent("swt.paintitem"));
addTypedEvent(IEventConstants.RESIZE, SWT.Resize);
addTypedEvent(IEventConstants.SELECTION, SWT.Selection); // sash
addTypedEvent(IEventConstants.SET_DATA, SWT.SetData);
// addTypedEvent ("Settings", SWT.Settings); // note: this event
// only goes to Display
addTypedEvent(IEventConstants.SHOW, SWT.Show);
addTypedEvent(IEventConstants.TRAVERSE, SWT.Traverse);
addTypedEvent(IEventConstants.VERIFY, SWT.Verify);
addTypedEvent(IEventConstants.IME_COMPOSITION, XWTMaps.getEvent("swt.imecomposition"));
}
buildTypedEvents = true;
}
private void addTypedEvent(String name, int eventType) {
String eventName = IEventConstants.getEventName(name);
if(!routedEventCache.containsKey(eventName)) {
TypedEvent typedEvent = new TypedEvent(name, eventType);
routedEventCache.put(eventName, typedEvent);
String eventPropertyName = IEventConstants.getEventPropertyName(name);
String eventDataName = IEventConstants.getEventPropertyDataName(name);
addProperty(new EventProperty(eventPropertyName, eventDataName, typedEvent));
}
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#findDefaultProperty()
*/
public IProperty findDefaultProperty() {
return null;
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#findEvent(java.lang.String)
*/
public IEvent findEvent(String name) {
assertInitialize();
return routedEventCache.get(normalize(name));
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#findProperty(java.lang.String)
*/
public IProperty findProperty(String name) {
assertInitialize();
IProperty property = propertyCache.get(normalize(name));
if(property == null && superClass != null) {
property = superClass.findProperty(name);
}
if(property == null) {
try {
Method getter = DynamicProperty.createGetter(type, name);
if(getter == null) {
return null;
}
Class<?> propertyType = getter.getReturnType();
if(shouldIgnored(getter.getDeclaringClass(), name, propertyType)) {
return null;
}
Method setter = DynamicProperty.createSetter(type, propertyType, name);
return new DynamicProperty(propertyType, setter, getter, name);
} catch (NoSuchMethodException e) {
return null;
}
}
return property;
}
protected String normalize(String name) {
return name == null ? name : name.toLowerCase();
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#getEvents()
*/
public IEvent[] getEvents() {
assertInitialize();
return routedEventCache.values().toArray(new IEvent[]{});
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#getName()
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#getProperties()
*/
public IProperty[] getProperties() {
assertInitialize();
return propertyCache.values().toArray(new IProperty[propertyCache.size()]);
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#getSuperClass()
*/
public IMetaclass getSuperClass() {
return superClass;
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#getType()
*/
public Class<?> getType() {
return type;
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#isAbstract()
*/
public boolean isAbstract() {
return Modifier.isAbstract(type.getModifiers());
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#isAssignableFrom(com.soyatec
* .xaswt.core.metadata.IMetaclass)
*/
public boolean isAssignableFrom(IMetaclass metaclass) {
return getType().isAssignableFrom(metaclass.getType());
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#isInstance(java.lang.Object)
*/
public boolean isInstance(Object object) {
return type.isInstance(object);
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#isSubclassOf(com.soyatec.xaswt
* .core.metadata.IMetaclass)
*/
public boolean isSubclassOf(IMetaclass metaclass) {
assertInitialize();
if(metaclass == null) {
return false;
}
if(this == metaclass) {
return true;
}
if(superClass == metaclass) {
return true;
}
if(superClass != null) {
return superClass.isSubclassOf(metaclass);
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.soyatec.xaswt.core.metadata.IMetaclass#isSuperclassOf(com.soyatec
* .xaswt.core.metadata.IMetaclass)
*/
public boolean isSuperclassOf(IMetaclass metaclass) {
return metaclass.isSubclassOf(this);
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#newInstance()
*/
public Object newInstance(Object[] parameters) {
assertInitialize();
Object object = doNewInstance(parameters);
if(parameters != null && parameters.length > 0) {
try {
updateContainment(parameters[0], object);
initialize(object);
} catch (Exception e) {
throw new XWTException(e);
}
}
return object;
}
private void updateContainment(Object parent, Object childElement) throws IllegalAccessException, InvocationTargetException, NoSuchFieldException {
if(childElement != null && parent != null && !(parent instanceof Widget)) {
//
// Add to default property identified by the type
//
IMetaclass parentMetaclass = XWT.getMetaclass(parent);
IProperty[] properties = parentMetaclass.getProperties();
IProperty useProperty = null;
int count = 0;
Class<?> childType = childElement.getClass();
for(IProperty property : properties) {
Class<?> propertyType = property.getType();
if(propertyType == null || propertyType == Object.class) {
continue;
}
if(property.isContainement()) {
useProperty = property;
count++;
}
}
if(count > 1) {
StringBuilder builder = new StringBuilder();
builder.append("Class has more containment properties: ");
count = 0;
for(IProperty property : properties) {
Class<?> propertyType = property.getType();
if(propertyType == null || propertyType == Object.class) {
continue;
}
if(property.isContainement()) {
if(count != 0) {
builder.append(", ");
}
builder.append(property.getName());
count++;
}
}
throw new XWTException("Class has more containment properties: ");
}
if(count == 0) {
for(IProperty property : properties) {
Class<?> propertyType = property.getType();
if(propertyType == null || propertyType == Object.class) {
continue;
}
if(propertyType.isArray()) {
Class<?> dataType = propertyType.getComponentType();
if(dataType.isAssignableFrom(childType)) {
if(useProperty == null) {
useProperty = property;
}
count++;
}
} else if(Collection.class.isAssignableFrom(propertyType)) {
if(useProperty == null) {
useProperty = property;
}
count++;
} else if(propertyType.isAssignableFrom(childType)) {
if(useProperty == null) {
useProperty = property;
}
count++;
}
}
}
if(count == 1) {
Class<?> propertyType = useProperty.getType();
if(propertyType.isArray()) {
Object[] existingValue = (Object[])useProperty.getValue(parent);
Class<?> dataType = propertyType.getComponentType();
Object[] value = null;
if(existingValue == null) {
value = (Object[])Array.newInstance(dataType, 1);
value[0] = childElement;
} else {
value = (Object[])Array.newInstance(dataType, existingValue.length + 1);
System.arraycopy(existingValue, 0, value, 0, existingValue.length);
value[existingValue.length] = childElement;
}
useProperty.setValue(parent, value);
} else if(Collection.class.isAssignableFrom(propertyType) && !(childElement instanceof IBinding)) {
Collection<Object> existingValue = (Collection)useProperty.getValue(parent);
if(existingValue == null) {
existingValue = new ArrayList<Object>();
}
existingValue.add(childElement);
useProperty.setValue(parent, existingValue);
} else if(propertyType.isAssignableFrom(childType)) {
useProperty.setValue(parent, childElement);
}
}
}
}
/*
* (non-Javadoc)
*
* @see com.soyatec.xaswt.core.metadata.IMetaclass#newInstance()
*/
public Object doNewInstance(Object[] parameters) {
assertInitialize();
try {
if(parameters.length == 0 || (!(parameters[0] instanceof Widget || JFacesHelper.isViewer(parameters[0])))) {
return getType().newInstance();
}
} catch (InstantiationException e1) {
} catch (IllegalAccessException e1) {
}
try {
Object swtObject = null;
Object parent = parameters[0];
Widget directParent = UserData.getWidget(parent);
if(directParent == null) {
directParent = UserData.getTreeParent(parent);
}
if(directParent != null && Control.class.isAssignableFrom(getType()) && !(directParent instanceof Composite)) {
directParent = getXWTLoader().findCompositeParent(directParent);
}
Object styleValue = null;
if(parameters.length == 2 && parameters[1] != null && (parameters[1].getClass() == int.class || parameters[1].getClass() == Integer.class)) {
styleValue = parameters[1];
}
Constructor<?> defaultConstructor = null;
for(Constructor<?> constructor : getType().getConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if(parameterTypes.length > 2 || parameterTypes.length == 0) {
if(parameterTypes.length == 0) {
defaultConstructor = constructor;
}
continue;
}
if(parameterTypes[0].isAssignableFrom(parent.getClass())) {
if(parameterTypes.length == 1) {
if(styleValue == null) {
swtObject = constructor.newInstance(new Object[]{ parent });
break;
}
} else if(parameterTypes[1].isAssignableFrom(int.class)) {
if(styleValue == null) {
swtObject = constructor.newInstance(new Object[]{ parent, 0 });
} else {
swtObject = constructor.newInstance(new Object[]{ parent, styleValue });
}
break;
}
}
}
if(swtObject == null) {
for(Constructor<?> constructor : getType().getConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if(parameterTypes.length > 2 || parameterTypes.length == 0) {
if(parameterTypes.length == 0) {
defaultConstructor = constructor;
}
continue;
}
if(directParent != null && parameterTypes[0].isAssignableFrom(directParent.getClass())) {
if(parameterTypes.length == 1) {
swtObject = constructor.newInstance(new Object[]{ directParent });
break;
} else if(parameterTypes[1].isAssignableFrom(int.class)) {
if(styleValue == null) {
swtObject = constructor.newInstance(new Object[]{ directParent, 0 });
} else {
swtObject = constructor.newInstance(new Object[]{ directParent, styleValue });
}
break;
}
}
}
}
if(swtObject == null) {
if(defaultConstructor == null) {
if(UserData.isUIElementType(getType())) {
// this is used for Visual
Shell shell = new Shell();
try {
Constructor<?> constructor = getType().getConstructor(Composite.class, int.class);
if(constructor != null) {
return constructor.newInstance(shell, SWT.NONE);
}
} catch (Exception e) {
throw new XWTException("Constructor " + getType().getName() + " no found.");
}
}
try {
swtObject = getType().newInstance();
} catch (Exception e) {
throw new XWTException("Constructor " + getType().getName() + " no found.");
}
} else {
swtObject = defaultConstructor.newInstance();
}
}
return swtObject;
} catch (SecurityException e) {
throw new XWTException(e);
} catch (IllegalArgumentException e) {
throw new XWTException(e);
} catch (InstantiationException e) {
throw new XWTException(e);
} catch (IllegalAccessException e) {
throw new XWTException(e);
} catch (InvocationTargetException e) {
throw new XWTException(e);
}
}
public Class<?> getDataContextType() {
return Object.class;
}
private void assertInitialize() {
initialize(type, superClass);
}
protected void initialize(Class<?> type, IMetaclass superClass) {
if(isInitialize()) {
return;
}
try {
BeanInfo beanInfo = java.beans.Introspector.getBeanInfo(type);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor p : propertyDescriptors) {
String propertyName = p.getName();
Class<?> propertyType = p.getPropertyType();
if(p.getReadMethod() == null) {
continue;
}
if(shouldIgnored(p.getReadMethod().getDeclaringClass(), propertyName, propertyType) || propertyCache.containsKey(propertyName.toLowerCase())) {
continue;
}
if(p.getPropertyType() != null) {
IProperty property = (superClass != null ? superClass.findProperty(p.getName().toLowerCase()) : null);
if(property != null && !property.isDefault()) {
addProperty(property);
} else {
if(p.getWriteMethod() != null || !p.getPropertyType().isPrimitive()) {
addProperty(new BeanProperty(p));
}
}
}
}
for(Field f : type.getDeclaredFields()) {
if(Modifier.isStatic(f.getModifiers())) {
continue;
}
String propertyName = f.getName();
Class<?> propertyType = f.getType();
if(shouldIgnored(f.getDeclaringClass(), propertyName, propertyType)) {
continue;
}
if(!propertyCache.containsKey(normalize(propertyName)) && !Modifier.isFinal(f.getModifiers()) && Modifier.isPublic(f.getModifiers())) {
addProperty(new FieldProperty(f));
}
}
for(EventSetDescriptor eventSetDescriptor : beanInfo.getEventSetDescriptors()) {
String name = IEventConstants.getEventName(eventSetDescriptor.getName());
BeanEvent event = new BeanEvent(eventSetDescriptor.getName(), eventSetDescriptor);
routedEventCache.put(name, event);
String propertyName = IEventConstants.getEventPropertyName(eventSetDescriptor.getName());
String propertyDataName = IEventConstants.getEventPropertyDataName(eventSetDescriptor.getName());
addProperty(new EventProperty(propertyName, propertyDataName, event));
}
if(isWidgetType(type)) {
LoadedEvent loadedEvent = new LoadedEvent(IEventConstants.XWT_LOADED_EVENT);
routedEventCache.put(normalize(IEventConstants.XWT_LOADED), loadedEvent);
routedEventCache.put(normalize(IEventConstants.XWT_LOADED_EVENT), loadedEvent);
}
markInitialized();
buildTypedEvents();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
private void markInitialized() {
initialize = true;
}
private boolean isInitialize() {
return initialize;
}
public void addEventGroup(IEventGroup eventGroup) {
if(eventGroupCache == Collections.<String, IEventGroup> emptyMap()) {
eventGroupCache = new HashMap<String, IEventGroup>();
}
for(String string : eventGroup.getEventNames()) {
if(eventGroupCache.containsKey(string)) {
throw new IllegalArgumentException("Event \"" + string + "\" already existis in a group.");
}
String key = normalize(string);
if("menudetecteventevent".equals(key)) {
System.out.println(string);
}
eventGroupCache.put(key, eventGroup);
}
}
public IEventGroup getEventGroup(String event) {
IEventGroup eventGroup = eventGroupCache.get(event);
if(eventGroup == null && superClass != null) {
return superClass.getEventGroup(event);
}
return eventGroup;
}
public void addInitializer(IObjectInitializer initializer) {
for(int i = 0; i < initializers.length; i++) {
if(initializers[i] == initializer) {
return;
}
}
IObjectInitializer[] oldValue = initializers;
initializers = new IObjectInitializer[oldValue.length + 1];
System.arraycopy(oldValue, 0, initializers, 0, oldValue.length);
initializers[oldValue.length] = initializer;
}
public void removeInitializer(IObjectInitializer initializer) {
for(int i = 0; i < initializers.length; i++) {
if(initializers[i] == initializer) {
IObjectInitializer[] oldValue = initializers;
initializers = new IObjectInitializer[oldValue.length - 1];
System.arraycopy(oldValue, 0, initializers, 0, i);
System.arraycopy(oldValue, i + 1, initializers, i, oldValue.length - i - 1);
return;
}
}
}
public IObjectInitializer[] getInitializers() {
return initializers;
}
public void initialize(Object instance) {
if(superClass != null) {
superClass.initialize(instance);
}
for(int i = 0; i < initializers.length; i++) {
if(initializers[i] != null) {
initializers[i].initialize(instance);
}
}
}
}