blob: c3ab399c0804be6946c96474b07ad223ff38d4ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2006 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.jem.internal.beaninfo.adapters;
/*
*/
import java.io.FileNotFoundException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.emf.common.notify.*;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.*;
import org.eclipse.emf.ecore.*;
import org.eclipse.emf.ecore.change.*;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.ESuperAdapter;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.*;
import org.eclipse.emf.ecore.xmi.PackageNotFoundException;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.jem.internal.beaninfo.*;
import org.eclipse.jem.internal.beaninfo.common.*;
import org.eclipse.jem.internal.beaninfo.core.*;
import org.eclipse.jem.internal.beaninfo.core.BeanInfoCacheController.ClassEntry;
import org.eclipse.jem.internal.java.beaninfo.IIntrospectionAdapter;
import org.eclipse.jem.internal.proxy.core.*;
import org.eclipse.jem.java.*;
import org.eclipse.jem.java.internal.impl.JavaClassImpl;
import org.eclipse.jem.util.TimerTests;
import org.eclipse.jem.util.logger.proxy.Logger;
/**
* Beaninfo adapter for doing introspection on a Java Model class.
* <p>
* The first time a introspect request is made, it will use the ClassEntry from the cache controller to determine if it should load from the
* cache or do a complete introspection. After that it will always do a complete introspection when it is marked as needing introspection.
* This is because the cache is useless to us then. At that point in time we already know that we or one of our superclasses or one of
* outer classes has changed, thereby requiring us to reintrospect, the cache is invalid at that point. Also once we did an introspection we
* don't want to do a load from cache but instead do a merge because someone may of been holding onto the features and we don't want to
* throw them away unnecessarily.
* <p>
* TODO Need to re-look into this to see if we can do a merge with the cache file because if it was an external jar and it has now gone
* valid, why waste time reintrospecting. But this needs to be carefully thought about.
* <p>
* The resource change listener
* will automatically mark any subclasses (both the BeaninfoClassAdapter and the ClassEntry) as being stale for us. That way we don't need
* to waste time checking the ce and superclasses everytime. We will keep the ClassEntry around simply to make the marking of it as stale easier for the
* resource listener. Finding the ce everytime would be expensive.
* <p>
* This is the process for determining if introspection required:
* <ol>
* <li>If needsIntrospection flag is false, then do nothing. Doesn't need introspection.
* <li>If class is undefined, then just set up for an undefined class and return. (In this case the CE should be thrown away, it should of been deleted).
* <li>If never introspected (not RETRIEVED_FULL_DOCUMENT), get the CE, get the modification stamp and call isStaleStamp. This determines if this
* stamp or any super class stamp is stale wrt to current stamp. If it is not stale, then load from cache because the cache is good.
* <li>If no cache or cache is stale or has introspected once, then introspect and replace cache.
* </ol>
*
*/
public class BeaninfoClassAdapter extends AdapterImpl implements IIntrospectionAdapter {
public static final String REFLECT_PROPERTIES = "Reflect properties"; // Reflect properties in IDE //$NON-NLS-1$
public static final String APPLY_EXTENSIONS = "Apply Overrides"; // Apply override files //$NON-NLS-1$
public static final String REMOTE_INTROSPECT = "Remote Introspect"; // Introspect on remote //$NON-NLS-1$
public static final String INTROSPECT = "Introspect"; // Straight introspection, whether load from cache or introspect. //$NON-NLS-1$
public static final String LOAD_FROM_CACHE = "Load from Cache"; //$NON-NLS-1$
public static BeaninfoClassAdapter getBeaninfoClassAdapter(EObject jc) {
return (BeaninfoClassAdapter) EcoreUtil.getExistingAdapter(jc, IIntrospectionAdapter.ADAPTER_KEY);
}
/**
* Clear out the introspection because introspection is being closed or removed from the project.
* Don't want anything hanging around that we had done.
*/
public void clearIntrospection() {
// Clear out the beandecorator if implicitly created.
Iterator beanItr = getJavaClass().getEAnnotationsInternal().iterator();
while (beanItr.hasNext()) {
EAnnotation dec = (EAnnotation) beanItr.next();
if (dec instanceof BeanDecorator) {
BeanDecorator decor = (BeanDecorator) dec;
if (decor.getImplicitDecoratorFlag() == ImplicitItem.IMPLICIT_DECORATOR_LITERAL) {
beanItr.remove();
((InternalEObject) decor).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
} else {
BeanInfoDecoratorUtility.clear((BeanDecorator) dec);
}
break;
}
}
// Clear out the features that we implicitly created.
Iterator propItr = getJavaClass().getEStructuralFeaturesInternal().iterator();
while (propItr.hasNext()) {
EStructuralFeature prop = (EStructuralFeature) propItr.next();
Iterator pdItr = prop.getEAnnotations().iterator();
while (pdItr.hasNext()) {
EAnnotation dec = (EAnnotation) pdItr.next();
if (dec instanceof PropertyDecorator) {
PropertyDecorator pd = (PropertyDecorator) dec;
if (pd.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL)
BeanInfoDecoratorUtility.clear(pd);
else {
pdItr.remove(); // Remove it from the property.
((InternalEObject) pd).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
if (pd.getImplicitDecoratorFlag() == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
propItr.remove(); // Remove the feature itself
((InternalEObject) prop).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
break;
}
}
}
// Clear out the operations that we implicitly created.
Iterator operItr = getJavaClass().getEOperationsInternal().iterator();
while (operItr.hasNext()) {
EOperation oper = (EOperation) operItr.next();
Iterator mdItr = oper.getEAnnotations().iterator();
while (mdItr.hasNext()) {
EAnnotation dec = (EAnnotation) mdItr.next();
if (dec instanceof MethodDecorator) {
MethodDecorator md = (MethodDecorator) dec;
if (md.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL)
BeanInfoDecoratorUtility.clear(md);
else {
mdItr.remove(); // Remove it from the operation.
((InternalEObject) md).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
if (md.getImplicitDecoratorFlag() == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
operItr.remove(); // Remove the oepration itself
((InternalEObject) oper).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
break;
}
}
// Clear out the events that we implicitly created.
Iterator evtItr = getJavaClass().getEventsGen().iterator();
while (evtItr.hasNext()) {
JavaEvent evt = (JavaEvent) evtItr.next();
Iterator edItr = evt.getEAnnotations().iterator();
while (edItr.hasNext()) {
EAnnotation dec = (EAnnotation) edItr.next();
if (dec instanceof EventSetDecorator) {
EventSetDecorator ed = (EventSetDecorator) dec;
if (ed.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL)
BeanInfoDecoratorUtility.clear(ed);
else {
edItr.remove(); // Remove it from the event.
((InternalEObject) ed).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
if (ed.getImplicitDecoratorFlag() == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
evtItr.remove(); // Remove the event itself
((InternalEObject) evt).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
}
break;
}
}
}
}
synchronized(this) {
needsIntrospection = true;
}
}
private void clearAll() {
clearIntrospection(); // First get rid of the ones we did so that they are marked as proxies.
// Clear out the annotations.
getJavaClass().getEAnnotationsInternal().clear();
// Clear out the attributes.
getJavaClass().getEStructuralFeaturesInternal().clear();
// Clear out the operations.
getJavaClass().getEOperationsInternal().clear();
// Clear out the events.
getJavaClass().getEventsGen().clear();
retrievedExtensionDocument = NEVER_RETRIEVED_EXTENSION_DOCUMENT; // Since we cleared everything, go back to no doc applied.
}
/**
* @version 1.0
* @author
*/
// A URI that will never resolve. Used to mark an object as no longer valid.
protected static final URI BAD_URI = URI.createURI("baduri"); //$NON-NLS-1$
protected boolean needsIntrospection = true;
protected BeanInfoCacheController.ClassEntry classEntry;
protected boolean isIntrospecting;
protected boolean isDoingAllProperties;
protected boolean isDoingAllOperations;
protected boolean isDoingAllEvents;
protected final static int
NEVER_RETRIEVED_EXTENSION_DOCUMENT = 0,
RETRIEVED_ROOT_ONLY = 1,
RETRIEVED_FULL_DOCUMENT = 2,
CLEAR_EXTENSIONS = 3;
protected int retrievedExtensionDocument = NEVER_RETRIEVED_EXTENSION_DOCUMENT;
protected BeaninfoAdapterFactory adapterFactory;
private WeakReference staleFactory; // When reference not null, then this factory is a stale factory and
// a new one is needed when the factory returned == this one.
// It is a WeakRef so that if the factory goes away on its own
// that we don't hold onto it.
// A temporary hashset of the local properties. Used when creating a new
// property to use the old one. It is cleared out at the end of attribute introspection.
// It is only built once during attribute introspection so that we have the snapshot
// of before the introspection (i.e. the ones that were specifically added by users
// and not through introspection). The ones that we are adding do not need to be checked
// against each other because there will not be conflicts.
//
// Also at the end, we will go through the properties and see if it exists in the fPropertiesMap and
// it is not the value Boolean.FALSE in the map, and the entry is implicitly created. This means this
// was an implicit entry from the previous introspection and was not re-created in this introspection.
private HashMap propertiesMap;
private List featuresRealList; // Temp pointer to the real list we are building. It is the true list in java class.
// A temporary hashmap of the local operations. Used when creating a new
// operation to reuse the old one. It is cleared out at the end of operation introspection.
// It is only built once during operation introspection so that we have the snapshot
// of before the introspection.
private HashMap operationsMap;
private EList operationsRealList; // Temp pointer to the real list we are building. It is the true list in java class.
// A set of operations as we create them so that we which ones we added/kept and which are no longer in use and can be removed.
// If they aren't in this set at the end, then we know it should be removed if it is one we had created in the past.
private HashSet newoperations;
// A temporary hashset of the local events. Used when creating a new
// event to use the old one. It is cleared out at the end of event introspection.
// It is only built once during event introspection so that we have the snapshot
// of before the introspection (i.e. the ones that were specifically added by users
// and not through introspection). The ones that we are adding do not need to be checked
// against each other because there will not be conflicts.
//
// Also at the end, we will go through the events and see if it exists in the fEventsMap and
// it is not the value Boolean.FALSE in the map, and the entry is implicitly created. This means this
// was an implicit entry from the previous introspection and was not re-created in this introspection.
private HashMap eventsMap;
private EList eventsRealList; // Temp pointer to the real list we are building. It is the true list in java class.
private Boolean defaultBound;
// Whether this class is default bound or not (i.e. does this class implement add/remove property change listener. If null, then not yet queried.
public BeaninfoClassAdapter(BeaninfoAdapterFactory factory) {
super();
adapterFactory = factory;
}
/*
* Answer whether this java class is still connected to a live resource. It happens during unloading
* that we are no longer connected to a resource (because the javapackage has already been processed and
* unloaded) or the resource is in the process of being unloaded, but the unloading process will still
* call accessors on this java class, which will come over here. In those cases we should treat as
* introspection completed without doing anything.
*/
protected boolean isResourceConnected() {
Resource res = getJavaClass().eResource();
return res != null && res.isLoaded();
}
protected final ProxyFactoryRegistry getRegistry() {
ProxyFactoryRegistry factory = adapterFactory.getRegistry();
if (staleFactory != null && factory == staleFactory.get()) {
// We need to recycle the factory. The returned factory is the same factory when it went stale.
factory = adapterFactory.recycleRegistry();
}
staleFactory = null; // Whether we recycled or not, it is no longer stale.
return factory;
}
/**
* Return whether this adapter has been marked as stale. Needed
* by the users so that they can recycle if necessary.
*/
public boolean isStale() {
return staleFactory != null;
}
protected BeaninfoAdapterFactory getAdapterFactory() {
return adapterFactory;
}
public boolean isAdapterForType(Object key) {
return IIntrospectionAdapter.ADAPTER_KEY.equals(key);
}
/**
* This map is keyed by name. It is a snapshot of the properties at the
* time the introspection/reflection of properties was started. It is used
* for quick lookup.
*
* Once a property is used, the entry is replaced with a Boolean.FALSE. This
* is so we know which have already been used and at the end, which need
* to be deleted since they weren't used (i.e. the ones that aren't FALSE).
*/
protected HashMap getPropertiesMap() {
if (propertiesMap == null) {
List localFeatures = getJavaClass().getEStructuralFeaturesInternal();
propertiesMap = new HashMap(localFeatures.size());
Iterator itr = localFeatures.iterator();
while (itr.hasNext()) {
EStructuralFeature feature = (EStructuralFeature) itr.next();
propertiesMap.put(feature.getName(), feature);
}
}
return propertiesMap;
}
/**
* Get it once so that we don't need to keep getting it over and over.
*/
protected List getFeaturesList() {
if (featuresRealList == null)
featuresRealList = getJavaClass().getEStructuralFeaturesInternal();
return featuresRealList;
}
/**
* The map is keyed by longName. If a Method is passed in, then the
* id of the method is used (this is in reflection), if an IBeanProxy
* is passed in, then an id is created and looked up (this is in introspection).
* The map is used for a quick lookup of behaviors at the time introspection
* of behaviors started.
*/
protected HashMap getOperationsMap() {
if (operationsMap == null) {
List locals = getJavaClass().getEOperationsInternal();
int l = locals.size();
operationsMap = new HashMap(l);
for (int i = 0; i < l; i++) {
EOperation op = (EOperation) locals.get(i);
operationsMap.put(formLongName(op), op);
}
}
return operationsMap;
}
/**
* Get it once so that we don't need to keep getting it over and over.
*/
protected EList getOperationsList() {
if (operationsRealList == null)
operationsRealList = getJavaClass().getEOperationsInternal();
return operationsRealList;
}
/**
* The map is keyed by name.
* The map is used for a quick lookup of events at the time introspection
* of events started.
*
* Once an event is used, the entry is replaced with a Boolean.FALSE. This
* is so we know which have already been used and at the end, which need
* to be deleted since they weren't used (i.e. the ones that aren't FALSE).
*/
protected HashMap getEventsMap() {
if (eventsMap == null) {
List locals = getJavaClass().getEventsGen();
eventsMap = new HashMap(locals.size());
Iterator itr = locals.iterator();
while (itr.hasNext()) {
JavaEvent event = (JavaEvent) itr.next();
eventsMap.put(event.getName(), event);
}
}
return eventsMap;
}
/**
* Get it once so that we don't need to keep getting it over and over.
*/
protected EList getEventsList() {
if (eventsRealList == null)
eventsRealList = getJavaClass().getEventsGen();
return eventsRealList;
}
public void introspectIfNecessary() {
introspectIfNecessary(false);
}
protected void introspectIfNecessary(boolean doOperations) {
boolean doIntrospection = false;
synchronized (this) {
doIntrospection = needsIntrospection && !isIntrospecting;
if (doIntrospection)
isIntrospecting = true;
}
if (doIntrospection) {
boolean didIntrospection = false;
try {
introspect(doOperations);
didIntrospection = true;
} catch (Exception e) {
BeaninfoPlugin.getPlugin().getLogger().log(
new Status(
IStatus.WARNING,
BeaninfoPlugin.getPlugin().getBundle().getSymbolicName(),
0,
MessageFormat.format(
BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_,
new Object[] { getJavaClass().getJavaName(), ""}), //$NON-NLS-1$
e));
} finally {
synchronized (this) {
isIntrospecting = false;
needsIntrospection = !didIntrospection;
}
}
}
}
/**
* Get the class entry.
* @return
*
* @since 1.1.0
*/
public BeanInfoCacheController.ClassEntry getClassEntry() {
return classEntry;
}
private boolean canUseCache() {
// We check our level, we assume not stale unless CE says stale.
synchronized (this) {
// We may already have a class entry due to a subclass doing a check, so if we do, we'll use it. Else we'll get the latest one.
if (classEntry == null)
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null) {
// We have a cache to check.
long modStamp = classEntry.getModificationStamp();
// A sanity check, if this was an old, but now deleted one we want to throw it away and get it again. It may now be valid.
if (modStamp == BeanInfoCacheController.ClassEntry.DELETED_MODIFICATION_STAMP) {
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null)
modStamp = classEntry.getModificationStamp();
}
if (modStamp == IResource.NULL_STAMP || modStamp == BeanInfoCacheController.ClassEntry.DELETED_MODIFICATION_STAMP)
return false; // We are stale.
} else
return false; // We don't have a cache entry to check against, which means are deleted, or never cached.
}
// Now try the supers to see if we are out of date to them.
// Note: Only if this is an interface could there be more than one eSuperType.
List supers = getJavaClass().getESuperTypes();
if (!supers.isEmpty()) {
BeaninfoClassAdapter bca = getBeaninfoClassAdapter((EObject) supers.get(0));
ClassEntry superCE = bca.getClassEntry();
// If super is defined and out of date don't use cache. If super is undefined and super modification stamp is not super_undefined,
// then that means it was defined at time of cache and now not defined, so don't use cache.
if (superCE != null) {
if (superCE.getModificationStamp() != classEntry.getSuperModificationStamp())
return false; // Out-of-date wrt/super.
} else if (classEntry.getSuperModificationStamp() != ClassEntry.SUPER_UNDEFINED_MODIFICATION_STAMP)
return false; // was previously defined, and now undefined, so out of date.
String[] iNames = classEntry.getInterfaceNames();
if (iNames != null) {
if (iNames.length != supers.size() - 1)
return false; // We have a different number of supers, so stale.
// Now the interfaces may not be in the same order, but there shouldn't be too many, so we'll use O(n2) lookup. We'll try starting at
// the same index just in case. That way if they are the same order it will be linear instead. Most likely will be same order.
long[] iStamp = classEntry.getInterfaceModificationStamps();
for (int i = 1; i <= iNames.length; i++) {
JavaClass javaClass = (JavaClass) supers.get(i);
String intName = (javaClass).getQualifiedNameForReflection();
// Find it in the names list.
int stop = i-1;
int indx = stop; // Start at the stop, when we hit it again we will be done.
boolean found = false;
do {
if (iNames[indx].equals(intName)) {
found = true;
break;
}
} while ((++indx)%iNames.length != stop);
if (!found)
return false; // Couldn't find it, so we are stale.]
superCE = getBeaninfoClassAdapter(javaClass).getClassEntry();
if (superCE != null) {
if (superCE.getModificationStamp() != iStamp[indx])
return false; // Out-of-date wrt/interface.
} else if (iStamp[indx] != ClassEntry.SUPER_UNDEFINED_MODIFICATION_STAMP)
return false; // was previously defined, and now undefined, so out of date.
}
}
}
return true; // If we got here everything is ok.
}
private boolean canUseOverrideCache() {
// We check our config stamp.
// TODO in future can we listen for config changes and mark classes stale if the config changes?
synchronized (this) {
// We may already have a class entry due to a subclass doing a check, so if we do, we'll use it. Else we'll get the latest one.
if (classEntry == null)
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null) {
// We have a cache to check.
// A sanity check, if this was an old, but now deleted one we want to throw it away and get it again. It may now be valid.
if (classEntry.isDeleted()) {
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null)
if (classEntry.isDeleted())
return false; // We have been deleted. Probably shouldn't of gotton here in this case.
}
} else
return false; // We don't have a cache entry to check against, which means are deleted, or never cached.
return classEntry.getConfigurationModificationStamp() == Platform.getPlatformAdmin().getState(false).getTimeStamp();
}
}
/**
* Check if the cache for this entry is stale compared to the modification stamp, or if the modification stamp itself is stale, meaning
* subclass was stale already.
*
* @param requestStamp the timestamp to compare against.
* @return <code>true</code> if we can use the incoming cache stamp.
*
* @since 1.1.0
*/
protected boolean canUseCache(long requestStamp) {
long modStamp;
// Now get the current mod stamp for this class so we can compare it.
synchronized (this) {
// We may already have a class entry due to a subclass doing a check, so if we do, we'll use it. Else we'll get the latest one.
if (classEntry == null)
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null) {
// We have a cache to check.
modStamp = classEntry.getModificationStamp();
// A sanity check, if this was an old, but now deleted one we want to throw it away and get it again. It may now be valid.
if (modStamp == BeanInfoCacheController.ClassEntry.DELETED_MODIFICATION_STAMP) {
classEntry = BeanInfoCacheController.INSTANCE.getClassEntry(getJavaClass());
if (classEntry != null)
modStamp = classEntry.getModificationStamp();
}
if (modStamp == IResource.NULL_STAMP && modStamp == BeanInfoCacheController.ClassEntry.DELETED_MODIFICATION_STAMP)
return false; // Since we are stale, child asking question must also be stale and can't use the cache.
} else
return false; // We don't have a cache entry to check against, so child must be stale too.
}
// If the requested stamp is not the same as ours, then it is out of date (it couldn't be newer but it could be older).
return requestStamp == modStamp;
}
/*
* This should only be called through introspectIfNecessary so that flags are set correctly.
*/
private void introspect(boolean doOperations) {
IBeanProxy beaninfo = null;
try {
if (isResourceConnected()) {
// See if are valid kind of class.
if (getJavaClass().getKind() == TypeKind.UNDEFINED_LITERAL) {
// Not valid, don't let any further introspection occur.
// Mark that we've done all introspections so as not to waste time until we get notified that it has been added
// back in.
synchronized(this) {
if (classEntry != null) {
classEntry.markDeleted(); // mark it deleted in case still sitting in cache (maybe because it was in an external jar, we can't know if deleted when jar changed).
classEntry = null; // Get rid of it since now deleted.
}
needsIntrospection = false;
}
if (retrievedExtensionDocument == RETRIEVED_FULL_DOCUMENT || retrievedExtensionDocument == CLEAR_EXTENSIONS) {
// We've been defined at one point. Need to clear everything and step back
// to never retrieved so that we now get the root added in. If we had been
// previously defined, then we didn't have root. We will have to lose
// all other updates too. But they can come back when we're defined.
// Or we've been asked to clear all.
clearAll();
retrievedExtensionDocument = NEVER_RETRIEVED_EXTENSION_DOCUMENT;
}
if (retrievedExtensionDocument == NEVER_RETRIEVED_EXTENSION_DOCUMENT)
applyExtensionDocument(true); // Add in Root stuff so that it will work correctly even though undefined.
} else {
// TODO For now cause recycle of vm if any super type is stale so that if registry is stale for the super type it will be
// recreated. This is needed because reflection requires superProperties, etc. and recreating the registry
// currently re-marks everyone as stale, including this subclass. This would mean that
// re-introspection would need to be done, even though we just did it. The stale registry business needs to be re-addressed so that it is
// a lot smarter.
List supers = getJavaClass().getEAllSuperTypes();
for (int i = 0; i < supers.size(); i++) {
BeaninfoClassAdapter bca = (BeaninfoClassAdapter) EcoreUtil.getExistingAdapter((EObject) supers.get(i),
IIntrospectionAdapter.ADAPTER_KEY);
if (bca != null && bca.isStale())
bca.getRegistry();
}
// Now we need to force introspection, if needed, of all parents because we need them to be up-to-date for the
// class entry.
// TODO see if we can come up with a better way that we KNOW the CE is correct, even if any supers are stale.
supers = getJavaClass().getESuperTypes();
for (int i = 0; i < supers.size(); i++) {
BeaninfoClassAdapter bca = (BeaninfoClassAdapter) EcoreUtil.getRegisteredAdapter((EObject) supers.get(i),
IIntrospectionAdapter.ADAPTER_KEY);
bca.introspectIfNecessary();
}
TimerTests.basicTest.startCumulativeStep(INTROSPECT);
if (retrievedExtensionDocument == RETRIEVED_ROOT_ONLY || retrievedExtensionDocument == CLEAR_EXTENSIONS) {
// We need to clear out EVERYTHING because we are coming from an undefined to a defined.
// Nothing previous is now valid. (Particularly the root stuff).
// Or we had a Clean requested and need to clear the extensions too.
clearAll();
}
boolean firstTime = false;
if (retrievedExtensionDocument != RETRIEVED_FULL_DOCUMENT) {
firstTime = true; // If we need to apply the extension doc, then this is the first time.
applyExtensionDocument(false); // Apply the extension doc before we do anything.
}
// Now check to see if we can use the cache.
boolean doIntrospection = true;
if (firstTime) {
// Check if we can use the cache. Use Max value so that first level test (ourself) will always pass and go on to the supers.
if (canUseCache()) {
TimerTests.basicTest.startCumulativeStep(LOAD_FROM_CACHE);
// We can use the cache.
Resource cres = BeanInfoCacheController.INSTANCE.getCache(getJavaClass(), classEntry, true);
if (cres != null) {
try {
// Got a cache to use, now apply it.
for (Iterator cds = cres.getContents().iterator(); cds.hasNext();) {
ChangeDescription cacheCD = (ChangeDescription) cds.next();
cacheCD.apply();
}
// We need to walk through and create the appropriate ID's for events/actions/properties because by
// default from the change descriptions these don't get reflected back. And if some one doesn't
// use an ID to get them, they won't have an id set.
doIDs();
doIntrospection = false;
} catch (RuntimeException e) {
BeaninfoPlugin.getPlugin().getLogger().log(
MessageFormat.format(
BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_,
new Object[] { getJavaClass().getJavaName(), ""}), //$NON-NLS-1$
Level.WARNING);
BeaninfoPlugin.getPlugin().getLogger().log(e);
} finally {
// Remove the cres since it is now invalid. The applies cause them to be invalid.
cres.getResourceSet().getResources().remove(cres);
if (doIntrospection) {
// Apply had failed. We don't know how far it went. We need to wipe out and reget EVERYTHING.
clearAll();
applyExtensionDocument(false); // Re-apply the extension doc before we do anything else.
}
}
}
TimerTests.basicTest.stopCumulativeStep(LOAD_FROM_CACHE);
}
}
if (doIntrospection) {
// Finally we can get to handling ourselves.
TimerTests.basicTest.startCumulativeStep(REMOTE_INTROSPECT);
BeanDecorator decor = Utilities.getBeanDecorator(getJavaClass());
if (decor == null) {
decor = BeaninfoFactory.eINSTANCE.createBeanDecorator();
decor.setImplicitDecoratorFlag(ImplicitItem.IMPLICIT_DECORATOR_LITERAL);
getJavaClass().getEAnnotations().add(decor);
} else
BeanInfoDecoratorUtility.clear(decor); // Clear out previous results.
boolean doReflection = true;
if (doOperations)
newoperations = new HashSet(50);
if (decor.isDoBeaninfo()) {
int doFlags = 0;
if (decor == null || decor.isMergeIntrospection())
doFlags |= IBeanInfoIntrospectionConstants.DO_BEAN_DECOR;
if (decor == null || decor.isIntrospectEvents())
doFlags |= IBeanInfoIntrospectionConstants.DO_EVENTS;
if (decor == null || decor.isIntrospectProperties())
doFlags |= IBeanInfoIntrospectionConstants.DO_PROPERTIES;
if (doOperations && (decor == null || decor.isIntrospectMethods()))
doFlags |= IBeanInfoIntrospectionConstants.DO_METHODS;
if (doFlags != 0) {
// There was something remote to do.
IBeanTypeProxy targetType = null;
ProxyFactoryRegistry registry = getRegistry();
if (registry != null && registry.isValid())
targetType = registry.getBeanTypeProxyFactory().getBeanTypeProxy(getJavaClass().getQualifiedNameForReflection());
if (targetType != null) {
if (targetType.getInitializationError() == null) {
// If an exception is thrown, treat this as no proxy, however log it because we
// shouldn't have exceptions during introspection, but if we do it should be logged
// so it can be corrected.
try {
BeaninfoProxyConstants proxyConstants = getProxyConstants();
if (proxyConstants != null) {
beaninfo = proxyConstants.getIntrospectProxy().invoke(
null,
new IBeanProxy[] { targetType,
getRegistry().getBeanProxyFactory().createBeanProxyWith(false),
getRegistry().getBeanProxyFactory().createBeanProxyWith(doFlags)});
}
} catch (ThrowableProxy e) {
BeaninfoPlugin.getPlugin().getLogger().log(
new Status(IStatus.WARNING, BeaninfoPlugin.getPlugin().getBundle().getSymbolicName(), 0,
MessageFormat.format(BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_, new Object[] { //$NON-NLS-1$
getJavaClass().getJavaName(), ""}), //$NON-NLS-1$
e));
}
} else {
// The class itself couldn't be initialized. Just log it, but treat as no proxy.
BeaninfoPlugin.getPlugin().getLogger()
.log(
new Status(IStatus.WARNING, BeaninfoPlugin.getPlugin().getBundle().getSymbolicName(), 0,
MessageFormat.format(BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_, new Object[] { //$NON-NLS-1$
getJavaClass().getJavaName(), targetType.getInitializationError()}), null));
}
} else {
// The class itself could not be found. Just log it, but treat as no proxy.
BeaninfoPlugin.getPlugin().getLogger().log(
new Status(IStatus.INFO, BeaninfoPlugin.getPlugin().getBundle().getSymbolicName(), 0, MessageFormat.format(
BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_, new Object[] {
getJavaClass().getJavaName(),
BeanInfoAdapterMessages.BeaninfoClassAdapter_ClassNotFound}),
null));
}
if (beaninfo != null) {
doReflection = false; // We have a beaninfo, so we are doing introspection.
final BeanDecorator bdecor = decor;
// We have a beaninfo to process.
BeanInfoDecoratorUtility.introspect(beaninfo, new BeanInfoDecoratorUtility.IntrospectCallBack() {
/*; (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.adapters.BeanInfoDecoratorUtility.IntrospectCallBack#process(org.eclipse.jem.internal.beaninfo.common.BeanRecord)
*/
public BeanDecorator process(BeanRecord record) {
return bdecor;
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.adapters.BeanInfoDecoratorUtility.IntrospectCallBack#process(org.eclipse.jem.internal.beaninfo.common.PropertyRecord)
*/
public PropertyDecorator process(PropertyRecord record) {
return calculateProperty(record, false);
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.adapters.BeanInfoDecoratorUtility.IntrospectCallBack#process(org.eclipse.jem.internal.beaninfo.common.IndexedPropertyRecord)
*/
public PropertyDecorator process(IndexedPropertyRecord record) {
return calculateProperty(record, true);
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.adapters.BeanInfoDecoratorUtility.IntrospectCallBack#process(org.eclipse.jem.internal.beaninfo.common.MethodRecord)
*/
public MethodDecorator process(MethodRecord record) {
return calculateOperation(record);
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.adapters.BeanInfoDecoratorUtility.IntrospectCallBack#process(org.eclipse.jem.internal.beaninfo.common.EventSetRecord)
*/
public EventSetDecorator process(EventSetRecord record) {
return calculateEvent(record);
}
});
}
}
}
if (doReflection) {
// Need to do reflection stuff.
if (decor.isIntrospectProperties())
reflectProperties();
if (doOperations && decor.isIntrospectMethods())
reflectOperations();
if (decor.isIntrospectEvents())
reflectEvents();
}
ChangeDescription cd = ChangeFactory.eINSTANCE.createChangeDescription();
BeanInfoDecoratorUtility.buildChange(cd, decor);
finalizeProperties(cd);
if (doOperations)
finalizeOperations(cd);
finalizeEvents(cd);
classEntry = BeanInfoCacheController.INSTANCE.newCache(getJavaClass(), cd, doOperations ? BeanInfoCacheController.REFLECTION_OPERATIONS_CACHE : BeanInfoCacheController.REFLECTION_CACHE);
TimerTests.basicTest.stopCumulativeStep(REMOTE_INTROSPECT);
}
TimerTests.basicTest.stopCumulativeStep(INTROSPECT);
}
getAdapterFactory().registerIntrospection(getJavaClass().getQualifiedNameForReflection(), this);
}
} finally {
if (beaninfo != null) {
beaninfo.getProxyFactoryRegistry().releaseProxy(beaninfo); // Dispose of the beaninfo since we now have everything.
}
eventsMap = null; // Clear out the temp lists.
eventsRealList = null;
operationsMap = null; // Clear out the temp lists.
operationsRealList = null;
newoperations = null;
propertiesMap = null; // Get rid of accumulated map.
featuresRealList = null; // Release the real list.
}
}
private void doIDs() {
// Do properties.
if (getJavaClass().eIsSet(EcorePackage.eINSTANCE.getEClass_EStructuralFeatures())) {
List features = getFeaturesList();
int len = features.size();
for (int i = 0; i < len; i++) {
EStructuralFeature f = (EStructuralFeature) features.get(i);
PropertyDecorator pd = Utilities.getPropertyDecorator(f);
if (pd == null || !pd.isMergeIntrospection())
continue; // Not a property for us to give an ID to.
setPropertyID(f.getName(), f);
}
}
// Do events.
if (getJavaClass().eIsSet(JavaRefPackage.eINSTANCE.getJavaClass_Events())) {
List events = getEventsList();
int len = events.size();
for (int i = 0; i < len; i++) {
BeanEvent e = (BeanEvent) events.get(i);
EventSetDecorator ed = Utilities.getEventSetDecorator(e);
if (ed == null || !ed.isMergeIntrospection())
continue; // Not an event for us to give an ID to.
setEventID(e.getName(), e);
}
}
// Do Operations.
if (getJavaClass().eIsSet(EcorePackage.eINSTANCE.getEClass_EOperations())) {
List ops = getOperationsList();
int len = ops.size();
for (int i = 0; i < len; i++) {
EOperation o = (EOperation) ops.get(i);
MethodDecorator md = Utilities.getMethodDecorator(o);
if (md == null || !md.isMergeIntrospection())
continue; // Not an event for us to give an ID to.
setMethodID(o.getName(), o);
}
}
}
private static final String ROOT_OVERRIDE = BeaninfoPlugin.ROOT+'.'+BeaninfoPlugin.OVERRIDE_EXTENSION; //$NON-NLS-1$
protected void applyExtensionDocument(boolean rootOnly) {
try {
TimerTests.basicTest.startCumulativeStep(APPLY_EXTENSIONS);
boolean canUseCache = !rootOnly && canUseOverrideCache();
boolean alreadyRetrievedRoot = retrievedExtensionDocument == RETRIEVED_ROOT_ONLY;
retrievedExtensionDocument = rootOnly ? RETRIEVED_ROOT_ONLY : RETRIEVED_FULL_DOCUMENT;
JavaClass jc = getJavaClass();
Resource mergeIntoResource = jc.eResource();
ResourceSet rset = mergeIntoResource.getResourceSet();
String className = jc.getName();
if (canUseCache) {
// We can use the cache, see if we actually have one.
if (getClassEntry().overrideCacheExists()) {
// Get the cache and apply it before anything else.
Resource cacheRes = BeanInfoCacheController.INSTANCE.getCache(jc, getClassEntry(), false);
if (cacheRes != null) {
try {
new ExtensionDocApplies(null, rset, jc, null).run(cacheRes);
} catch (WrappedException e) {
BeaninfoPlugin.getPlugin().getLogger().log(
new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0,
"Error processing file\"" + cacheRes.getURI() + "\"", e.exception())); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
cacheRes.getResourceSet().getResources().remove(cacheRes); // We don't need it around once we do the merge. Normal merge would of gotton rid of it, but in case of error we do it here.
}
} else
canUseCache = false; // Need to rebuild the cache.
}
}
List overrideCache = null;
if (!alreadyRetrievedRoot && (rootOnly || jc.getSupertype() == null)) {
// It is a root class. Need to merge in root stuff.
if (!canUseCache) {
overrideCache = createOverrideCache(overrideCache, getAdapterFactory().getProject(), BeaninfoPlugin.ROOT, ROOT_OVERRIDE, rset, jc);
}
applyExtensionDocTo(rset, jc, ROOT_OVERRIDE, BeaninfoPlugin.ROOT, BeaninfoPlugin.ROOT);
if (rootOnly)
return;
}
String baseOverridefile = className + '.' + BeaninfoPlugin.OVERRIDE_EXTENSION; // getName() returns inner classes with "$" notation, which is good. //$NON-NLS-1$
String packageName = jc.getJavaPackage().getPackageName();
if (!canUseCache) {
overrideCache = createOverrideCache(overrideCache, getAdapterFactory().getProject(), packageName, baseOverridefile, rset, jc);
}
applyExtensionDocTo(rset, jc, baseOverridefile, packageName, className);
if (!canUseCache) {
// We have an override cache to store. If the cache is null, this will flag that there is no override cache for the current configuration. That way we won't bother trying again until config changes.
BeanInfoCacheController.INSTANCE.newCache(jc, overrideCache, BeanInfoCacheController.OVERRIDES_CACHE);
}
} finally {
TimerTests.basicTest.stopCumulativeStep(APPLY_EXTENSIONS);
}
}
/*
* Build up the fixed overrides into the cache, and apply as we gather them.
* Return the cache or null if the cache is empty at the end.
*/
private List createOverrideCache(List cache, IProject project, String packageName, String overrideFile, ResourceSet rset, JavaClass mergeIntoJavaClass) {
// Now get the overrides paths
String[] paths = BeaninfoPlugin.getPlugin().getOverridePaths(project, packageName);
if (paths.length == 0)
return cache;
// Now apply the overrides.
if (cache == null)
cache = new ArrayList();
BeaninfoPlugin.IOverrideRunnable runnable = new ExtensionDocApplies(overrideFile, rset, mergeIntoJavaClass, cache);
for (int i = 0; i < paths.length; i++) {
runnable.run(paths[i]);
}
return !cache.isEmpty() ? cache : null;
}
private static final URI ROOT_URI = URI.createGenericURI(BeaninfoPlugin.ROOT_SCHEMA, BeaninfoPlugin.ROOT_OPAQUE, null);
private static final String ROOT_FRAGMENT = "//@root"; //$NON-NLS-1$
private static final Pattern FRAGMENT_SPLITTER = Pattern.compile("/"); //$NON-NLS-1$
// TODO This is to make event util optional. This should be removed entirely with 1.3 when event util goes away.
private static boolean RETRIEVED_EVENT_METHODS;
private static Object EVENT_FACTORY_INSTANCE;
private static java.lang.reflect.Method CREATE_EVENT_UTIL_METHOD;
private static java.lang.reflect.Method DO_FORWARD_EVENTS_METHOD;
private static boolean isEventUtilLoaded() {
if (!RETRIEVED_EVENT_METHODS) {
try {
Class eventFactoryClass = Class.forName("com.ibm.etools.emf.event.EventFactory");
Field eventFactoryField = eventFactoryClass.getField("eINSTANCE");
Object eventFactoryInstance = eventFactoryField.get(null);
java.lang.reflect.Method createEventMethod = eventFactoryClass.getMethod("createEventUtil", new Class[] {Notifier.class, ResourceSet.class});
Class eventUtilClass = createEventMethod.getReturnType();
java.lang.reflect.Method doForwardEventsMethod = eventUtilClass.getMethod("doForwardEvents", new Class[] {List.class});
EVENT_FACTORY_INSTANCE = eventFactoryInstance;
CREATE_EVENT_UTIL_METHOD = createEventMethod;
DO_FORWARD_EVENTS_METHOD = doForwardEventsMethod;
} catch (ClassNotFoundException e) {
} catch (SecurityException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (NoSuchMethodException e) {
}
RETRIEVED_EVENT_METHODS = true;
}
return EVENT_FACTORY_INSTANCE != null;
}
private class ExtensionDocApplies implements BeaninfoPlugin.IOverrideRunnable {
private final String overrideFile;
private final ResourceSet rset;
private final JavaClass mergeIntoJavaClass;
private final List overridesCache;
public ExtensionDocApplies(String overrideFile, ResourceSet rset, JavaClass mergeIntoJavaClass, List overridesCache) {
this.overrideFile = overrideFile;
this.rset = rset;
this.mergeIntoJavaClass = mergeIntoJavaClass;
this.overridesCache = overridesCache;
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.core.BeaninfoPlugin.IOverrideRunnable#run(java.lang.String)
*/
public void run(String overridePath) {
Resource overrideRes = null;
URI uri = URI.createURI(overridePath+overrideFile);
try {
overrideRes = rset.getResource(uri, true);
run(overrideRes);
} catch (WrappedException e) {
// FileNotFoundException is ok
if (!(e.exception() instanceof FileNotFoundException)) {
if (e.exception() instanceof CoreException
&& ((CoreException) e.exception()).getStatus().getCode() == IResourceStatus.RESOURCE_NOT_FOUND) {
// This is ok. Means uri_mapping not set so couldn't find in Workspace, also ok.
} else if (e.exception() instanceof PackageNotFoundException && ((PackageNotFoundException) e.exception()).getMessage().indexOf("event.xmi") != -1) {
if (!RETRIEVED_EVENT_METHODS) {
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "An old style override file using the com.ibm.event format was found, but com.ibm.event was not installed. The first such file is "+uri, null)); //$NON-NLS-1$
RETRIEVED_EVENT_METHODS = true; // Mark we've retrieved. This is ok because if event is missing here, it will be missing for retrieve too.
}
} else {
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "Error loading file\"" + uri + "\"", e.exception())); //$NON-NLS-1$ //$NON-NLS-2$
}
}
// In case it happened after creating resource but during load. Need to get rid of it in the finally.
overrideRes = rset.getResource(uri, false);
} catch (Exception e) {
// Couldn't load it for some reason.
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "Error loading file\"" + uri + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
overrideRes = rset.getResource(uri, false); // In case it happened after creating resource but during load so that we can get rid of it.
} finally {
if (overrideRes != null)
rset.getResources().remove(overrideRes); // We don't need it around once we do the merge. Normal merge would of gotton rid of it, but in case of error we do it here.
}
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.beaninfo.core.BeaninfoPlugin.IOverrideRunnable#run(org.eclipse.emf.ecore.resource.Resource)
*/
public void run(Resource overrideRes) {
try {
EList contents = overrideRes.getContents();
if (overridesCache != null) {
// TODO Until https://bugs.eclipse.org/bugs/show_bug.cgi?id=109169 is fixed, we need our own Copier.
EcoreUtil.Copier copier = new EcoreUtil.Copier() {
private static final long serialVersionUID = 1L;
protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject) {
if (eObject.eIsSet(eReference)) {
if (eReference.isMany()) {
List source = (List) eObject.eGet(eReference);
InternalEList target = (InternalEList) copyEObject.eGet(getTarget(eReference));
if (source.isEmpty()) {
target.clear();
} else {
boolean isBidirectional = eReference.getEOpposite() != null;
int index = 0;
for (Iterator k = ((EcoreEList) source).basicIterator(); k.hasNext();) {
Object referencedEObject = k.next();
Object copyReferencedEObject = get(referencedEObject);
if (copyReferencedEObject == null) {
if (!isBidirectional) {
target.addUnique(index, referencedEObject);
++index;
}
} else {
if (isBidirectional) {
int position = target.indexOf(copyReferencedEObject);
if (position == -1) {
target.addUnique(index, copyReferencedEObject);
} else if (index != position) {
target.move(index, copyReferencedEObject);
}
} else {
target.addUnique(index, copyReferencedEObject);
}
++index;
}
}
}
} else {
Object referencedEObject = eObject.eGet(eReference, false);
if (referencedEObject == null) {
copyEObject.eSet(getTarget(eReference), null);
} else {
Object copyReferencedEObject = get(referencedEObject);
if (copyReferencedEObject == null) {
if (eReference.getEOpposite() == null) {
copyEObject.eSet(getTarget(eReference), referencedEObject);
}
} else {
copyEObject.eSet(getTarget(eReference), copyReferencedEObject);
}
}
}
}
}
};
// We fixup the CD first so that when serialized it will be to the correct object already. Simplifies apply later.
Iterator itr = contents.iterator();
while (itr.hasNext()) {
Object o = itr.next();
if (o instanceof ChangeDescription) {
fixupCD((ChangeDescription) o);
}
}
Collection result = copier.copyAll(contents);
copier.copyReferences();
overridesCache.addAll(result); // Make a copy for the override cache to be used next time needed.
}
if (!contents.isEmpty()) {
// TODO It could be either the old event model or the new ChangeDescription. When we remove Event Model we need to remove
// the test. This could be the override cache too, so we must apply the contents in the order they are in the resource.
try {
List events = null;
Object[] eventsParm = null;
Iterator itr = contents.iterator();
while (itr.hasNext()) {
Object o = itr.next();
if (o instanceof ChangeDescription) {
ChangeDescription cd = (ChangeDescription) o;
fixupCD(cd);
cd.apply();
} else if (isEventUtilLoaded()) {
if (events == null) {
events = new ArrayList(1);
events.add(null); // EventUtil needs a list, but we will be calling one by one. So just reuse this list.
eventsParm = new Object[] { events};
}
// It is the old format.
events.set(0, o);
try {
Object util = CREATE_EVENT_UTIL_METHOD.invoke(EVENT_FACTORY_INSTANCE, new Object[] {mergeIntoJavaClass, rset});
DO_FORWARD_EVENTS_METHOD.invoke(util, eventsParm);
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "Error processing file\"" + overrideRes.getURI() + "\"", e.getCause())); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
} finally {
uninstallRootResource();
}
}
} catch (WrappedException e) {
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "Error processing file\"" + overrideRes.getURI() + "\"", e.exception())); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/*
* fix it up so that references to "X:ROOT#//@root" are replaced with reference to the javaclass.
*/
private void fixupCD(ChangeDescription cd) {
EStructuralFeature keyFeature = ChangePackage.eINSTANCE.getEObjectToChangesMapEntry_Key();
Iterator changes = cd.getObjectChanges().iterator();
while (changes.hasNext()) {
EObject entry = (EObject) changes.next();
EObject key = (EObject) entry.eGet(keyFeature, false);
if (key != null && key.eIsProxy()) {
// See if it is our special.
URI uri = ((InternalEObject) key).eProxyURI();
String rootFrag = uri.fragment();
if (BeaninfoPlugin.ROOT_SCHEMA.equals(uri.scheme()) && BeaninfoPlugin.ROOT_OPAQUE.equals(uri.opaquePart()) && (rootFrag != null && rootFrag.startsWith(ROOT_FRAGMENT))) {
// It is one of ours. Get the fragment, remove the leading //root and append to the end of the
// uri from the java class itself.
if (rootFrag.length() <= ROOT_FRAGMENT.length())
entry.eSet(keyFeature, mergeIntoJavaClass); // No sub-path, so just the java class.
else {
// There is a sub-path below the java class that is needed. Need to walk down the path.
String[] path = FRAGMENT_SPLITTER.split(rootFrag.substring(ROOT_FRAGMENT.length()+1));
InternalEObject newKey = (InternalEObject) mergeIntoJavaClass;
for (int i = 0; newKey != null && i < path.length; i++) {
newKey = (InternalEObject) newKey.eObjectForURIFragmentSegment(path[i]);
}
if (newKey != null)
entry.eSet(keyFeature, newKey);
}
}
}
}
}
/*
* Uninstall the fake root resource. This must be called after installRootResource has been called. So must use try/finally.
*
*
* @since 1.2.0
*/
private void uninstallRootResource() {
Resource root = rset.getResource(ROOT_URI, false);
if (root != null)
rset.getResources().remove(root);
}
}
protected void applyExtensionDocTo(ResourceSet rset, JavaClass mergeIntoJavaClass, String overrideFile, String packageName, String className) {
BeaninfoPlugin.getPlugin().applyOverrides(getAdapterFactory().getProject(), packageName, className, mergeIntoJavaClass, rset,
new ExtensionDocApplies(overrideFile, rset, mergeIntoJavaClass, null));
}
/**
* Return the target as a JavaClass
*/
protected JavaClassImpl getJavaClass() {
return (JavaClassImpl) getTarget();
}
/**
* Answer the beaninfo constants record
*/
protected BeaninfoProxyConstants getProxyConstants() {
return BeaninfoProxyConstants.getConstants(getRegistry());
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getEStructuralFeatures()
*/
public EList getEStructuralFeatures() {
introspectIfNecessary();
return getJavaClass().getEStructuralFeaturesInternal();
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getAllProperties()
*/
public EList getAllProperties() {
return allProperties();
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getEOperations()
*/
public EList getEOperations() {
if (getClassEntry() != null && getClassEntry().isOperationsStored())
introspectIfNecessary(); // Already stored, just do if necessary.
else {
synchronized (this) {
needsIntrospection = true; // Force reintrospection because we either aren't storing operations, or have never loaded.
}
introspectIfNecessary(true); // But force the operations now.
}
return getJavaClass().getEOperationsInternal();
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getEAllOperations()
*/
public BasicEList getEAllOperations() {
return allOperations();
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getEvents()
*/
public EList getEvents() {
introspectIfNecessary();
return getJavaClass().getEventsGen();
}
/**
* @see org.eclipse.jem.java.beaninfo.IIntrospectionAdapter#getAllEvents()
*/
public EList getAllEvents() {
return allEvents();
}
private void finalizeProperties(ChangeDescription cd) {
// Now go through the list and remove those that should be removed, and set the etype for those that don't have it set.
Map oldLocals = getPropertiesMap();
Iterator itr = getFeaturesList().iterator();
while (itr.hasNext()) {
EStructuralFeature a = (EStructuralFeature) itr.next();
PropertyDecorator p = Utilities.getPropertyDecorator(a);
Object aOld = oldLocals.get(a.getName());
if (aOld != null && aOld != Boolean.FALSE) {
// A candidate for removal. It was in the old list and it was not processed.
if (p != null) {
ImplicitItem implicit = p.getImplicitDecoratorFlag();
if (implicit != ImplicitItem.NOT_IMPLICIT_LITERAL) {
p.setEModelElement(null); // Remove from the feature;
((InternalEObject) p).eSetProxyURI(BAD_URI);
// Mark it as bad proxy so we know it is no longer any use.
p = null;
if (implicit == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
itr.remove(); // Remove it, this was implicitly created and not processed this time.
((InternalEObject) a).eSetProxyURI(BAD_URI); // Mark it as bad proxy so we know it is no longer any use.
continue;
}
// Need to go on because we need to check the eType to make sure it is set. At this point we have no decorator but we still have a feature.
}
}
}
// [79083] Also check for eType not set, and if it is, set it to EObject type. That way it will be valid, but not valid as
// a bean setting.
if (a.getEType() == null) {
// Set it to EObject type. If it becomes valid later (through the class being changed), then the introspect/reflect
// will set it to the correct type.
a.setEType(EcorePackage.eINSTANCE.getEObject());
Logger logger = BeaninfoPlugin.getPlugin().getLogger();
if (logger.isLoggingLevel(Level.WARNING))
logger.logWarning("Feature \""+getJavaClass().getQualifiedName()+"->"+a.getName()+"\" did not have a type set. Typically due to override file creating feature but property not found on introspection/reflection."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (p != null && cd != null) {
// Now create the appropriate cache entry for this property.
BeanInfoDecoratorUtility.buildChange(cd, p);
}
}
}
/**
* merge all the Properties (i.e. supertypes) (properties)
*/
protected EList allProperties() {
EList jcAllProperties = getJavaClass().getAllPropertiesGen();
BeaninfoSuperAdapter superAdapter =
(BeaninfoSuperAdapter) EcoreUtil.getRegisteredAdapter(getJavaClass(), BeaninfoSuperAdapter.class);
if (jcAllProperties != null) {
// See if new one required.
if (superAdapter == null || !superAdapter.isAllPropertiesCollectionModified())
return jcAllProperties;
// Can't get superadapter, so must not be attached to a resource, so return current list. Or no change required.
}
UniqueEList.FastCompare allProperties = new UniqueEList.FastCompare() {
/**
* Comment for <code>serialVersionUID</code>
*
* @since 1.1.0
*/
private static final long serialVersionUID = 1L;
protected Object[] newData(int capacity) {
return new EStructuralFeature[capacity];
}
};
boolean doAllProperties = false;
synchronized(this) {
// If we are introspecting, don't do all properties because it is an invalid list. Just return empty without reseting the all modified flag.
doAllProperties = !isDoingAllProperties && !isIntrospecting && isResourceConnected();
if (doAllProperties)
isDoingAllProperties = true;
}
if (doAllProperties) {
try {
EList localProperties = getJavaClass().getProperties();
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types and still keep order.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
// Now we need to merge in the supers.
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
// If there is only one supertype, we can add to the actual events, else we will use the linked hashset so that
// we don't add possible duplicates (e.g. IA extends IB,IC and IB extends IC. In this case there would be dups
// because IB would contribute IC's too).
Collection workingAllProperties = superTypes.size() == 1 ? (Collection) allProperties : new LinkedHashSet();
// We will now merge as directed.
boolean mergeAll = bd == null || bd.isMergeSuperProperties();
if (!mergeAll) {
// we don't to want to merge super properties, but we still need super non-properties or explict ones.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getAllProperties();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
EStructuralFeature p = (EStructuralFeature) supers.get(i1);
PropertyDecorator pd = Utilities.getPropertyDecorator(p);
if ( pd == null || (pd.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !pd.isMergeIntrospection()))
workingAllProperties.add(p);
}
}
} else {
// We want to merge all.
// BeanInfo could of given us the not merge list. If the list is empty, then we accept all.
if (bd != null && bd.eIsSet(BeaninfoPackage.eINSTANCE.getBeanDecorator_NotInheritedPropertyNames())) {
// We were given a list of names.
// Get the names into a set to create a quick lookup.
HashSet superSet = new HashSet(bd.getNotInheritedPropertyNames());
// Now walk and add in non-bean properties (and bean properties that were explicitly added and not mergeable (i.e. didn't come thru beaninfo))
// and add those not specifically called out by BeanInfo in the not inherited list.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getAllProperties();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
EStructuralFeature p = (EStructuralFeature) supers.get(i1);
PropertyDecorator pd = Utilities.getPropertyDecorator(p);
if (pd == null || (pd.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !pd.isMergeIntrospection()) || !superSet.contains(pd.getName()))
workingAllProperties.add(p);
}
}
} else {
// BeanInfo (or reflection always does this) called out that all super properties are good
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
workingAllProperties.addAll(((JavaClass) superTypes.get(i)).getAllProperties());
}
}
}
if (workingAllProperties != allProperties)
allProperties.addAll(workingAllProperties); // Now copy over the ordered super properties.
}
allProperties.addAll(localProperties);
superAdapter.setAllPropertiesCollectionModified(false); // Now built, so reset to not changed.
} finally {
synchronized(this) {
isDoingAllProperties = false;
}
}
}
if (!allProperties.isEmpty()) {
allProperties.shrink();
return new EcoreEList.UnmodifiableEList.FastCompare(
getJavaClass(),
null,
allProperties.size(),
allProperties.data());
} else
return ECollections.EMPTY_ELIST;
}
/*
* Fill in the property using the prop record. If this one that should have merging done, then
* return the PropertyDecorator so that it can have the PropertyRecord merged into it. Else
* return null if no merging.
*/
protected PropertyDecorator calculateProperty(PropertyRecord pr, boolean indexed) {
// If this is an indexed property, then a few fields will not be set in here, but
// will instead be set by the calculateIndexedProperty, which will be called.
JavaHelpers type = pr.propertyTypeName != null ? Utilities.getJavaType(MapJNITypes.getFormalTypeName(pr.propertyTypeName), getJavaClass().eResource().getResourceSet()) : null;
if (indexed) {
// If no array write method found, then see if there is an indexed write method. If there is, then it is changable.
if (type == null) {
// If no array type from above, create one from the indexed type proxy. Add '[]' to turn it into an array.
type = Utilities.getJavaType(MapJNITypes.getFormalTypeName(((IndexedPropertyRecord) pr).indexedPropertyTypeName)+"[]", getJavaClass().eResource().getResourceSet()); //$NON-NLS-1$
}
}
if (type != null)
return createProperty(pr.name, indexed, type); // A valid property descriptor.
else
return null;
}
/**
* Fill in the property and its decorator using the passed in information.
*/
protected PropertyDecorator createProperty(String name, boolean indexed, EClassifier type) {
// First find if there is already a property of this name, and if there is, is the PropertyDecorator
// marked to not allow merging in of introspection results.
HashMap existingLocals = getPropertiesMap();
EStructuralFeature prop = null;
PropertyDecorator pd = null;
Object p = existingLocals.get(name);
if (Boolean.FALSE == p)
return null; // We've already processed this name, can't process it again.
if (p != null) {
// We've found one of the same name. Whether we modify it or use it as is, we put in a
// special dummy in its place. That marks that we've already processed it and accepted it.
existingLocals.put(name, Boolean.FALSE);
// If the decorator for this entry says not to merge then return.
// If there is no PropertyDecorator, then we will merge. If they
// didn't want to merge then should of created of property decorator and said no merge.
pd = Utilities.getPropertyDecorator((EStructuralFeature) p);
if (pd != null && !pd.isMergeIntrospection())
return null;
prop = (EStructuralFeature) p;
}
// Need to test if this is an implicit decorator and it is not of the
// same type (i.e. is indexed now but wasn't or visa-versa, then we need
// to get rid of the decorator and recreate it. If it is not implicit, then
// we have to use it as is because the user specified, so it won't become
// an indexed if the user did not created it as an index, and visa-versa.
// Also if it is implicit, then we need to unset certain features that may of
// been set by a previous reflection which has now become introspected.
// When reflected we set the actual fields instead of the letting proxy determine them.
if (pd != null) {
if (pd.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL) {
// We can't change the type for explicit.
indexed = pd instanceof IndexedPropertyDecorator;
} else {
if ((indexed && !(pd instanceof IndexedPropertyDecorator)) || (!indexed && pd instanceof IndexedPropertyDecorator)) {
// It is implicit and of a different type, so just get rid of it and let it be recreated correctly.
prop.getEAnnotations().remove(pd);
pd = null;
}
}
if (pd != null)
if (indexed)
BeanInfoDecoratorUtility.clear((IndexedPropertyDecorator) pd);
else
BeanInfoDecoratorUtility.clear(pd);
}
ImplicitItem implicit = pd == null ? ImplicitItem.IMPLICIT_DECORATOR_LITERAL : pd.getImplicitDecoratorFlag();
if (prop == null) {
// We will create a new property.
// We can't have an implicit feature, but an explicit decorator.
getFeaturesList().add(prop = EcoreFactory.eINSTANCE.createEReference());
implicit = ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL;
}
setPropertyID(name, prop);
prop.setName(name);
prop.setTransient(false);
prop.setVolatile(false);
// Containment and Unsettable is tricky for EReferences. There is no way to know whether it has been explicitly set to false, or it defaulted to
// false because ECore has not made containment/unsettable an unsettable feature. So we need to instead use the algorithm of if we here
// created the feature, then we will by default set it to containment/unsettable. If it was created through diff merge, then
// we will leave it alone. It is the responsibility of the merge file writer to set containment/unsettable correctly.
if (implicit == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
prop.setUnsettable(true);
}
prop.setEType(type);
if (!indexed) {
prop.setLowerBound(0);
prop.setUpperBound(1);
} else {
prop.setLowerBound(0);
prop.setUpperBound(-1);
prop.setUnique(true);
}
// Now create/fill in the property descriptor for it.
// If there already is one then we
// will use it. This allows merging with beanfinfo.
if (pd == null) {
pd =
(!indexed)
? BeaninfoFactory.eINSTANCE.createPropertyDecorator()
: BeaninfoFactory.eINSTANCE.createIndexedPropertyDecorator();
pd.setImplicitDecoratorFlag(implicit);
prop.getEAnnotations().add(pd);
}
return pd;
}
/**
* @param name
* @param prop
*
* @since 1.1.0
*/
private void setPropertyID(String name, EStructuralFeature prop) {
// Now fill it in. Normal id for an attribute is "classname.attributename" but we can't use that
// for us because that format is used by Java Core for a field and there would be confusion.
// So we will use '/' instead.
((XMIResource) prop.eResource()).setID(prop, getJavaClass().getName() + BeaninfoJavaReflectionKeyExtension.FEATURE + name);
}
/*
* Reflect the properties. This requires going through local methods and matching them up to
* see if they are properties.
*/
private void reflectProperties() {
// If we are set to mergeSuperTypeProperties, then we need to get the super properties.
// This is so that duplicate any from super that we find here. When reflecting we don't
// allow discovered duplicates unless they are different types.
//
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
TimerTests.basicTest.startCumulativeStep(REFLECT_PROPERTIES);
Set supers = new HashSet(50);
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
if (bd == null || bd.isMergeSuperProperties()) {
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List superAll = ((JavaClass) superTypes.get(i)).getAllProperties();
int len = superAll.size();
for (int i1=0; i1<len; i1++) {
EStructuralFeature sf = (EStructuralFeature) superAll.get(i1);
supers.add(sf.getName());
}
}
}
}
// If any of the classes in the hierarchy are bound, then all reflected properties are considered bound.
boolean isBound = isDefaultBound();
if (!isBound) {
List superTypes = getJavaClass().getEAllSuperTypes();
// Start from end because that will be first class above the this one.
for (int i=superTypes.size()-1; !isBound && i>=0; i--) {
JavaClass spr = (JavaClass) superTypes.get(i);
BeaninfoClassAdapter bi = (BeaninfoClassAdapter) EcoreUtil.getExistingAdapter(spr, IIntrospectionAdapter.ADAPTER_KEY);
// They should all be reflected, but be on safe side, check if null.
if (bi != null)
isBound = bi.isDefaultBound();
}
}
HashMap props = new HashMap();
Iterator itr = getJavaClass().getPublicMethods().iterator();
while (itr.hasNext()) {
Method mthd = (Method) itr.next();
if (mthd.isStatic() || mthd.isConstructor())
continue; // Statics/constructors don't participate as properties
if (mthd.getName().startsWith("get")) { //$NON-NLS-1$
String name = java.beans.Introspector.decapitalize(mthd.getName().substring(3));
if (name.length() == 0 || supers.contains(name))
continue; // Had get(...) and not getXXX(...) so not a getter. Or this is the same name as a super, either way ignore it.
PropertyInfo propInfo = (PropertyInfo) props.get(name);
if (propInfo == null) {
propInfo = new PropertyInfo();
if (propInfo.setGetter(mthd, false))
props.put(name, propInfo);
} else
propInfo.setGetter(mthd, false);
} else if (mthd.getName().startsWith("is")) { //$NON-NLS-1$
String name = java.beans.Introspector.decapitalize(mthd.getName().substring(2));
if (name.length() == 0 || supers.contains(name))
continue; // Had is(...) and not isXXX(...) so not a getter. Or this is the same name as a super, either way ignore it.
PropertyInfo propInfo = (PropertyInfo) props.get(name);
if (propInfo == null) {
propInfo = new PropertyInfo();
if (propInfo.setGetter(mthd, true))
props.put(name, propInfo);
} else
propInfo.setGetter(mthd, true);
} else if (mthd.getName().startsWith("set")) { //$NON-NLS-1$
String name = java.beans.Introspector.decapitalize(mthd.getName().substring(3));
if (name.length() == 0 || supers.contains(name))
continue; // Had set(...) and not setXXX(...) so not a setter. Or this is the same name as a super, either way ignore it.
PropertyInfo propInfo = (PropertyInfo) props.get(name);
if (propInfo == null) {
propInfo = new PropertyInfo();
if (propInfo.setSetter(mthd))
props.put(name, propInfo);
} else
propInfo.setSetter(mthd);
}
}
// Now go through the hash map and create the properties.
itr = props.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry entry = (Map.Entry) itr.next();
((PropertyInfo) entry.getValue()).createProperty((String) entry.getKey(), isBound);
}
TimerTests.basicTest.stopCumulativeStep(REFLECT_PROPERTIES);
}
private class PropertyInfo {
public EClassifier type, indexedType;
public boolean constrained;
public Method getter, setter, indexedGetter, indexedSetter;
public boolean setGetter(Method get, boolean mustBeBoolean) {
List parms = get.getParameters();
if (parms.size() > 1)
return false; // Invalid - improper number of parms.
boolean indexed = parms.size() == 1;
if (indexed && !((JavaParameter) parms.get(0)).getEType().getName().equals("int")) //$NON-NLS-1$
return false; // Invalid - a parm that is not an int is invalid for indexed.
EClassifier retType = get.getReturnType();
if (retType == null || retType.getName().equals("void")) //$NON-NLS-1$
return false; // Invalid - must have a return type
if (mustBeBoolean && !retType.getName().equals("boolean")) //$NON-NLS-1$
return false; // Invalid - it must be a boolean.
if (indexed) {
if (indexedType != null) {
if (indexedType != retType)
return false; // Invalid - type is different from previous info.
}
if (type != null && !(((JavaHelpers) type).isArray() && ((ArrayType) type).getComponentType() == retType))
return false; // Invalid - indexed type doesn't match component type of base type.
} else {
if (type != null) {
if (type != retType)
return false; // Invalid - type is different from previous info.
}
if (indexedType != null && !(((JavaHelpers) retType).isArray() && ((ArrayType) retType).getComponentType() == indexedType))
if (type == null) {
// We had a potential indexed and had not yet found the regular type. We've now found
// the regular type, and it is not indexed. So it takes priority and will wipe out
// the indexed type.
indexedGetter = null;
indexedSetter = null;
indexedType = null;
} else
return false; // Invalid - indexed type doesn't match component type of base type we already have
}
if (indexed) {
if (indexedGetter != null)
return false; // Already have an indexed getter.
indexedGetter = get;
indexedType = retType;
} else {
if (getter != null)
return false; // Already have a getter
getter = get;
type = retType;
}
return true;
}
public boolean setSetter(Method set) {
List parms = set.getParameters();
if (parms.size() > 2 || parms.size() < 1)
return false; // Invalid - improper number of parms.
boolean indexed = parms.size() == 2;
if (indexed && !((JavaParameter) parms.get(0)).getEType().getName().equals("int")) //$NON-NLS-1$
return false; // Invalid - a parm that is not an int is invalid for indexed.
EClassifier retType = set.getReturnType();
if (retType != null && !retType.getName().equals("void")) //$NON-NLS-1$
return false; // Invalid - must not have a return type
EClassifier propType = null;
if (indexed) {
propType = ((JavaParameter) parms.get(1)).getEType();
if (indexedType != null) {
if (indexedType != propType)
return false; // Invalid - type is different from previous info.
}
if (type != null && !(((JavaHelpers) type).isArray() && ((ArrayType) type).getComponentType() == propType))
return false; // Invalid - indexed type doesn't match component type of base type, or base type not an array
} else {
propType = ((JavaParameter) parms.get(0)).getEType();
if (type != null) {
if (type != propType)
return false; // Invalid - type is different from previous info.
}
if (indexedType != null
&& !(((JavaHelpers) propType).isArray() && ((ArrayType) propType).getComponentType() == indexedType))
if (type == null) {
// We had a potential indexed and had not yet found the regular type. We've now found
// the regular type, and it is not indexed of the correct type. So it takes priority and will wipe out
// the indexed type.
indexedGetter = null;
indexedSetter = null;
indexedType = null;
} else
return false; // Invalid - indexed type doesn't match component type of base type from this setter.
}
if (indexed) {
if (indexedSetter != null)
return false; // Already have an indexed getter.
indexedSetter = set;
indexedType = propType;
} else {
if (setter != null)
return false; // Already have a getter
setter = set;
type = propType;
}
if (set.getJavaExceptions().contains(Utilities.getJavaClass("java.beans.PropertyVetoException", getJavaClass().eResource().getResourceSet()))) //$NON-NLS-1$
constrained = true;
return true;
}
public void createProperty(String name, boolean isBound) {
boolean indexed = indexedType != null;
if (indexed && type == null)
return; // A potential indexed, but never found the getter/setter of the regular type.
PropertyDecorator prop =
BeaninfoClassAdapter.this.createProperty(
name,
indexed,
type);
if (prop == null)
return; // Reflection not wanted.
indexed = prop instanceof IndexedPropertyDecorator; // It could of been forced back to not indexed if explicitly set.
// At this point in time all implicit settings have been cleared. This is done back in createProperty() method above.
// So now apply reflected settings on the property decorator.
BeanInfoDecoratorUtility.setProperties(prop, isBound, constrained, getter, setter);
if (indexed)
BeanInfoDecoratorUtility.setProperties((IndexedPropertyDecorator) prop, indexedGetter, indexedSetter);
}
};
/*
* Should only be called from introspect so that flags are correct.
*/
private void finalizeOperations(ChangeDescription cd) {
// Now go through the list and remove those that should be removed.
Iterator itr = getOperationsList().iterator();
while (itr.hasNext()) {
EOperation a = (EOperation) itr.next();
MethodDecorator m = Utilities.getMethodDecorator(a);
if (!newoperations.contains(a)) {
// A candidate for removal. It is in the list but we didn't add it. Check to see if it one we had created in the past.
// If no methoddecorator, then keep it, not one ours.
if (m != null) {
ImplicitItem implicit = m.getImplicitDecoratorFlag();
if (implicit != ImplicitItem.NOT_IMPLICIT_LITERAL) {
m.setEModelElement(null); // Remove it because it was implicit.
((InternalEObject) m).eSetProxyURI(BAD_URI);
if (implicit == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
itr.remove(); // The operation was implicit too.
((InternalEObject) a).eSetProxyURI(BAD_URI);
}
continue; // At this point we no longer have a method decorator, so go to next.
}
}
}
if (m != null && cd != null) {
// Now create the appropriate cache entry for this method.
BeanInfoDecoratorUtility.buildChange(cd, m);
}
}
}
/**
* Fill in the behavior and its decorator using the mthdDesc.
*/
protected MethodDecorator calculateOperation(MethodRecord record) {
return createOperation(record.name, formLongName(record), null, record);
}
/**
* Fill in the behavior and its decorator using the passed in information.
*/
protected MethodDecorator createOperation(String name, String longName, Method method, MethodRecord record) {
// First find if there is already a behavior of this name and method signature , and if there is, is the MethodDecorator
// marked to not allow merging in of introspection results.
HashMap existingLocals = getOperationsMap();
EOperation oper = null;
MethodDecorator md = null;
Object b = null;
if (name != null)
b = existingLocals.get(longName);
else
b = existingLocals.get(longName);
if (b != null) {
// If the decorator for this entry says not to merge then return.
// If there is no decorator, then we will merge. If they didn't want to
// merge, then they should of created a decorator with no merge on it.
md = Utilities.getMethodDecorator((EOperation) b);
if (md != null && !md.isMergeIntrospection())
return null;
oper = (EOperation) b;
}
// Need to find the method and method id.
if (method == null) {
// No method sent, create a proxy to it from the record.
method = BeanInfoDecoratorUtility.createJavaMethodProxy(record.methodForDescriptor);
}
ImplicitItem implicit = md == null ? ImplicitItem.IMPLICIT_DECORATOR_LITERAL : ImplicitItem.NOT_IMPLICIT_LITERAL;
if (oper == null) {
// We will create a new MethodProxy.
oper = BeaninfoFactory.eINSTANCE.createMethodProxy();
getOperationsList().add(oper);
implicit = ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL;
}
if (name == null)
name = method.getName();
// Now fill it in.
if (oper instanceof MethodProxy)
((MethodProxy) oper).setMethod(method);
setMethodID(name, oper);
oper.setName(name);
newoperations.add(oper);
// Now create/fill in the method decorator for it.
// If there already is one then we
// will use it. This allows merging with beaninfo.
if (md == null) {
md = BeaninfoFactory.eINSTANCE.createMethodDecorator();
md.setImplicitDecoratorFlag(implicit);
oper.getEAnnotations().add(md);
} else
BeanInfoDecoratorUtility.clear(md);
return md;
}
/**
* @param name
* @param oper
*
* @since 1.1.0
*/
private void setMethodID(String name, EOperation oper) {
((XMIResource) oper.eResource()).setID(oper, getJavaClass().getName() + BeaninfoJavaReflectionKeyExtension.BEHAVIOR + name);
}
private void reflectOperations() {
// If we are set to mergeSuperTypeBehaviors, then we need to get the super behaviors.
// This is so that duplicate any from super that we find here. When reflecting we don't
// allow discovered duplicates unless they are different signatures. So all super operations
// will be allowed and we will not override them.
//
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
Set supers = new HashSet(50);
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
if (bd == null || bd.isMergeSuperMethods()) {
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List superAll = ((JavaClass) superTypes.get(i)).getEAllOperations();
int len = superAll.size();
for (int i1=0; i1<len; i1++) {
EOperation op = (EOperation) superAll.get(i1);
supers.add(formLongName(op));
}
}
}
}
Iterator itr = getJavaClass().getPublicMethods().iterator();
while (itr.hasNext()) {
Method mthd = (Method) itr.next();
if (mthd.isStatic() || mthd.isConstructor())
continue; // Statics/constructors don't participate as behaviors
String longName = formLongName(mthd);
// Add if super not already contain it.
if (!supers.contains(longName))
createOperation(null, longName, mthd, null); // Don't pass a name, try to create it by name, only use ID if there is more than one of the same name.
}
}
/*
* merge all the Behaviors(i.e. supertypes)
*/
protected BasicEList allOperations() {
BasicEList jcAllOperations = (BasicEList) getJavaClass().primGetEAllOperations();
BeaninfoSuperAdapter superAdapter =
(BeaninfoSuperAdapter) EcoreUtil.getRegisteredAdapter(getJavaClass(), BeaninfoSuperAdapter.class);
if (jcAllOperations != null) {
// See if new one required.
if (superAdapter == null || !superAdapter.isAllOperationsCollectionModified())
return jcAllOperations;
// Can't get superadapter, so must not be attached to a resource, so return current list. Or no change required.
}
UniqueEList.FastCompare allOperations = new UniqueEList.FastCompare() {
/**
* Comment for <code>serialVersionUID</code>
*
* @since 1.1.0
*/
private static final long serialVersionUID = 1L;
protected Object[] newData(int capacity) {
return new EOperation[capacity];
}
};
boolean doAllOperations = false;
synchronized(this) {
// If we are introspecting, don't do all operatoins because it is an invalid list. Just return empty without reseting the all modified flag.
doAllOperations = !isDoingAllOperations && !isIntrospecting && isResourceConnected();
if (doAllOperations)
isDoingAllOperations = true;
}
if (doAllOperations) {
try {
EList localOperations = getJavaClass().getEOperations();
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types and still keep order.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
// Now we need to merge in the supers.
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
// If there is only one supertype, we can add to the actual events, else we will use the linked hashset so that
// we don't add possible duplicates (e.g. IA extends IB,IC and IB extends IC. In this case there would be dups
// because IB would contribute IC's too).
Collection workingAllOperations = superTypes.size() == 1 ? (Collection) allOperations : new LinkedHashSet();
// We will now merge as directed.
boolean mergeAll = bd == null || bd.isMergeSuperMethods();
if (!mergeAll) {
// we don't to want to merge super operations, but we still need super non-operations.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getEAllOperations();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
EOperation o = (EOperation) supers.get(i1);
MethodDecorator md = Utilities.getMethodDecorator(o);
if (md == null || (md.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !md.isMergeIntrospection()))
workingAllOperations.add(o);
}
}
} else {
// We want to merge all.
// BeanInfo could of given us the don't merge list. If the list is empty, then we accept all.
if (bd != null && bd.eIsSet(BeaninfoPackage.eINSTANCE.getBeanDecorator_NotInheritedMethodNames())) {
// We were given a list of names.
// Get the names into a set to create a quick lookup.
HashSet superSet = new HashSet(bd.getNotInheritedMethodNames());
// Now walk and add in non-bean operations (and bean operations that were explicitly added and not mergeable (i.e. didn't come thru beaninfo))
// and add those not specifically called out by BeanInfo not merge list.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getEAllOperations();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
EOperation o = (EOperation) supers.get(i1);
MethodDecorator md = Utilities.getMethodDecorator(o);
if (md == null || (md.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !md.isMergeIntrospection()))
workingAllOperations.add(o);
else {
String longName = formLongName(o);
if (longName == null || !superSet.contains(longName))
workingAllOperations.add(o);
}
}
}
} else {
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
workingAllOperations.addAll(((JavaClass) superTypes.get(i)).getEAllOperations());
}
}
}
if (workingAllOperations != allOperations)
allOperations.addAll(workingAllOperations); // Now copy over the ordered super operations.
}
allOperations.addAll(localOperations);
ESuperAdapter sa = getJavaClass().getESuperAdapter();
sa.setAllOperationsCollectionModified(false); // Now built, so reset to not changed.
} finally {
synchronized(this) {
isDoingAllOperations = false;
}
}
}
allOperations.shrink();
return new EcoreEList.UnmodifiableEList.FastCompare(
getJavaClass(),
EcorePackage.eINSTANCE.getEClass_EAllOperations(),
allOperations.size(),
allOperations.data());
}
/*
* Should only be called from introspect so that flags are correct.
*/
private void finalizeEvents(ChangeDescription cd) {
// Now go through the list and remove those that should be removed.
Map oldLocals = getEventsMap();
Iterator itr = getEventsList().iterator();
while (itr.hasNext()) {
JavaEvent a = (JavaEvent) itr.next();
EventSetDecorator e = Utilities.getEventSetDecorator(a);
Object aOld = oldLocals.get(a.getName());
if (aOld != null && aOld != Boolean.FALSE) {
// A candidate for removal. It was in the old list and it was not processed.
if (e != null) {
ImplicitItem implicit = e.getImplicitDecoratorFlag();
if (implicit != ImplicitItem.NOT_IMPLICIT_LITERAL) {
e.setEModelElement(null); // Remove it because it was implicit.
((InternalEObject) e).eSetProxyURI(BAD_URI);
if (implicit == ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL) {
itr.remove(); // The feature was implicit too.
((InternalEObject) a).eSetProxyURI(BAD_URI);
}
continue; // At this point we don't have a decorator any more, so don't bother going on.
}
}
}
if (e != null && cd != null) {
// Now create the appropriate cache entry for this event.
BeanInfoDecoratorUtility.buildChange(cd, e);
}
}
}
/**
* Fill in the event and its decorator using the eventDesc.
*/
protected EventSetDecorator calculateEvent(EventSetRecord record) {
return createEvent(record.name);
}
/**
* Fill in the event and its decorator using the passed in information.
*/
protected EventSetDecorator createEvent(String name) {
// First find if there is already an event of this name, and if there is, is the EventSetDecorator
// marked to not allow merging in of introspection results.
HashMap existingLocals = getEventsMap();
JavaEvent event = null;
EventSetDecorator ed = null;
Object b = existingLocals.get(name);
if (Boolean.FALSE == b)
return null; // We've already processed this event, can't process it again.
if (b != null) {
// We've found one of the same event. Whether we modify it or use it as is, we put in a
// special dummy in its place. That marks that we've already processed it and accepted it.
existingLocals.put(name, Boolean.FALSE);
// If the decorator for this entry says not to merge then return.
// If there is no decorator, then we will merge. If they didn't want to
// merge, then they should of created a decorator with no merge on it.
ed = Utilities.getEventSetDecorator((JavaEvent) b);
if (ed != null && !ed.isMergeIntrospection())
return null;
event = (JavaEvent) b;
}
ImplicitItem implicit = ed == null ? ImplicitItem.IMPLICIT_DECORATOR_LITERAL : ImplicitItem.NOT_IMPLICIT_LITERAL;
if (event == null) {
// We will create a new Event.
event = BeaninfoFactory.eINSTANCE.createBeanEvent();
getEventsList().add(event);
implicit = ImplicitItem.IMPLICIT_DECORATOR_AND_FEATURE_LITERAL; // Can't have an implicit feature but explicit decorator.
}
setEventID(name, event);
event.setName(name);
// Now create in the event decorator for it.
// If there already is one then we
// will use it. This allows merging with beaninfo.
if (ed == null) {
ed = BeaninfoFactory.eINSTANCE.createEventSetDecorator();
ed.setImplicitDecoratorFlag(implicit);
event.getEAnnotations().add(ed);
} else
BeanInfoDecoratorUtility.clear(ed);
return ed;
}
/**
* @param name
* @param event
*
* @since 1.1.0
*/
private void setEventID(String name, JavaEvent event) {
// Now fill it in.
((XMIResource) event.eResource()).setID(event, getJavaClass().getName() + BeaninfoJavaReflectionKeyExtension.EVENT + name);
}
/**
* Reflect the events. This requires going through local public methods and creating an
* event for the discovered events.
*/
protected void reflectEvents() {
// If we are set to mergeSuperTypeEvents, then we need to get the super events.
// This is so that duplicate any from super that we find here. When reflecting we don't
// allow discovered duplicates.
//
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
Set supers = new HashSet(50);
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
if (bd == null || bd.isMergeSuperEvents()) {
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List superAll = ((JavaClass) superTypes.get(i)).getAllEvents();
int len = superAll.size();
for (int i1=0; i1<len; i1++) {
JavaEvent se = (JavaEvent) superAll.get(i1);
supers.add(se.getName());
}
}
}
}
HashMap events = new HashMap();
eventListenerClass = (JavaClass) JavaRefFactory.eINSTANCE.reflectType("java.util.EventListener", getJavaClass()); // Initialize, needed for building eventinfos. //$NON-NLS-1$
tooManyExceptionClass = (JavaClass) JavaRefFactory.eINSTANCE.reflectType("java.util.TooManyListenersException", getJavaClass()); // Initialize, needed for building eventinfos. //$NON-NLS-1$
Iterator itr = getJavaClass().getPublicMethods().iterator();
while (itr.hasNext()) {
Method mthd = (Method) itr.next();
if (mthd.isStatic() || mthd.isConstructor())
continue; // Statics/constructors don't participate in events.
String key = validEventAdder(mthd);
if (key != null) {
EventInfo eventInfo = (EventInfo) events.get(key);
if (eventInfo == null) {
eventInfo = new EventInfo();
eventInfo.setAdder(mthd);
events.put(key, eventInfo);
} else
eventInfo.setAdder(mthd);
} else {
key = validEventRemove(mthd);
if (key != null) {
EventInfo eventInfo = (EventInfo) events.get(key);
if (eventInfo == null) {
eventInfo = new EventInfo();
eventInfo.setRemover(mthd);
events.put(key, eventInfo);
} else
eventInfo.setRemover(mthd);
}
}
}
eventListenerClass = null; // No longer need it.
// Now actually create the events.
HashSet eventNames = new HashSet(events.size()); // Set of found event names, to prevent duplicates
Iterator evtItr = events.entrySet().iterator();
while (evtItr.hasNext()) {
Map.Entry eventMap = (Map.Entry) evtItr.next();
EventInfo ei = (EventInfo) eventMap.getValue();
if (ei.isValidInfo()) {
String eventName = getEventName((String) eventMap.getKey());
if (eventNames.contains(eventName))
continue; // Aleady created it. (Note: Introspector actually takes last one over previous dups, but the order is totally undefined, so choosing first is just as good or bad.
if (supers.contains(eventName))
continue; // Don't override a super event.
if (ei.createEvent(eventName))
eventNames.add(eventName); // It was validly created.
}
}
tooManyExceptionClass = null; // No longer need it.
}
/**
* merge all the Events (i.e. supertypes)
*/
protected EList allEvents() {
EList jcAllEvents = getJavaClass().getAllEventsGen();
BeaninfoSuperAdapter superAdapter =
(BeaninfoSuperAdapter) EcoreUtil.getRegisteredAdapter(getJavaClass(), BeaninfoSuperAdapter.class);
if (jcAllEvents != null) {
// See if new one required.
if (superAdapter == null || !superAdapter.isAllEventsCollectionModified())
return jcAllEvents;
// Can't get superadapter, so must not be attached to a resource, so return current list. Or no change required.
}
UniqueEList.FastCompare allEvents = new UniqueEList.FastCompare() {
/**
* Comment for <code>serialVersionUID</code>
*
* @since 1.1.0
*/
private static final long serialVersionUID = 1L;
protected Object[] newData(int capacity) {
return new JavaEvent[capacity];
}
};
boolean doAllEvents = false;
synchronized(this) {
// If we are introspecting, don't do all properties because it is an invalid list. Just return empty without reseting the all modified flag.
doAllEvents = !isDoingAllEvents && !isIntrospecting && isResourceConnected();
if (doAllEvents)
isDoingAllEvents = true;
}
if (doAllEvents) {
try {
EList localEvents = getJavaClass().getEvents();
// Kludge: BeanInfo spec doesn't address Interfaces, but some people want to use them.
// Interfaces can have multiple extends, while the Introspector ignores these for reflection,
// the truth is most people want these. So we will add them in. But since there could be more than one it
// gets confusing. We need to look for dups from the super types and still keep order.
//
// Supertypes will never be more than one entry for classes, it is possible to be 0, 1, 2 or more for interfaces.
List superTypes = getJavaClass().getESuperTypes();
if (!superTypes.isEmpty()) {
// Now we need to merge in the supers.
BeanDecorator bd = Utilities.getBeanDecorator(getJavaClass());
// If there is only one supertype, we can add to the actual events, else we will use the linked hashset so that
// we don't add possible duplicates (e.g. IA extends IB,IC and IB extends IC. In this case there would be dups
// because IB would contribute IC's too).
Collection workingAllEvents = superTypes.size() == 1 ? (Collection) allEvents : new LinkedHashSet();
// We will now merge as directed.
boolean mergeAll = bd == null || bd.isMergeSuperEvents();
if (!mergeAll) {
// we don't to want to merge super events, but we still need super non-events or explicit ones.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getAllEvents();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
JavaEvent e = (JavaEvent) supers.get(i1);
EventSetDecorator ed = Utilities.getEventSetDecorator(e);
if (ed == null || (ed.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !ed.isMergeIntrospection()))
workingAllEvents.add(e);
}
}
} else {
// We want to merge all.
// BeanInfo has given us the not merge list. If the list is empty, then we accept all.
if (bd != null && bd.eIsSet(BeaninfoPackage.eINSTANCE.getBeanDecorator_NotInheritedEventNames())) {
// We were given a list of names.
// Get the names into a set to create a quick lookup.
HashSet superSet = new HashSet(bd.getNotInheritedEventNames());
// Now walk and add in non-bean events (and bean events that were explicitly added and not mergeable (i.e. didn't come thru beaninfo))
// and add those not specifically called out by BeanInfo.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
List supers = ((JavaClass) superTypes.get(i)).getAllEvents();
int len = supers.size();
for (int i1 = 0; i1 < len; i1++) {
JavaEvent e = (JavaEvent) supers.get(i1);
EventSetDecorator ed = Utilities.getEventSetDecorator(e);
if (ed == null || (ed.getImplicitDecoratorFlag() == ImplicitItem.NOT_IMPLICIT_LITERAL && !ed.isMergeIntrospection()) || !superSet.contains(ed.getName()))
workingAllEvents.add(e);
}
}
} else {
// BeanInfo called out that all super events should be added, or no beaninfo.
int lenST = superTypes.size();
for (int i=0; i<lenST; i++) {
workingAllEvents.addAll(((JavaClass) superTypes.get(i)).getAllEvents());
}
}
}
if (workingAllEvents != allEvents)
allEvents.addAll(workingAllEvents); // Now copy over the ordered super events.
}
allEvents.addAll(localEvents);
superAdapter.setAllEventsCollectionModified(false); // Now built, so reset to not changed.
} finally {
synchronized (this) {
isDoingAllEvents = false;
}
}
}
if (allEvents.isEmpty())
return ECollections.EMPTY_ELIST;
else {
allEvents.shrink();
return new EcoreEList.UnmodifiableEList.FastCompare(
getJavaClass(),
JavaRefPackage.eINSTANCE.getJavaClass_AllEvents(),
allEvents.size(),
allEvents.data());
}
}
private JavaClass eventListenerClass, // Event Listener class. Needed for validation.
tooManyExceptionClass; // Too many listeners exception.
/*
* Pass in the key, it will be used to form the name.
*/
protected String getEventName(String key) {
return key.substring(0, key.indexOf(':'));
}
/*
* Answers event key if valid, null if not valid.
*/
protected String validEventAdder(Method method) {
String name = method.getName();
if (!name.startsWith("add") || !name.endsWith("Listener")) //$NON-NLS-1$ //$NON-NLS-2$
return null; // Not valid format for an add listener name.
// Note. The Bean specs only state that it should be "add<ListenerType>" but the
// introspector implicitly adds an additional constraint in that the listener type
// should end with "Listener". If it doesn't the introspector would actually throw
// an index out of bounds. We'll just ignore such methods.
List parms = method.getParameters();
if (parms.size() != 1)
return null; // Invalid - improper number of parms.
EClassifier returnType = method.getReturnType();
if (returnType == null || !returnType.getName().equals("void")) //$NON-NLS-1$
return null; // Invalid - must be void return type.
EClassifier parmType = ((JavaParameter) parms.get(0)).getEType();
if (!BeaninfoClassAdapter.this.eventListenerClass.isAssignableFrom(parmType))
return null; // Parm must be inherit from EventListener
// Beans introspector also requires that the "listener" part of the name be a
// suffix of the type name. So if the type name was "ISomeListener" then
// the method name must be "addSomeListener" (actually "addomeListener"
// would also be valid according to the introspector!).
// This allows the type to be an interface using the "I" convention while not
// requiring the "I" to be in the name of the event.
if (!parmType.getName().endsWith(name.substring(3)))
return null; // It is not "add{ListenerType}".
// This is a unique containing event name and listener type
// This is so we can have a unique key for two events with the same
// name but different listener type. (This matches Introspector so that we aren't
// coming up with different results.
return java.beans.Introspector.decapitalize(name.substring(3, name.length() - 8))
+ ':'
+ ((JavaHelpers) parmType).getQualifiedName();
}
/*
* Answers event key if valid, null if not valid.
*/
protected String validEventRemove(Method method) {
String name = method.getName();
if (!name.startsWith("remove") || !name.endsWith("Listener")) //$NON-NLS-1$ //$NON-NLS-2$
return null; // Not valid format for a remove listener name.
// Note. The Bean specs only state that it should be "add<ListenerType>" but the
// introspector implicitly adds an additional constraint in that the listener type name
// should end with "Listener". If it doesn't the introspector would actually throw
// an index out of bounds. We'll just ignore such methods.
List parms = method.getParameters();
if (parms.size() != 1)
return null; // Invalid - improper number of parms.
EClassifier returnType = method.getReturnType();
if (returnType == null || !returnType.getName().equals("void")) //$NON-NLS-1$
return null; // Invalid - must be void return type.
EClassifier parmType = ((JavaParameter) parms.get(0)).getEType();
if (!BeaninfoClassAdapter.this.eventListenerClass.isAssignableFrom(parmType))
return null; // Parm must be inherit from EventListener
// Beans introspector also requires that the "listener" part of the name be a
// suffix of the type name. So if the type name was "ISomeListener" then
// the method name must be "removeSomeListener" (actually "removeomeListener"
// would also be valid according to the introspector!).
// This allows the type to be an interface using the "I" convention while not
// requiring the "I" to be in the name of the event.
if (!parmType.getName().endsWith(name.substring(6)))
return null; // It is not "add{ListenerType}".
// This is a unique containing event name and listener type
// This is so we can have a unique key for two events with the same
// name but different listener type. (This matches Introspector so that we aren't
// coming up with different results).
return java.beans.Introspector.decapitalize(name.substring(6, name.length() - 8))
+ ':'
+ ((JavaHelpers) parmType).getQualifiedName();
}
public boolean isDefaultBound() {
if (defaultBound == null) {
// Haven't yet decided on it.
Iterator methods = getJavaClass().getPublicMethods().iterator();
boolean foundAdd = false, foundRemove = false;
while (methods.hasNext() && (!foundAdd || !foundRemove)) {
Method method = (Method) methods.next();
if ("addPropertyChangeListener".equals(method.getName())) { //$NON-NLS-1$
List parms = method.getParameters();
if (parms.size() == 1) {
JavaParameter parm = (JavaParameter) parms.get(0);
if ("java.beans.PropertyChangeListener".equals(((JavaHelpers) parm.getEType()).getQualifiedName())) { //$NON-NLS-1$
foundAdd = true;
continue;
}
}
} else if ("removePropertyChangeListener".equals(method.getName())) { //$NON-NLS-1$
List parms = method.getParameters();
if (parms.size() == 1) {
JavaParameter parm = (JavaParameter) parms.get(0);
if ("java.beans.PropertyChangeListener".equals(((JavaHelpers) parm.getEType()).getQualifiedName())) { //$NON-NLS-1$
foundRemove = true;
continue;
}
}
}
}
defaultBound = (foundAdd && foundRemove) ? Boolean.TRUE : Boolean.FALSE;
}
return defaultBound.booleanValue();
}
private class EventInfo {
public Method addListenerMethod;
public Method removeListenerMethod;
public void setAdder(Method addMethod) {
addListenerMethod = addMethod;
}
public void setRemover(Method removeMethod) {
removeListenerMethod = removeMethod;
}
// Answer whether this is a valid event info.
public boolean isValidInfo() {
return (addListenerMethod != null && removeListenerMethod != null);
}
public boolean createEvent(String name) {
EventSetDecorator ed = BeaninfoClassAdapter.this.createEvent(name);
if (ed == null)
return false; // Reflection not wanted.
// See if unicast.
boolean unicast = false;
List exceptions = addListenerMethod.getJavaExceptions();
int len = exceptions.size();
for(int i=0; i<len; i++) {
if (exceptions.get(i) == BeaninfoClassAdapter.this.tooManyExceptionClass) {
unicast = true;
break;
}
}
// We'll let listener methods get retrieved dynamically when needed.
BeanInfoDecoratorUtility.setProperties(ed, addListenerMethod, removeListenerMethod, unicast, (JavaClass) ((JavaParameter) addListenerMethod.getParameters().get(0)).getEType());
return true;
}
}
/**
* Marh the factory as stale, but leave the overrides alone. They are not stale.
* @param stale
*
* @since 1.1.0.1
*/
public void markStaleFactory(ProxyFactoryRegistry stale) {
markStaleFactory(stale, false);
}
/**
* Mark this factory as the stale factory.
*
* @param clearOverriddes clear the overrides too. They are stale.
*/
public void markStaleFactory(ProxyFactoryRegistry stale, boolean clearOverriddes) {
if (staleFactory == null) {
// It's not stale so make it stale.
// So that next access will re-introspect
defaultBound = null; // So query on next request.
staleFactory = new WeakReference(stale);
// Need to mark the esuperadapter that things have changed so that any
// subtype will know to reuse the parent for anything that requires knowing parent info.
Adapter a = EcoreUtil.getExistingAdapter(getTarget(), ESuperAdapter.class);
// Simulate that this objects super has changed. This will make all subclasses
// think about the super has changed and next retrieving anything that involves the
// super will cause a rebuild to occur.
Notification note =
new ENotificationImpl((InternalEObject) getTarget(), Notification.SET, EcorePackage.ECLASS__ESUPER_TYPES, null, null);
if (a != null)
a.notifyChanged(note);
// Do the same with BeaninfoSuperAdapter so that events also will be rebuilt.
a = EcoreUtil.getExistingAdapter(getTarget(), BeaninfoSuperAdapter.ADAPTER_KEY);
if (a != null)
a.notifyChanged(note);
synchronized (this) {
needsIntrospection = true;
if (clearOverriddes) {
retrievedExtensionDocument = CLEAR_EXTENSIONS;
if (classEntry != null) {
classEntry.markDeleted();
classEntry = null; // Get a new one next time.
}
}
}
}
}
/**
* Form a longname for the addkey function.
*/
private String formLongName(EOperation feature) {
Method mthd = null;
if (feature instanceof Method)
mthd = (Method) feature;
else if (feature instanceof MethodProxy)
mthd = ((MethodProxy) feature).getMethod();
else
return null; // Don't know what it is.
StringBuffer longName = new StringBuffer(100);
longName.append(feature.getName()); // Feature Name
longName.append(':');
longName.append(mthd.getName()); // Method Name
longName.append('(');
List p = mthd.getParameters();
for (int i = 0; i < p.size(); i++) {
JavaParameter parm = (JavaParameter) p.get(i);
if (i>0)
longName.append(',');
longName.append(parm.getJavaType().getQualifiedName());
}
return longName.toString();
}
/*
* More than one operation can have the same name. To identify them uniquely, the long name is created,
* which is formed from the name and the method signature.
*/
private String formLongName(MethodRecord record) {
StringBuffer longName = new StringBuffer(100);
longName.append(record.name); // Feature Name
longName.append(':');
longName.append(record.methodForDescriptor.methodName); // Method Name
longName.append('(');
String[] p = record.methodForDescriptor.parameterTypeNames;
if (p != null)
for (int i = 0; i < p.length; i++) {
if (i>0)
longName.append(',');
longName.append(MapJNITypes.getFormalTypeName(p[i]));
}
return longName.toString();
}
/**
* @see org.eclipse.emf.common.notify.Adapter#notifyChanged(Notification)
*/
public void notifyChanged(Notification msg) {
// In case of removing adapter, make sure we are first removed from the factory so it doesn't know about us anymore.
if (msg.getEventType() == Notification.REMOVING_ADAPTER)
getAdapterFactory().removeAdapter(this);
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return super.toString() + '(' + (getJavaClass() != null ? getJavaClass().getQualifiedName() : "?") + ')'; //$NON-NLS-1$
}
}