/******************************************************************************* * 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.beaninfo.vm; /* */ import java.awt.Image; import java.beans.*; import java.lang.reflect.*; import org.eclipse.jem.beaninfo.common.IBaseBeanInfoConstants; /** * A BaseBeanInfo that provides common support for BeanInfos within the JEM environment. * * @since 1.1.0 */ public abstract class BaseBeanInfo extends SimpleBeanInfo implements IBaseBeanInfoConstants { // Constants to use to create all descriptors etc. protected static java.util.ResourceBundle RESBUNDLE = java.util.ResourceBundle.getBundle("org.eclipse.jem.beaninfo.vm.beaninfo"); //$NON-NLS-1$ /** * Bound indicator for apply property arguments. * * @since 1.1.0 */ public static final String BOUND = "bound";//$NON-NLS-1$ /** * Constrained indicator for apply property arguments. * * @since 1.1.0 */ public static final String CONSTRAINED = "constrained";//$NON-NLS-1$ /** * Property editor class indicator for apply property arguments. * * @since 1.1.0 */ public static final String PROPERTYEDITORCLASS = "propertyEditorClass";//$NON-NLS-1$ /** * Read Method indicator for apply property arguments. * * @since 1.1.0 */ public static final String READMETHOD = "readMethod";//$NON-NLS-1$ /** * Write method indicator for apply property arguments. * * @since 1.1.0 */ public static final String WRITEMETHOD = "writeMethod";//$NON-NLS-1$ /** * Displayname indicator for apply property arguments. * * @since 1.1.0 */ public static final String DISPLAYNAME = "displayName";//$NON-NLS-1$ /** * Expert indicator for apply property arguments. * * @since 1.1.0 */ public static final String EXPERT = "expert";//$NON-NLS-1$ /** * Hidden indicator for apply property arguments. * * @since 1.1.0 */ public static final String HIDDEN = "hidden";//$NON-NLS-1$ /** * Preferred indicator for apply property arguments. * * @since 1.1.0 */ public static final String PREFERRED = "preferred";//$NON-NLS-1$ /** * Short description indicator for apply property arguments. * * @since 1.1.0 */ public static final String SHORTDESCRIPTION = "shortDescription";//$NON-NLS-1$ /** * Customizer class indicator for apply property arguments. * * @since 1.1.0 */ public static final String CUSTOMIZERCLASS = "customizerClass";//$NON-NLS-1$ /** * In Default eventset indicator for apply property arguments. * * @since 1.1.0 */ public static final String INDEFAULTEVENTSET = "inDefaultEventSet";//$NON-NLS-1$ /** * This is a Feature Attribute Key. When this key exists, the value is a java.lang.reflect.Field. It means this property * is a field and not a getter/setter. The getter/setter will be ignored and the property type will be the type of the field. * <p> * At this time, do not use field on an indexed property. This is currently an undefined situation. * * @since 1.1.0 */ public static final String FIELDPROPERTY = "field"; //$NON-NLS-1$ /** * Obscure indicator for apply property arguments. Obsure is a pre-defined attribute name too. That is where the obscure setting is stored. * <p> * Obsure means most users don't need it. In the future such features won't even be cached so as to reduce the in-memory costs. Currently this * flag is ignored. * * @since 1.1.0 */ public static final String OBSCURE = "ivjObscure";//$NON-NLS-1$ /** * Design time indicator for apply property arguments. Design time is a pre-defined attribute name too. That is where the design time setting is * stored. * <p> * Design time means: * <ul> * <li>Not set: Will be a property that can be connected to, and shows on property sheet (if not hidden). * <li><code>true</code>: Special property (it will show on property sheet if not hidden), but it can't be connected to. Usually this is a * property that is fluffed up for the IDE purposes but doesn't have a get/set method. This means it is a property for design time and not for * runtime. * <li><code>false</code>: This property will not show up on property sheet but it can be connected to. * </ul> * * @since 1.1.0 */ public static final String DESIGNTIMEPROPERTY = "ivjDesignTimeProperty"; //$NON-NLS-1$ /** * EventAdapterClass indicator for apply property arguments. Event adapter class is a pre-defined attribute name too. That is where the event * adapter is stored. * <p> * Adapter class for eventSetDescriptors that provide default no-op implementation of the interface methods. For example * <code>java.awt.event.WindowListener</code> has an adapter of <code>java.awt.event.WindowAdapter</code>. What is stored is actually the * class name, not the class itself. * * @since 1.1.0 */ public static final String EVENTADAPTERCLASS = "eventAdapterClass"; //$NON-NLS-1$ public static final boolean JVM_1_3 = System.getProperty("java.version", "").startsWith("1.3"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ /** * Empty args list for those descriptors that don't have arguments. * @since 1.1.0 */ public static final Object[] EMPTY_ARGS = new Object[0]; /** * Capitalize the string. This uppercases only the first character. So if you have property name of "abc" it will become "Abc". * * @param s * @return string with first letter capitalized. * * @since 1.1.0 */ public static String capitalize(String s) { if (s.length() == 0) { return s; } char chars[] = s.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); return new String(chars); } /** * Create a BeanDescriptor object given an array of keyword/value arguments. Use the keywords defined in this class, e.g. BOUND, EXPERT, etc. * * @param cls * bean for which the bean descriptor is being created. * @param args * arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if no args * @return new bean descriptor * * @since 1.1.0 */ public static BeanDescriptor createBeanDescriptor(Class cls, Object[] args) { Class customizerClass = null; if (args != null) { /* Find the specified customizerClass */ for (int i = 0; i < args.length; i += 2) { if (CUSTOMIZERCLASS.equals(args[i])) { customizerClass = (Class) args[i + 1]; break; } } } BeanDescriptor bd = new BeanDescriptor(cls, customizerClass); if (args != null) { for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; setFeatureDescriptorValue(bd, key, value); } } return bd; } /** * Create a beans EventSetDescriptor given the following: * * @param cls * The bean class * @param name * Name of event set * @param args * arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if no args. * @param lmds * array of MethodDescriptors defining the listener methods * @param listenerType * type of listener * @param addListenerName * add listener method name * @param removeListenerNameremove * listener method name * @return new event set descriptor * @since 1.1.0 */ public static EventSetDescriptor createEventSetDescriptor(Class cls, String name, Object[] args, MethodDescriptor[] lmds, Class listenerType, String addListenerName, String removeListenerName) { EventSetDescriptor esd = null; Class[] paramTypes = { listenerType}; try { java.lang.reflect.Method addMethod = null; java.lang.reflect.Method removeMethod = null; try { /* get addListenerMethod with parameter types. */ addMethod = cls.getMethod(addListenerName, paramTypes); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_get_the_meth1_EXC_"), //$NON-NLS-1$ new Object[] { addListenerName})); } ; try { /* get removeListenerMethod with parameter types. */ removeMethod = cls.getMethod(removeListenerName, paramTypes); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_get_the_meth1_EXC_"), //$NON-NLS-1$ new Object[] { removeListenerName})); } ; esd = new EventSetDescriptor(name, listenerType, lmds, addMethod, removeMethod); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_the_E1_EXC_"), //$NON-NLS-1$ new Object[] { name})); } ; if (args != null) { // set the event set descriptor properties for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; if (INDEFAULTEVENTSET.equals(key)) { esd.setInDefaultEventSet(((Boolean) value).booleanValue()); } else setFeatureDescriptorValue(esd, key, value); } } return esd; } /** * Create a bean's MethodDescriptor. * * @param cls * class of the method. * @param name * name of the method. * @param args * arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if no args * @param params * parameter descriptors or <code>null</code> if no parameter descriptors. * @param paramTypes * parameter types * @return new method descriptor * * @since 1.1.0 */ public static MethodDescriptor createMethodDescriptor(Class cls, String name, Object[] args, ParameterDescriptor[] params, Class[] paramTypes) { MethodDescriptor md = null; try { java.lang.reflect.Method aMethod = null; try { /* getMethod with parameter types. */ aMethod = cls.getMethod(name, paramTypes); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_get_the_meth1_EXC_"), //$NON-NLS-1$ new Object[] { name})); } ; if (params != null && params.length > 0) md = new MethodDescriptor(aMethod, params); else md = new MethodDescriptor(aMethod); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_Method_EXC_"), //$NON-NLS-1$ new Object[] { name})); } ; if (args != null) { // set the method properties for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; setFeatureDescriptorValue(md, key, value); } } return md; } private static PropertyDescriptor createOtherPropertyDescriptor(String name, Class cls) throws IntrospectionException { Method readMethod = null; Method writeMethod = null; String base = capitalize(name); Class[] parameters = new Class[0]; // First we try boolean accessor pattern try { readMethod = cls.getMethod("is" + base, parameters);//$NON-NLS-1$ } catch (Exception ex1) { } if (readMethod == null) { try { // Else we try the get accessor pattern. readMethod = cls.getMethod("get" + base, parameters);//$NON-NLS-1$ } catch (Exception ex2) { // Find by matching methods of the class readMethod = findMethod(cls, "get" + base, 0);//$NON-NLS-1$ } } if (readMethod == null) { // For write-only properties, find the write method writeMethod = findMethod(cls, "set" + base, 1);//$NON-NLS-1$ } else { // In Sun's code, reflection fails if there are two // setters with the same name and the first setter located // does not have the same return type of the getter. // This fixes that. parameters = new Class[1]; parameters[0] = readMethod.getReturnType(); try { writeMethod = cls.getMethod("set" + base, parameters);//$NON-NLS-1$ } catch (Exception ex3) { } } // create the property descriptor if ((readMethod != null) || (writeMethod != null)) { return new PropertyDescriptor(name, readMethod, writeMethod); } else { throw new IntrospectionException(java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_find_the_acc1_EXC_"), //$NON-NLS-1$ new Object[] { name})); } } /** * Create a beans parameter descriptor. * * @param name * name of parameter * @param args * arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if no args * @return new parameter descriptor * * @since 1.1.0 */ public static ParameterDescriptor createParameterDescriptor(String name, Object[] args) { ParameterDescriptor pd = null; try { pd = new ParameterDescriptor(); } catch (Exception ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_Param1_EXC_"), //$NON-NLS-1$ new Object[] { name})); } ; // set the name pd.setName(name); if (args != null) { // set the method properties for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; setFeatureDescriptorValue(pd, key, value); } } return pd; } private static Method GETCLASS; static { try { GETCLASS = Object.class.getMethod("getClass", (Class[]) null); //$NON-NLS-1$ } catch (SecurityException e) { } catch (NoSuchMethodException e) { } } /** * Create a property descriptor describing a field property. * <p> * Note: This is non-standard. The VE knows how to handle this, but any one else using BeanInfo will see this as a property with * no getter or setter. * @param name * @param field * @param args arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if no args * @return * * @since 1.1.0 */ public static PropertyDescriptor createFieldPropertyDescriptor(String name, Field field, Object[] args) { try { PropertyDescriptor pd = new PropertyDescriptor(name, null, null); pd.setValue(FIELDPROPERTY, field); // Set the field property so we know it is a field. applyFieldArguments(pd, args); // Need to set in a phony read method because Introspector will throw it away otherwise. We just use Object.getClass for this. // We will ignore the property type for fields. If used outside of VE then it will look like a class property. pd.setReadMethod(GETCLASS); return pd; } catch (IntrospectionException e) { throwError(e, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_the_P1_EXC_"), //$NON-NLS-1$ new Object[] { name})); return null; } } /** * Create a bean's property descriptor. * * @param cls * class of who owns the property (usually the bean). It is used to look up get/set methods for the property. * @param name * name of the property. It will use get{Name} and set{Name} to find get/set methods. * @param args * arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. * @return new property descriptor * * @since 1.1.0 */ public static PropertyDescriptor createPropertyDescriptor(Class cls, String name, Object[] args) { PropertyDescriptor pd = null; try { // Create assuming that the getter/setter follows reflection patterns pd = new PropertyDescriptor(name, cls); } catch (IntrospectionException e) { // Try creating a property descriptor for read-only, write-only // or if Sun's reflection fails try { pd = createOtherPropertyDescriptor(name, cls); } catch (IntrospectionException ie) { throwError(ie, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_the_P1_EXC_"), //$NON-NLS-1$ new Object[] { name})); } } applyPropertyArguments(pd, args, cls); return pd; } /** * Create a new PropertyDescriptor based upon the PD sent in. It will clone the sent in one, and apply the args to override any specific setting. * Class cls is used for finding read/write methods, if any. * * This is used when wanting to override only a few specific settings from a property descriptor from the super class. * * @param fromPDS * The PropertyDescriptor array to find the entry to clone. It will be changed in place in the array. * @param name * The name of the property to find and clone and override. * @param cls * The class to use to find read/write methods in args. If no read/write methods specified, then this may be null. * @param args * The arguments to override from fromPD. arg pairs, [0] keyword, [1] value, [2] keyword, [3] value, etc. or null if none to override */ public void replacePropertyDescriptor(PropertyDescriptor[] pds, String name, Class cls, Object[] args) { PropertyDescriptor pd = null; int iPD = findPropertyDescriptor(pds, name); if (iPD == -1) return; PropertyDescriptor fromPD = pds[iPD]; try { pd = pds[iPD] = new PropertyDescriptor(fromPD.getName(), null, null); } catch (IntrospectionException e) { throwError(e, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_the_P1_EXC_"), //$NON-NLS-1$ new Object[] { fromPD.getName()})); } // Now copy over the contents of fromPD. clonePropertySettings(fromPD, pd); // Now apply the overrides applyPropertyArguments(pd, args, cls); return; } private void clonePropertySettings(PropertyDescriptor fromPD, PropertyDescriptor pd) { try { pd.setReadMethod(fromPD.getReadMethod()); pd.setWriteMethod(fromPD.getWriteMethod()); pd.setPropertyEditorClass(fromPD.getPropertyEditorClass()); pd.setBound(fromPD.isBound()); pd.setConstrained(fromPD.isConstrained()); cloneFeatureSettings(fromPD, pd); } catch (IntrospectionException e) { throwError(e, java.text.MessageFormat.format(RESBUNDLE.getString("Cannot_create_the_P1_EXC_"), //$NON-NLS-1$ new Object[] { fromPD.getName()})); } } private void cloneFeatureSettings(FeatureDescriptor fromFD, FeatureDescriptor fd) { fd.setExpert(fromFD.isExpert()); fd.setHidden(fromFD.isHidden()); fd.setPreferred(fromFD.isPreferred()); fd.setShortDescription(fromFD.getShortDescription()); fd.setDisplayName(fromFD.getDisplayName()); java.util.Enumeration keys = fromFD.attributeNames(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); Object value = fromFD.getValue(key); fd.setValue(key, value); } } /* * The common property arguments between field and standard properties. */ private static boolean applyCommonPropertyArguments(PropertyDescriptor pd, String key, Object value) { if (BOUND.equals(key)) { pd.setBound(((Boolean) value).booleanValue()); } else if (CONSTRAINED.equals(key)) { pd.setConstrained(((Boolean) value).booleanValue()); } else if (PROPERTYEDITORCLASS.equals(key)) { pd.setPropertyEditorClass((Class) value); } else if (FIELDPROPERTY.equals(key)) return true; // This should not be applied except through createFieldProperty. else return false; return true; } private static void applyPropertyArguments(PropertyDescriptor pd, Object[] args, Class cls) { if (args != null) { for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; if (!applyCommonPropertyArguments(pd, key, value)) { if (READMETHOD.equals(key)) { String methodName = (String) value; Method method; try { method = cls.getMethod(methodName, new Class[0]); pd.setReadMethod(method); } catch (Exception e) { throwError(e, java.text.MessageFormat.format(RESBUNDLE.getString("{0}_no_read_method_EXC_"), //$NON-NLS-1$ new Object[] { cls, methodName})); } } else if (WRITEMETHOD.equals(key)) { String methodName = (String) value; try { if (methodName == null) { pd.setWriteMethod(null); } else { Method method; Class type = pd.getPropertyType(); method = cls.getMethod(methodName, new Class[] { type}); pd.setWriteMethod(method); } } catch (Exception e) { throwError(e, java.text.MessageFormat.format(RESBUNDLE.getString("{0}_no_write_method_EXC_"), //$NON-NLS-1$ new Object[] { cls, methodName})); } } else { // arbitrary value setFeatureDescriptorValue(pd, key, value); } } } } } private static void applyFieldArguments(PropertyDescriptor pd, Object[] args) { if (args != null) { for (int i = 0; i < args.length; i += 2) { String key = (String) args[i]; Object value = args[i + 1]; if (!applyCommonPropertyArguments(pd, key, value)) { if (READMETHOD.equals(key)) { // ignored for field. } else if (WRITEMETHOD.equals(key)) { // ignored for field. } else { // arbitrary value setFeatureDescriptorValue(pd, key, value); } } } } } /** * Find the method by comparing (name & parameter size) against the methods in the class. This is an expensive call and should be used only if * getMethod with specific parameter types can't find method. * * @return java.lang.reflect.Method * @param aClass * java.lang.Class * @param methodName * java.lang.String * @param parameterCount * int */ public static java.lang.reflect.Method findMethod(java.lang.Class aClass, java.lang.String methodName, int parameterCount) { try { /* * Since this method attempts to find a method by getting all methods from the class, this method should only be called if getMethod * cannot find the method. */ java.lang.reflect.Method methods[] = aClass.getMethods(); for (int index = 0; index < methods.length; index++) { java.lang.reflect.Method method = methods[index]; if ((method.getParameterTypes().length == parameterCount) && (method.getName().equals(methodName))) { return method; } ; } ; } catch (java.lang.Throwable exception) { return null; } ; return null; } /** * Find a property descriptor of a given name in the list. * * @param pds * The array of property descriptors to search, may be null. * @param name * The name to search for. * @return The found property descriptor index, or -1 if not found. */ public static int findPropertyDescriptor(PropertyDescriptor[] pds, String name) { for (int i = 0; i < pds.length; i++) { if (name.equals(pds[i].getName())) return i; } return -1; } /* * (non-Javadoc) * * @see java.beans.BeanInfo#getDefaultEventIndex() */ public int getDefaultEventIndex() { return -1; } /* * (non-Javadoc) * * @see java.beans.BeanInfo#getDefaultPropertyIndex() */ public int getDefaultPropertyIndex() { return -1; } /* (non-Javadoc) * @see java.beans.SimpleBeanInfo#getBeanDescriptor() */ public BeanDescriptor getBeanDescriptor() { // Default is to create an empty one. return createBeanDescriptor(getBeanClass(), EMPTY_ARGS); } /** * Implementation for BeanInfo. This implementation will return the BeanInfo of the superclass. * * @see BeanInfo#getAdditionalBeanInfo() * @since 1.1.0 */ public BeanInfo[] getAdditionalBeanInfo() { try { BeanInfo[] result = new BeanInfo[] { Introspector.getBeanInfo(getBeanClass().getSuperclass())}; PropertyDescriptor[] oPDs = result[0].getPropertyDescriptors(); PropertyDescriptor[] nPDs = overridePropertyDescriptors(oPDs); if (oPDs != nPDs) result[0] = new OverridePDBeanInfo(result[0], nPDs); return result; } catch (IntrospectionException e) { return new BeanInfo[0]; } } private static class OverridePDBeanInfo implements BeanInfo { private BeanInfo originalBeanInfo; private PropertyDescriptor[] overridePDs; public OverridePDBeanInfo(BeanInfo bi, PropertyDescriptor[] pds) { originalBeanInfo = bi; overridePDs = pds; } public BeanInfo[] getAdditionalBeanInfo() { return originalBeanInfo.getAdditionalBeanInfo(); } public BeanDescriptor getBeanDescriptor() { return originalBeanInfo.getBeanDescriptor(); } public int getDefaultEventIndex() { return originalBeanInfo.getDefaultEventIndex(); } public int getDefaultPropertyIndex() { return originalBeanInfo.getDefaultPropertyIndex(); } public EventSetDescriptor[] getEventSetDescriptors() { return originalBeanInfo.getEventSetDescriptors(); } public Image getIcon(int iconKind) { return originalBeanInfo.getIcon(iconKind); } public MethodDescriptor[] getMethodDescriptors() { return originalBeanInfo.getMethodDescriptors(); } public PropertyDescriptor[] getPropertyDescriptors() { return overridePDs; } } /** * Allow overrides to parent beaninfo. Subclasses should override this method if they wish to override and change any inherited properties. This * allows removal of inherited properties or changes of specific properties (such as change from hidden to not hidden). * * Note: If there any changes, this must return a DIFFERENT array. If it returns the same array, then the changes will not be accepted. If just * overriding, should use pds.clone() to get the new array and then change the specific entries. * * @param pds * @return The new changed array or the same array if no changes. * @since 1.1.0 */ protected PropertyDescriptor[] overridePropertyDescriptors(PropertyDescriptor[] pds) { return pds; } /** * Get the bean class this beaninfo is for. Used by subclasses to quickly get the bean class without having to code it over and over. * * @return bean class for this beaninfo. * * @since 1.1.0 */ public abstract Class getBeanClass(); /** * Called whenever the bean information class throws an exception. By default it prints a message and then a stacktrace to sys err. * * @param exception * java.lang.Throwable * @since 1.1.0 */ public void handleException(Throwable exception) { System.err.println(RESBUNDLE.getString("UNCAUGHT_EXC_")); //$NON-NLS-1$ exception.printStackTrace(); } private static void setFeatureDescriptorValue(FeatureDescriptor fd, String key, Object value) { if (DISPLAYNAME.equals(key)) { fd.setDisplayName((String) value); } else if (EXPERT.equals(key)) { fd.setExpert(((Boolean) value).booleanValue()); } else if (HIDDEN.equals(key)) { fd.setHidden(((Boolean) value).booleanValue()); } else if (PREFERRED.equals(key)) { fd.setPreferred(((Boolean) value).booleanValue()); if (JVM_1_3) { // Bug in 1.3 doesn't preserve the preferred flag, so we will put it into the attributes too. fd.setValue(PREFERRED, value); } } else if (SHORTDESCRIPTION.equals(key)) { fd.setShortDescription((String) value); } // Otherwise assume an arbitrary-named value // Assume that the FeatureDescriptor private hashTable\ // contains only arbitrary-named attributes else { fd.setValue(key, value); } } /** * Fatal errors are handled by calling this method. By default it throws an Error exception. * * @param e * exception exception message placed into the new Error thrown. * @param s * message added to exception message. <code>null</code> if nothing to add. * * @throws Error * turns exception into an Error. * @since 1.1.0 */ protected static void throwError(Exception e, String s) { throw new Error(e.toString() + " " + s);//$NON-NLS-1$ } }