/******************************************************************************* * 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.java.adapters; /* */ import java.util.List; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.*; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.jem.java.*; import org.eclipse.jem.util.logger.proxy.Logger; /** * */ public abstract class JavaReflectionAdaptor extends ReflectionAdaptor { private static final String C_METHOD_DEFAULT_CTOR = String.valueOf(C_METHOD_PARM_DELIMITER) + S_CONSTRUCTOR_TOKEN; protected static final String LEFT_BRACKET = "[";//$NON-NLS-1$ /** * Special notification event type. This is sent against a JavaClass (as the target) whenever flush of the reflection occurs. It will be * sent under the notification event type of REFLECTION_EVENT. * @since 1.1.0 */ public static final EAttribute FLUSH_REFLECTION_EVENT = EcorePackage.eINSTANCE.getEcoreFactory().createEAttribute(); /** * Special notification event type. This is sent against a JavaClass (as the target) whenever flush of a new class (i.e. * no source was found) of the reflection occurs. It will be * sent under the notification event type of REFLECTION_EVENT. * @since 1.1.0 */ public static final EAttribute FLUSH_NEW_REFLECTION_EVENT = EcorePackage.eINSTANCE.getEcoreFactory().createEAttribute(); /* * Fill in the name. Not really needed but it would be nice. */ static { FLUSH_REFLECTION_EVENT.setName("flushReflectedValues"); //$NON-NLS-1$ FLUSH_NEW_REFLECTION_EVENT.setName("flushNewReflectedValues"); //$NON-NLS-1$ } protected boolean hasFlushed = false; protected boolean isFlushing = false; /** * JavaReflectionAdapter constructor comment. */ public JavaReflectionAdaptor() { super(); } /** * JavaReflectionAdapter constructor comment. * @param target org.eclipse.emf.common.notify.Notifier */ public JavaReflectionAdaptor(org.eclipse.emf.common.notify.Notifier target) { super(target); } /** * createBlock - instantiate a Block containing the passed source */ public Block createBlock(String name, String sourceString) { Block newBlock = getJavaFactory().createBlock(); newBlock.setName(name + "_" + "block");//$NON-NLS-2$//$NON-NLS-1$ newBlock.setSource(sourceString); return newBlock; } /** * setSuper - set our supertype here, implemented interface are handled separately */ public JavaClass createJavaClassRef(String targetName) { return JavaRefFactory.eINSTANCE.createClassRef(targetName); } /** * createJavaParameter - instantiate a Java Parameter based on the passed name and type name (a simple name, NOT A SIGNATURE!!!) * The id for a parameter has to be complex in order to be parsable into class, method, and parm. * It is created by appending the parm name to the method id, with a known separator. * It will look something like "Foo.doSomething(java.lang.Integer-arg0" */ public JavaParameter createJavaParameter(Method parentMethod, String parmName, String parmTypeName) { JavaParameter newParm = getJavaFactory().createJavaParameter(); if (parmName!=null) newParm.setName(parmName); // ((InternalEObject)newParm).eSetID(parentMethod.eID() + C_METHODID_PARMID_DELIMITER + parmName); String classRefString = parmTypeName; newParm.setEType(createJavaClassRef(classRefString)); return newParm; } /** * This method will return a List of dimensions for a typeName. * For example "foo[][]" would return a List of Integers * 1, 1. At some point we may want to actually capture the size * for Fields but we would need the initializer source to determine that. */ public List getArrayDimensions(String typeName) { List dimensions = new java.util.ArrayList(); if (typeName != null) { int begin = 0; int index = -1; while (begin < typeName.length()) { index = typeName.indexOf(LEFT_BRACKET, begin); if (index > -1) { dimensions.add(new Integer(1)); begin = index + 1; } else { begin = typeName.length(); } } } return dimensions; } /* Get the Java Factory */ protected static JavaRefFactory getJavaFactory() { return ((org.eclipse.jem.java.JavaRefPackage)EPackage.Registry.INSTANCE.getEPackage(org.eclipse.jem.java.JavaRefPackage.eNS_URI)).getJavaRefFactory(); } public abstract Object getReflectionSource(); /** * getTypeNamesFromMethodUUID - Pull the parm type names out of a method ID * It will be in the form: "simpleclass.methodName(com.fronk.Parm1_type,parm2type" */ protected static String[] getTypeNamesFromMethodID(String methodID) { if (methodID.charAt(methodID.length()-1) == C_METHOD_PARM_DELIMITER || methodID.endsWith(C_METHOD_DEFAULT_CTOR)) return emptyStringArray; // Count the parms first. The number of parms is the number of occurrences of ',' + 1 int numParms = 1; int pos = -1; // Skip the '.' after classname pos = methodID.indexOf(C_CLASS_MEMBER_DELIMITER, ++pos); // Look for the start of the parms int parmPos = methodID.indexOf(C_METHOD_PARM_DELIMITER, ++pos); pos = parmPos; while ((pos = methodID.indexOf(C_PARM_PARM_DELIMITER, ++pos)) != -1) numParms++; String[] parmTypeNames = new String[numParms]; // now collect the parm names // skip the method name pos = parmPos; int i = 0, end; do { end = methodID.indexOf(C_PARM_PARM_DELIMITER, pos + 1); // This is the last parameter, we may need to strip a trailing &V for a constructor if (end == -1) end = methodID.indexOf(S_CONSTRUCTOR_TOKEN, pos + 1); // otherwise take the rest of the ID if (end == -1) end = methodID.length(); parmTypeNames[i++] = methodID.substring(pos + 1, end); } while ((pos = methodID.indexOf(C_PARM_PARM_DELIMITER, ++pos)) != -1); return parmTypeNames; } public abstract boolean hasCachedReflectionSource(); public boolean hasReflectionSource() { return getReflectionSource() != null; } /** * Subclasses should override. * */ public void releaseSourceType(){ } /** * Subclasses should override. * @return */ public Notification releaseSourceTypeNoNotification() { return null; } public static void releaseSourceType(JavaClass javaClass) { if (javaClass == null) return; JavaReflectionAdaptor existing = (JavaReflectionAdaptor) retrieveAdaptorFrom(javaClass); if (existing != null) existing.releaseSourceType(); } /* * This method is called by a Field Adaptor to set the type of aField * to be aTypeName. aTypeName may contain array brackets which need * to be detected in order to set the array dimensions on aField. */ protected void setFieldType(Field aField, String aTypeName) { if (aField != null && aTypeName != null) { String classRefString = aTypeName; aField.setEType(createJavaClassRef(classRefString)); } } public final boolean flushReflectedValuesIfNecessary() { return flushReflectedValuesIfNecessary(false); } public final boolean flushReflectedValuesIfNecessary(boolean clearCachedModelObject) { Notification not = flushReflectedValuesIfNecessaryNoNotification(clearCachedModelObject); if (not != null) getTarget().eNotify(not); return hasFlushed; } public synchronized Notification flushReflectedValuesIfNecessaryNoNotification(boolean clearCachedModelObject) { if (!hasFlushed && !isFlushing) { boolean isExisting = hasCachedReflectionSource(); try { isFlushing = true; hasReflected = false; hasFlushed = flushReflectedValues(clearCachedModelObject); } catch (Throwable e) { hasFlushed = false; Logger.getLogger().log(e); if (e instanceof RuntimeException) throw (RuntimeException) e; else if (e instanceof Error) throw (Error) e; else throw new RuntimeException(e.getMessage()); } finally { isFlushing = false; postFlushReflectedValuesIfNecessary(isExisting); } return createFlushNotification(isExisting); } return null; } /** * @param isExisting * @return */ protected Notification createFlushNotification(boolean isExisting) { EStructuralFeature feature = isExisting ? FLUSH_REFLECTION_EVENT : FLUSH_NEW_REFLECTION_EVENT; return new ENotificationImpl((InternalEObject)getTarget(),EVENT, feature, null, null); } protected void postFlushReflectedValuesIfNecessary(boolean isExisting) { } /** * Subclasses should override to perform the actual clearing of the values. */ protected boolean flushReflectedValues(boolean clearCachedModelObject) { return true; } /** * Return a boolean indicating whether reflection had occurred. */ public boolean reflectValuesIfNecessary() { if (isFlushing) return false; return super.reflectValuesIfNecessary(); } /** * reflectValues - template method, subclasses override to pump values into target */ public boolean reflectValues() { hasFlushed = false; return true; } }