Package net.helipilot50.stocktrade.framework

Source Code of net.helipilot50.stocktrade.framework.FrameworkUtils

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package net.helipilot50.stocktrade.framework;

import java.beans.BeanInfo;
import java.beans.Expression;
import java.beans.IndexedPropertyDescriptor;
import java.beans.Introspector;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyDescriptor;
import java.beans.Statement;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import net.helipilot50.stocktrade.framework.anchored.Anchorable;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;

/**
* A collection of utility methods used by framework classes
*/
public class FrameworkUtils {

  private static Logger log = Logger.getLogger(FrameworkUtils.class);
  private static String workingDirectory = null;

  /**
   * Return the current working directory.  If the workingDirectory is null, then
   * return the user.dir System property.
   *    */
    public static synchronized String getWorkingDirectory() {
      if (workingDirectory == null) {
        workingDirectory = System.getProperty("user.dir", "");
      }
     
    return workingDirectory;
  }

    /**
     * Update the workingDirectory.
     * @param workingDirectory
     */
  public static synchronized void setWorkingDirectory(String workingDirectory) {
    FrameworkUtils.workingDirectory = workingDirectory;
  }

  /**
     * Implement the thread.appContext  methods as a ThreadLocal implementation. Each ThreadLocal
     * variable stores a hashtable of values. These need to be passed inter-partition, so we
     * provide getAppContextValues and setAppContextValues to accomodate this
     */
    private static ThreadLocal<Map<String, Object>> appContext = new InheritableThreadLocal<Map<String, Object>>() {
        protected Map<String, Object> initialValue() {
            return new HashMap<String, Object>();
        }
        /**
         * Set the child value on the inheritable thread local. Since we store our thread local values in our
         * own hashtable, if we don't override this method the parent and child tasks will share the same instance
         * of a hashtable. This means that changing the value of a thread local variable in one thread will
         * affect the value of that thread local variable in another thread. This is incorrect behaviour, and
         * even allows sibling tasks to affect one another
         * <p>
         * Note that in Forte, the SetSubtasks parameter to the setAppContext method did not affect existing
         * tasks, but rather tasks created after the app context has been set. The use of the
         * InheritableThreadLocal mimics this behaviour (once the parent's map is shallowly cloned) and
         * thus we're effectively treating all setAppContext calls to have setSubTasks = true. This should not
         * affect people's code.
         */
        @Override
        protected Map<String, Object> childValue(Map<String, Object> parentValue) {
          Map<String, Object> result = new HashMap<String, Object>(parentValue.size());
          result.putAll(parentValue);
          return result;
        }
    };

    public static Object getAppContext(String pKey) {
      // TF:17/06/2008:Made the app context comparison case insensitive
        return (appContext.get()).get(pKey.toUpperCase());
    }

    public static Object getAppContext(TextData pKey) {
        return FrameworkUtils.getAppContext(pKey.toString());
    }

    public static void setAppContext(String pKey, Object pContext) {
      // TF:17/06/2008:Made the app context comparison case insensitive
      if (pKey != null) {
        pKey = pKey.toUpperCase();
      }
        if (pContext == null) {
            (FrameworkUtils.appContext.get()).remove(pKey);
        }
        else {
            (FrameworkUtils.appContext.get()).put(pKey, pContext);
        }
    }

    public static void setAppContext(TextData pKey, Object pContext) {
        FrameworkUtils.setAppContext(pKey.toString(), pContext);
    }

    public static Map<String, Object> getAppContextValues() {
        return FrameworkUtils.appContext.get();
    }

    public static void setAppContextValues(Map<String, Object> pValues) {
        if (pValues == null) {
            // This has probably come from a client or server with no values, just clear it out
            FrameworkUtils.appContext.get().clear();
        }
        else {
            FrameworkUtils.appContext.set(pValues);
        }
    }

    /**
     * This method creates a new instance of the passed classtype. If the
     * type cannot be instantiated, a UsageException is thrown
     * @param pType - the type to instantiate
     * @return the new object
     */
    public static Object newInstance(Class<?> pType) {
        try {
            return pType.newInstance();
        }
        catch (Exception e) {
            UsageException newException = new UsageException(e.getMessage(), e);
            newException.setReasonCode(Constants.SP_ER_UNSUPPORTEDMETHOD);
            throw newException;
        }
    }

    /**
     * Returns a constant to indicate the type of the passed class if the class
     * is a primitive type. If the passed type is not primitive, an illega;
     * argument exception is thrown.
     *
     * This method may return different values than the Forte method of the same
     * name owing to the mapping of forte types to java types.
     * @param pType - the class type under determination
     * @return One of the SP_TYPE_ constants
     * @throws UsageException if the passed type is not primitve
     */
    public static int getBaseType(Class<?> pType) {
        if (pType.isPrimitive()) {
            if (pType.equals(Boolean.TYPE))         return Constants.SP_TYPE_BOOLEAN;
            else if (pType.equals(Character.TYPE))  return Constants.SP_TYPE_CHAR;
            else if (pType.equals(Byte.TYPE))       return Constants.SP_TYPE_UI1;
            else if (pType.equals(Short.TYPE))      return Constants.SP_TYPE_SHORT;
            else if (pType.equals(Integer.TYPE))    return Constants.SP_TYPE_INTEGER;
            else if (pType.equals(Long.TYPE))       return Constants.SP_TYPE_LONG;
            else if (pType.equals(Float.TYPE))      return Constants.SP_TYPE_FLOAT;
            else if (pType.equals(Double.TYPE))     return Constants.SP_TYPE_DOUBLE;
            else                                    return Constants.SP_TYPE_VOID;
        }
        else if (pType.equals(String.class)) {
            return Constants.SP_TYPE_STRING;
        }
        else {
            throw new UsageException(Messages.getString("FrameworkUtils.0")); //$NON-NLS-1$
        }
    }

    /**
     * This method gets the field with the passed name on the passed class,
     * or on the superclass, if the field on the superclass is public.
     * @param pClass - the class to find the field on
     * @param pName - the name of the field
     * @return the desired field, or null if the field cannot be found.
     */
    public static Field getField(Class<?> pClass, String pName) {
        Field aField = null;
        Class<?> aClass = pClass;
        while (aClass != null) {
            // See if this is field is private or protected on this class
            try {
                aField = aClass.getDeclaredField(pName);
                return aField;
            } catch (Exception e) {
                // Doesn't exist on this class, check the superclass
                aClass = aClass.getSuperclass();
            }
        }
        return null;
    }

    /**
     * Retrieve the JavaBeans <code>PropertyDescriptors</code> for the given property.
     * <p>
     * If the passed in property name starts with an upper case character, this method
     * will change it to a property name that confirms to the Java Bean spec that the
     * beans introspector is based on.
     *
     * @param clazz the Class to retrieve the PropertyDescriptor for
     * @param propertyName the name of the property
     * @return the corresponding PropertyDescriptor, or <code>null</code> if none
     */
    public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName)
    {
        String beanPropertyName = propertyName;
        // The JavaBean introspector as per the java bean spec requires property names start
        // with a lower case character.
        if (Character.isUpperCase(propertyName.charAt(0)))
        {
            // Convert it to lower
            StringBuilder buffer = new StringBuilder(beanPropertyName.length());
            buffer.append(Character.toLowerCase(propertyName.charAt(0)));
            buffer.append(propertyName.substring(1));
            beanPropertyName = buffer.toString();
        }

        PropertyDescriptor propertyDescriptor = org.springframework.beans.BeanUtils.getPropertyDescriptor(clazz, beanPropertyName);
        if (propertyDescriptor == null)
        {
            //Property names are case sensitive. Go through all the properties and do a
            // case insensistive search
            PropertyDescriptor[] propertyDescriptors = org.springframework.beans.BeanUtils.getPropertyDescriptors(clazz);

            for (PropertyDescriptor descriptor : propertyDescriptors)
            {
                if (descriptor.getName().equalsIgnoreCase(propertyName))
                {
                    return descriptor;
                }
            }           
        }
        return propertyDescriptor;
    }


    /**
     *   @author Dave Savage
     *   use BeanUtils to get a whole heap of PropertyDescriptors ...
     */
    public static PropertyDescriptor[] getPropertyDescriptors (Class<?> clazz) {
        return org.springframework.beans.BeanUtils.getPropertyDescriptors(clazz);
    }

    public static Array_Of_Field<Field> getFields(Class<?> pClass) {
        // Get the declared fields
        Array_Of_Field<Field> aList = new Array_Of_Field<Field>();
        while (pClass != null) {
            Field[] fields = pClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                // Do not return transient fields
                if (!Modifier.isTransient(fields[i].getModifiers())) {
                    aList.add(fields[i]);
                }
            }
            pClass = pClass.getSuperclass();
        }
        return aList;
    }

    /**
     * Returns an array of references to Field objects that reflect the private and public attributes
     * declared by the passed class.<p>
     * If the current type is not a class or if this class has no attributes, GetAttributes returns an
     * empty array.
     * @param pClass the class on which to get the fields
     * @return the array of fields for this type
     */
    public static Array_Of_Field<Field> getDeclaredFields(Class<?> pClass) {
        // Get the declared fields
        Array_Of_Field<Field> aList = new Array_Of_Field<Field>();
        if (pClass != null) {
            Field []fields = pClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                // Do not return transient fields
                if (!Modifier.isTransient(fields[i].getModifiers())) {
                    aList.add(fields[i]);
                }
            }
        }
        return aList;
    }

    public static boolean isSupported(Class<?> pClass) {
        return (pClass.isPrimitive() || pClass.equals(String.class)) && (!pClass.equals(void.class));
    }

    /**
     * This method gets the method with the passed name on the passed class,
     * or on the superclass, if the method on the superclass is public. This
     * method is needed as the getMethod on Class in java only returns the
     * public methods, not the private ones as well
     * @param pClass - the class to find the field on
     * @param pName - the name of the field
     * @return the desired field, or null if the field cannot be found.
     */
    public static Method getMethod(Class<?> pClass, String pName, Class<?>[] pParams) {
        Method aMethod = null;
        Class<?> aClass = pClass;
        while (aClass != null) {
            try {
                aMethod = aClass.getDeclaredMethod(pName, pParams);
                return aMethod;
            } catch (Exception e) {
                // Doesn't exist on this class, check the superclass
                aClass = aClass.getSuperclass();
            }
        }
        return null;
    }

    public static Array_Of_Method<Method> getMethods(Class<?> pClass) {
        // Get the declared fields
        Array_Of_Method<Method> aList = new Array_Of_Method<Method>();
        while (pClass != null) {
            Method []methods = pClass.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                aList.add(methods[i]);
            }
            pClass = pClass.getSuperclass();
        }
        return aList;
    }

    /**
     * Returns an array of references to Method objects that reflect the private and public attributes
     * declared by the passed class.<p>
     * If the current type is not a class or if this class has no attributes, getDeclaredMethods returns an
     * empty array.
     * @param pClass the class on which to get the fields
     * @return the array of fields for this type
     */
    public static Array_Of_Method<Method> getDeclaredMethods(Class<?> pClass) {
        // Get the declared methods
        Array_Of_Method<Method> aList = new Array_Of_Method<Method>();
        if (pClass != null) {
            Method[] methods = pClass.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                aList.add(methods[i]);
            }
        }
        return aList;
    }

    /**
     * Set a property value.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     * @return true in all cases.
     * @throws BeansException if the property doesn't exist or cannot be set.
     */
    public static boolean setProperty(String propertyName, Object bean,  Object value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(propertyName, value);
       
        // This method must return true as it is occassionally used for mapping output parameters of methods
        // invoked in while loop variants back to their underlying values.
        return true;
    }

    /**
     * Return the property name that would be associated with a field with the passed name.
     * This is the correct way of doing it, rather than forming "set" + pField.getName because the
     * case of the getter / setter may not match.
     * @param pFieldName the name of the field
     * @return the property that would need to be set for this field
     */
    public static String getPropertyName(String pFieldName) {
        StringBuilder fieldName = new StringBuilder(pFieldName);
        // the bean spec calls for a lower case first letter unless the second
        // character is in upper case...
        boolean needsLowerCaseFirstLetter = true;
        if (fieldName.length() > 1) {
            if (Character.isUpperCase(fieldName.charAt(1))) {
                fieldName.setCharAt(0, Character.toUpperCase(fieldName.charAt(0)));
                needsLowerCaseFirstLetter = false;
            }
        }
        if (needsLowerCaseFirstLetter) {
            fieldName.setCharAt(0, Character.toLowerCase(fieldName.charAt(0)));
        }
        return fieldName.toString();
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, Object value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), value);
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, int value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Integer(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, boolean value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), Boolean.valueOf(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, short value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Short(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, float value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Float(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, double value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Double(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, long value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Long(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, char value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Character(value));
    }

    /**
     * Set a property value based on the contents of a field. This method will translate
     * the field name into the appropriate property prior to setting it.
     * @param propertyName name of the property to set value of
     * @param bean the bean whose property we are setting
     * @param value the new value
     */
    public static void setProperty(Field field, Object bean, byte value)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        wrapper.setPropertyValue(getPropertyName(field.getName()), new Byte(value));
    }


    /**
     * This method sets the value of a field to a particular object. Because the fields
     * in Java are often private, if it cannot set the value of the field, it tries to
     * invoke set<<Field>> method instead.
     * @param pField - The field to set the value of
     * @param pOwner - The object that this field belongs to
     * @param pObject - the value to set.
     */
    public static void setFieldValue(Field pField, Object pOwner, Object pObject) {
        try {
            setProperty(pField, pOwner, pObject);
        }
        catch (BeansException be) {
            // First, see if this field is marked as Final. If so, we will not be
            // able to set it's value via introspection, so throw an exception.
            int theModifiers = pField.getModifiers();
            if (Modifier.isFinal(theModifiers)) {
                throw new UsageException(Messages.getString("FrameworkUtils.1") + pField.getName() + Messages.getString("FrameworkUtils.2") //$NON-NLS-1$ //$NON-NLS-2$
                        + pField.getDeclaringClass().getName() + Messages.getString("FrameworkUtils.3")); //$NON-NLS-1$
            }

            if (pField.getType().isPrimitive() || pField.getType().equals(String.class)) {
                pObject = FrameworkUtils.mapToCorrectJavaPrimitiveType(pObject, pField.getType());
            }
            try {
                pField.setAccessible(true);
                pField.set(pOwner, pObject);
            } catch (Exception e) {
                Class<?> aClass = pField.getDeclaringClass();
                String aFieldName;
                String tempName = pField.getName();
                aFieldName = "set" + tempName.substring(0,1).toUpperCase() + ((tempName.length() > 1) ? tempName.substring(1) : ""); //$NON-NLS-1$ //$NON-NLS-2$

                try {
                    Method aMethod;

                    try {
                        aMethod= aClass.getDeclaredMethod(aFieldName, (Class[])null);
                    }
                    catch (NoSuchMethodException nsme) {
                        aFieldName = "set" + tempName; //$NON-NLS-1$
                        aMethod= aClass.getDeclaredMethod(aFieldName, (Class[])null);
                    }
                    aMethod.invoke(pOwner, new Object[] {pObject});
                }
                catch (Exception e1) {
                    throw new UsageException(Messages.getString("FrameworkUtils.7") + pField.getName() + Messages.getString("FrameworkUtils.8") + aClass.getName() + ": " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                }
            }
        }
    }

    /**
     * Translate a primitive class to the appropriate data value. Note that for this purpose, a string
     * is still considered a primitive type, as we want a mutable value, and String doesn't provide this.
     * @param pClass
     * @return
     */
    public static Class<?> getDataValueClass(Class<?> pClass) {
        if (pClass.equals(Boolean.TYPE))    return BooleanData.class;
        else if (pClass.equals(Character.TYPE))  return TextData.class;
        else if (pClass.equals(Byte.TYPE))    return IntegerData.class;
        else if (pClass.equals(Short.TYPE))    return IntegerData.class;
        else if (pClass.equals(Integer.TYPE))  return IntegerData.class;
        else if (pClass.equals(Long.TYPE))    return IntegerData.class;
        else if (pClass.equals(Float.TYPE))    return DoubleData.class;
        else if (pClass.equals(Double.TYPE))  return DoubleData.class;
        else if (pClass.equals(String.class))  return TextData.class;
        else                  return pClass;
    }

    /**
     * For introspection calls, Forte returns DataValue subclasses for primitive types, including
     * string. However, Java returns String, Boolean, etc values. As the Forte code normally
     * expects the datavalue subclasses, this method will map from the Java types to the DataValue
     * subclasses.
     *
     * If the passed object is not a class that encapsulates a primitive type or a String, the passed
     * object is returned.
     *
     * @param pObject - the object to map
     * @return a data value, or the passed object
     */
    public static Object mapToDataValue(Object pObject) {
        if (pObject == null)          return null;
        else if (pObject instanceof String)    return new TextData((String)pObject);
        else if (pObject instanceof Booleanreturn new BooleanData(((Boolean)pObject).booleanValue());
        else if (pObject instanceof Characterreturn new TextData(((Character)pObject).toString());
        else if (pObject instanceof Byte)       return new IntegerData(((Byte)pObject).intValue());
        else if (pObject instanceof Short)      return new IntegerData(((Short)pObject).shortValue());
        else if (pObject instanceof Integer)    return new IntegerData(((Integer)pObject).intValue());
        else if (pObject instanceof Long)       return new IntegerData(((Long)pObject).intValue());
        else if (pObject instanceof Float)      return new DoubleData(((Float)pObject).floatValue());
        else if (pObject instanceof Double)     return new DoubleData(((Double)pObject).doubleValue());
        else if (pObject.getClass().isArray()) {
          // AD:6/6/2008 Check if a primitive array and convert it to an object array
          Object[] objectList;
          if (pObject.getClass().getComponentType().isPrimitive()) {
            int arrayLength = Array.getLength(pObject);
            objectList = new Object[arrayLength];
            for (int i = 0; i < arrayLength; i++) {
              objectList[i] = FrameworkUtils.mapToDataValue(Array.get(pObject, i));
        }
      } else {
        objectList = (Object[])pObject;
      }
                            return mapToDynamicArray(objectList);
        }
        else                  return pObject;
    }

    /**
     * For introspection calls, Forte returns DynamicArray subclasses for primitive arrays.
     *
     * @param objectList - the object array to map
     * @return a DynamicArray based on the array
     * @author AD:5/6/2008
     */
    @SuppressWarnings("unchecked")
  public static DynamicArray<?> mapToDynamicArray(Object[] objectList) {
        if (objectList == nullreturn null;
      DynamicArray<Object> dynamicArray;
      Class<?> classType = objectList.getClass().getComponentType();
      // Instantiate the DynamicArray
        try {
          String dynamicArrayClassName = FrameworkUtils.getPackageName(classType) + ".Array_Of_" + FrameworkUtils.getClassName(classType);
          dynamicArray = (DynamicArray)classType.getClassLoader().loadClass(dynamicArrayClassName).newInstance();
    } catch (Exception e) {
      dynamicArray = new DynamicArray<Object>();
    }
    // Add items into DynamicArray
    dynamicArray.addAll(Arrays.asList(objectList));
      return dynamicArray;
    }

    /**
     * For introspection calls, Forte passes DataValue subclasses for primitive types, including
     * string. However, Java expects String, Boolean, etc values. This method will map from the
     * Forte types to the Java primitive types
     *
     * If the passed object is not a DataValue subclass, the passed object is returned.
     *
     * @param pObject - the object to map
     * @return a data value, or the passed object
     */
    public static Object mapToJavaPrimitiveType(Object pObject) {
        if (pObject == null)            return null;
        else if (pObject instanceof TextData)    return ((TextData)pObject).isNull() ? null : pObject.toString();
        else if (pObject instanceof BooleanDatareturn ((BooleanData)pObject).isNull() ? null : Boolean.valueOf(((BooleanData)pObject).getValue());
        else if (pObject instanceof DecimalDatareturn ((DecimalData)pObject).isNull() ? null : new Double(((DecimalData)pObject).getValue());
        else if (pObject instanceof DoubleData)     return ((DoubleData)pObject).isNull() ? null : new Double(((DoubleData)pObject).getValue());
        else if (pObject instanceof IntegerData)    return ((IntegerData)pObject).isNull() ? null : new Integer(((IntegerData)pObject).getValue());
        else                    return pObject;
    }

    /**
     * For introspection calls, Forte passes DataValue subclasses for primitive types, including
     * string. However, Java expects String, Boolean, etc values. This method will map from the
     * Forte types to the Java primitive types. When mapping to an attribute, Forte would try to
     * automatically convert the object to the correct type for primitive types. This method
     * mirrors this in Java.
     *
     * If the passed object is not a DataValue subclass, the passed object is returned.
     *
     * @param pObject - the object to map
     * @return a data value, or the passed object
     */
    public static Object mapToCorrectJavaPrimitiveType(Object pObject, Class<?> pType) {
        if (pObject == null || pType == null)      return null;
        if (pType.isPrimitive() || pType.equals(String.class)) {
            if (pObject instanceof TextData) {
                TextData aTextData = (TextData)pObject;
                if (pType.equals(Boolean.TYPE))      return Boolean.valueOf(aTextData.getBooleanValue());
                else if (pType.equals(Byte.TYPE))    return new Byte(aTextData.toString());
                else if (pType.equals(Short.TYPE))    return new Short(aTextData.toString());
                else if (pType.equals(Integer.TYPE))  return new Integer(aTextData.toString());
                else if (pType.equals(Long.TYPE))    return new Long(aTextData.toString());
                else if (pType.equals(Float.TYPE))    return new Float(aTextData.toString());
                else if (pType.equals(Double.TYPE))    return new Double(aTextData.toString());
                else                  return aTextData.toString();
            }
            else if (pObject instanceof NumericData) {
                NumericData aNumericData = (NumericData)pObject;
                if (pType.equals(Boolean.TYPE))      return Boolean.valueOf(aNumericData.getTextValue().getBooleanValue());
                else if (pType.equals(Byte.TYPE))    return new Byte((byte)aNumericData.intValue());
                else if (pType.equals(Short.TYPE))    return new Short((short)aNumericData.intValue());
                else if (pType.equals(Integer.TYPE))  return new Integer(aNumericData.intValue());
                else if (pType.equals(Long.TYPE))    return new Long(aNumericData.intValue());
                else if (pType.equals(Float.TYPE))    return new Float(aNumericData.doubleValue());
                else if (pType.equals(Double.TYPE))    return new Double(aNumericData.doubleValue());
                else                  return aNumericData.toString();
            }
            else if (pObject instanceof BooleanData) {
                BooleanData aBool = (BooleanData)pObject;
                if (pType.equals(Boolean.TYPE))      return Boolean.valueOf(aBool.getValue());
                else if (pType.equals(String.class))  return String.valueOf(aBool.getValue());
            }
        }
        return pObject;
    }

    /**
     * Get the the value of the field on the passed object. If the field is private, this method
     * will also search for methods called "get<Field>" and "get<field>" to see if it can find
     * the value. Note that primitive types in Forte will be mapped to datavalue subclasses, but
     * in Java they'll be mapped to Boolean, Integer, etc values. Hence we explicitly map them.
     * @param pField
     * @param pOwner
     * @return
     */
    public static Object getFieldValue(Field pField, Object pOwner) {
        Object result = null;
        try {
            pField.setAccessible(true);
            result = pField.get(pOwner);
        } catch (Exception e) {
            Class<?> aClass = pField.getDeclaringClass();
            String aFieldName;
            String tempName = pField.getName();
            aFieldName = "get" + tempName.substring(0,1).toUpperCase() + ((tempName.length() > 1) ? tempName.substring(1) : ""); //$NON-NLS-1$ //$NON-NLS-2$

            try {
                Method aMethod;

                try {
                    aMethod= aClass.getDeclaredMethod(aFieldName, (Class[])null);
                }
                catch (NoSuchMethodException nsme) {
                    aFieldName = "get" + tempName; //$NON-NLS-1$
                    aMethod= aClass.getDeclaredMethod(aFieldName, (Class[])null);
                }
                result = aMethod.invoke(pOwner, (Object[])null);
            }
            catch (Exception e1) {
                // TOOL ignores this case, as they're really all public. We'll return null
                result = null;
            }
        }
        return FrameworkUtils.mapToDataValue(result);
    }

    /**
     * gets the value of a Forte attribute by name
     * @param propName - the name of the Attribute
     * @param owner - The object reference
     * @return
     */
    public static Object getAttributeValue(String propName, Object owner){
        try {
            Expression expr = new Expression(owner, "get" + propName, new Object[0]); //$NON-NLS-1$
            expr.execute();
            return expr.getValue();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Sets the value of a Forte Attribute.
     * @param propName
     * @param owner
     * @param value
     */
    public static boolean setAttributeValue(String propName, Object owner, Object value){
        try {
            Statement stmt = new Statement(owner, "set" + propName, new Object[]{value}); //$NON-NLS-1$
            stmt.execute();
            return true;
        } catch (Exception e) {
            return false;    }   
    }

    public static Object invokeMethod(Method pMethod, Object pObject, DynamicArray<? extends Object> args) {
      return FrameworkUtils.invokeMethod(pMethod, pObject, args == null ? null : args.toArray());
    }
   
    public static Object invokeMethod(Method pMethod, Object pObject, Object[] args) {
        /*
         * translate any primitive type arguments into java-style primitive arguements.
         */
        Class<?>[] params = pMethod.getParameterTypes();
        for (int i = 0; args != null && i < params.length; i++) {
            args[i] = FrameworkUtils.mapToCorrectJavaPrimitiveType(args[i], params[i]);
        }
        pMethod.setAccessible(true);
        Object result;
        try {
            result = pMethod.invoke(pObject, args);
            return FrameworkUtils.mapToDataValue(result);
        } catch (UsageException e) {
            log.error(Messages.getString("FrameworkUtils.15") + pMethod.toString(), e); //$NON-NLS-1$
            UsageException errorVar = new UsageException(e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        } catch (IllegalAccessException e) {
            log.error(Messages.getString("FrameworkUtils.16") + pMethod.toString(), e); //$NON-NLS-1$
            UsageException errorVar = new UsageException(e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        } catch (InvocationTargetException e) {
            if (e.getCause() != null) {
                if (e.getCause() instanceof RuntimeException) {
                    throw (RuntimeException)e.getCause();
                }
            }
            log.error(Messages.getString("FrameworkUtils.17") + pMethod.toString(), e); //$NON-NLS-1$
            UsageException errorVar = new UsageException(e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * This method returns the type of element within an array. It relies on the
     * array having a static method called getElementType that returns the classtype
     * (because java has no equivalent of an element in an ArrayList)
     * @param pType
     * @return
     */
    public static Class<?> getArrayType(Class<?> pType) {
        Class<?> result = null;
        try {
            result = (Class<?>)(pType.getMethod("getElementType", (Class[])null).invoke((Object)null, (Object[])null)); //$NON-NLS-1$
        }
        catch (Exception e) {
        }
        return result;
    }

    /**
     * This method returns the type of element within a List. Since List is an interface,
     * the static method called getElementType should be from the concrete implementation
     * class that returns the classtype.
     * @param pType
     * @return
     */
    public static Class<?> getListType(Class<?> pType) {
        Class<?> result = null;
        try {
            // Get the concrete implementation class of the interface.
            // Note well that this will work only if the name of the
            // concrete implementation class is composed with the name
            // of the interface and suffixed with "Impl".
            TextData name = new TextData();
            name.concat(pType.getName()).concat("Impl");
            Class<?> implementationClass = null;
            implementationClass = getClassTypeFromClassName(name.getValue());

            result = (Class<?>)(implementationClass.getMethod("getElementType", (Class[])null).invoke((Object)null, (Object[])null)); //$NON-NLS-1$
        }
        catch (Exception e) {
        }
        return result;
    }

    /**
     * This method returns the node name of the current node (machine). If no node information is
     * available, this method will return the string "Unknown"
     * @return the name of the local machine
     */
    public static TextData getNodeName() {
        try {
            return new TextData(InetAddress.getLocalHost().getHostName());
        }
        catch (UnknownHostException e) {
            return new TextData(Messages.getString("FrameworkUtils.19")); //$NON-NLS-1$
        }
    }

    /**
     * get the object reference of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField - the field to get the value of
     * @param pObject - the object on which to apply this get operation
     * @return the referenced object
     */
    public static Object getObject(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.get(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * get the object reference of the property on the passed java bean. This method will translate any
     * non-runtime exception into a runtime exception
     * @param property the property to get the value of
     * @param bean the java bean on which to apply this get operation
     * @return the referenced object
     */
    public Object getPropertyValue(PropertyDescriptor property, Object bean)
    {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        return wrapper.getPropertyValue(property.getName());
    }

    /**
     * Checks whether a property on a given bean is equal to the specified
     * value.
     *
     * @param property the property on the target bean
     * @param bean the target bean that contains the property
     * @param value the value we are hecking against
     * @return true if the property is equal to the passed in value
     */
    public static boolean isEqual(PropertyDescriptor property, Object bean, Object value)
    {
        try {
            return property.getReadMethod().invoke(bean).equals(value);
        }
        catch (Exception e) {
            throw new UsageException(e.getMessage(), e);
        }
    }

    /**
     * gets the integer value of the field.
     * @param pField
     * @param pObject
     * @return
     */
    public static int getInt(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getInt(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the float value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static float getFloat(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getFloat(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the Long value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static long getLong(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getLong(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the short value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static short getShort(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getShort(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the double value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static double getDouble(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getDouble(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the boolean value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static boolean getBoolean(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getBoolean(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * gets the char value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static char getChar(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getChar(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }

    /**
     * gets the byte value of the field
     * @param pField
     * @param pObject
     * @return
     */
    public static byte getByte(Field pField, Object pObject) {
        try {
            pField.setAccessible(true);
            return pField.getByte(pObject);
        }
        catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Gets the class name.
     * @param c = Class
     * @param pFullyQualified If this is true, the returned string will include the package
     * @return The class name without the package name
     */
    public static String getClassName(Class<?> c, boolean pFullyQualified) {
        String FQClassName = c.getName();
        if (!pFullyQualified) {
            int firstChar;
            firstChar = FQClassName.lastIndexOf ('.') + 1;
            if ( firstChar > 0 ) {
                FQClassName = FQClassName.substring ( firstChar );
            }
        }
        return FQClassName;
    }

    /**
     * Gets the class name only. (Not qualified by the package).
     * @param c = Class
     * @return The class name without the package name
     */
    public static String getClassName(Class<?> c) {
        return FrameworkUtils.getClassName(c, false);
    }

    /**
     * Gets the package name for a class.
     * @param c = Class
     * @return The class name without the package name
     */
    public static String getPackageName(Class<?> c) {
        String packageName = c.getName();
    int lastChar;
    lastChar = packageName.lastIndexOf('.') + 1;
    if (lastChar > 0) {
      packageName = packageName.substring(0, lastChar-1);
    } else {
      packageName = "";
    }
        return packageName;
    }

    /**
     * Set the object reference of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField - the field to get the value of
     * @param pObject - the object on which to apply this get operation
     * @param pValue - the value to set
     */
    public static void setObject(Field pField, Object pObject, Object pValue) {
      // CraigM:05/06/2008 - Call the setter so the data binding will work
      boolean success = false;
        try {
        String standardFieldName = pField.getName();
          TextData fieldName = new TextData(standardFieldName);
          // AD:10/6/2008 If the first character is lower case and the second is upper case then make the first upper case.
          if (fieldName.isLower()) {
            fieldName.setOffset(1);
            if (fieldName.isUpper()) {
              standardFieldName = standardFieldName.substring(0,1).toUpperCase() + standardFieldName.substring(1);
        }
      }

          // Ask Spring to get the write method to set the attribute (this will call the setter)
          PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(pObject.getClass(), standardFieldName);
         
          if (pd != null) {
            pd.getWriteMethod().invoke(pObject, pValue);
              success = true;
          }
    } catch (Throwable e) {
    }
   
    // CraigM:05/06/2008 - Could not find the setter, try to set the field directly
    if (success == false) {
          try {
              pField.setAccessible(true);
              pField.set(pObject, pValue);
          } catch (IllegalAccessException e) {
              UsageException errorVar = new UsageException(e.getMessage());
              ErrorMgr.addError(errorVar);
              throw errorVar;
          }
    }
    }
   
    /**
     * Sets the property on the passed bean. This method will translate any
     * non-runtime exception into a runtime exception
     * @param property the property to set the value of
     * @param bean the bean on which to apply this get operation
     * @param value the value to set
     */
    public static void setObject(PropertyDescriptor property, Object bean, Object value) {
      try
      {
        property.getWriteMethod().invoke(bean, value);
      } catch (Exception e) {
        throw new UsageException(e);
      }
    }

    /**
     * Set the integer value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setInteger(Field pField, Object pObject, int pValue) {
        try {
            pField.setAccessible(true);
            pField.setInt(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the integer value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setInteger(Field pField, Object pObject, double pValue) {
        setInteger(pField, pObject, (int)pValue);
    }
    /**
     * Set the integer value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setInt(Field pField, Object pObject, int pValue) {
        try {
            pField.setAccessible(true);
            pField.setInt(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    public static void setInt(Field pField, Object pObject, long pValue) {
      setInt(pField, pObject, (int)pValue);
    }
    /**
     * Set the long value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setLong(Field pField, Object pObject, long pValue) {
        try {
            pField.setAccessible(true);
            pField.setLong(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the long value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setLong(Field pField, Object pObject, int pValue) {
      setLong(pField, pObject, (long)pValue);
    }
    /**
     * Set the long value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setLong(Field pField, Object pObject, double pValue) {
      setLong(pField, pObject, (long)pValue);
    }
    /**
     * Set the integer value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setShort(Field pField, Object pObject, int pValue) {
        try {
            pField.setAccessible(true);
            pField.setShort(pObject, (short)pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the double value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setDouble(Field pField, Object pObject, double pValue) {
        try {
            pField.setAccessible(true);
            pField.setDouble(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the float value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setFloat(Field pField, Object pObject, float pValue) {
        try {
            pField.setAccessible(true);
            pField.setFloat(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the float value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
  public static void setFloat(Field pField, Object pObject,
      double pValue) {
    setFloat(pField, pObject, (float)pValue)
  }
    /**
     * Set the boolean value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
    public static void setBoolean(Field pField, Object pObject, boolean pValue) {
        try {
            pField.setAccessible(true);
            pField.setBoolean(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the property change listeners on this object and its children in the object graph.
     * This ensures that all the leaf elements can be mapped to a window
     * @param obj - the root object in the object graph.
     * @param pcs - the PropertyChangeSuport  object from the window.
     */
    public static void setPropertyListeners(Object obj, String prefix, PropertyChangeSupport pcs){
        Set<Object> map = new HashSet<Object>();
        setPropertyListeners(obj, prefix, pcs, map);
    }
   
    private static void setPropertyListeners(Object obj, String prefix, PropertyChangeSupport pcs, Set<Object> map){
        if (map.contains(obj)) {
          return;
        }
        try {
            map.add(obj);
            /*
             * if the class has the method setPropertyListeners(), execute it with the
             * passed in PropertyChangeSupport
             */
            if (obj == null) {
              return;
            }
            Method spl = obj.getClass().getMethod("setPropertyListeners",new Class[] {PropertyChangeSupport.class}); //$NON-NLS-1$
            if (spl == null) {
              return;
            }
           
            Object[] args = new Object[] {pcs};
            spl.invoke(obj, args);
            /*
             * Set the prefix that is added to each property change event from this class
             */
            Logger.getLogger("PropertyChange").debug("Property change Prefix >" + prefix + "<"); //$NON-NLS-1$ //$NON-NLS-2$
            Statement stmt = new Statement(obj, "setChangePrefix"new Object[] {prefix}); //$NON-NLS-1$
            stmt.execute();
            if ((obj instanceof DataValue) || (obj instanceof GenericNode))
                return;
            BeanInfo bi = Introspector.getBeanInfo(obj.getClass());

            /*
             * now check to see if this class has an object graph of children
             * if so execute this method on each child
             */
            PropertyDescriptor[] pds = bi.getPropertyDescriptors();
            for (int i = 0; i < pds.length; i++){
                PropertyDescriptor pd = pds[i];
                if (pd instanceof IndexedPropertyDescriptor)
                    continue;
                Class<?> type = pd.getPropertyType();
                if (type.isPrimitive())
                    continue;
                if (type.isArray())
                    continue;
                if (pd.getWriteMethod() == null)
                    continue;
                if (Object.class.isAssignableFrom(type)) {
                    Method rm = pd.getReadMethod();
                    if (rm == null)
                        continue;
                    Object o = rm.invoke(obj, new Object[0]);
                    if (o == null)
                        continue;
                    Logger.getLogger("PropertyChange").debug(FrameworkUtils.getClassName(o.getClass()) + " : " + Integer.toString(i) + " : " + pd.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    setPropertyListeners(o, prefix + "_" + pd.getName().toUpperCase(), pcs); //$NON-NLS-1$
                } else {
                    continue;
                }
            }
        } catch (NoSuchMethodException e) {
            // do nothing
        } catch (UsageException e) {
            Logger.getLogger("task.part.logmgr").error("setPropertyListeners() failed", e); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (IllegalAccessException e) {
            Logger.getLogger("task.part.logmgr").error("setPropertyListeners() failed", e); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (InvocationTargetException e) {
            // do nothing
        } catch (Exception e) {
            Logger.getLogger("task.part.logmgr").error("setPropertyListeners() failed", e); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * Set the value change listeners on this object and its children in the object graph.
     * This ensures that all the leaf elements can be mapped to a window
     * @param obj - the object who is listening.
     *
     */
    public static void setValueListeners(Object changeProducer, Object listener){
        try {
            //Method[] meths = obj.getClass().getMethods();
            if (changeProducer == null) return;
            Method spl = changeProducer.getClass().getMethod("addListener", new Class[] {Object.class}); //$NON-NLS-1$
            Object[] args = new Object[] {listener};
            spl.invoke(changeProducer, args);
        } catch (NoSuchMethodException e) {
            // do nothing
        } catch (UsageException e) {
            Logger.getLogger("task.part.logmgr").error("setValueListeners() failed", e); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (IllegalAccessException e) {
            Logger.getLogger("task.part.logmgr").error("setValueListeners() failed", e); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (InvocationTargetException e) {
            // do nothing
        }
    }

    /**
     * TraceBack sumilates the Forte method task.TraceBack()
     * @return - String containing the Stack Trace
     */
    public static String traceBack(){//PM:6 oct. 2008:recoded using a StringBuilder
        StackTraceElement[] traces = new Exception("Stack Trace").getStackTrace();
        return formTraceBackString(traces);
    }

    public static String traceBack(Throwable e){//PM:6 oct. 2008:recoded using a StringBuilder
        StackTraceElement[] traces = e.getStackTrace();
        return formTraceBackString(traces);
    }
    private static String formTraceBackString(StackTraceElement[] trace){
      StringBuilder result = new StringBuilder(1000);
        result.append("Traceback:\n");
        // This method is actually going to be the 0th element, so avoid it.
        for (int i = 1; i < trace.length; i++){
          StackTraceElement element = trace[i];
          String fileName = element.getFileName();
          int lineNumber = element.getLineNumber();
            result.append("\tat ").append(element.getClassName()).append(".").append(element.getMethodName());
            if  (element.isNativeMethod()){
              result.append("(Native Method)");
            } else {
             if (fileName != null && lineNumber >= 0){
               result.append("(").append(fileName).append(":").append(lineNumber).append(")");
             } else {
               result.append("(").append(fileName).append(":").append("(Unknown Source))");
             }
            }
              result.append("\n");
        }
        return result.toString();
    }
    /**
     * Trim any trailing spaces off a string. This is needed as a utility as every datafield
     * (in forte) automatically right trims its value before mapping it and we don't want to
     * incur the cost of instantiating a textdata every time
     * @param pValue - the string to right trim
     * @return the trimmed value
     */
    public static String trimTrailing(String pValue) {
        if (pValue == null)
            return null;

        StringCharacterIterator iter = new StringCharacterIterator(pValue);
        for (char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
            if (!Character.isWhitespace(c)) {
                break;
            }
        }

        if ((pValue.length() - iter.getIndex()) > 0) {
            pValue = pValue.substring(0,iter.getIndex() + 1);
        }
        return pValue;
    }
    /**
     * This method simulates the WorstSeverity attribute on the Forte ErrorMgr.
     * It decents the list of causes in the exception list looking for the worst severity
     * @param th - throwable
     * @return
     */
    public static int getWorstSeverity(Throwable th) {
        int worst = Constants.SP_ER_INFORMATION;
        Throwable target = th;
        while (target != null) {
            if (target instanceof GenericException)
                if (((GenericException)target).severity > worst)
                    worst = ((GenericException)th).severity;
            target = target.getCause();
        }
        return worst;
    }
    public static void checkValidJVM() throws JVMVersionException {
        String jvmVersion = System.getProperty("java.version");
        String requiredVersion = PropertiesMgr.getProperty(PropertiesMgr.cJVM_VERSION);

        if (requiredVersion.compareTo(jvmVersion) > 0) {
            JVMVersionException errorVar = new JVMVersionException(requiredVersion, jvmVersion);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Returns the LANGUAGE And TERRITORY portions of the current default locale in the Forte format.  
     * @return
     */
    public static String getLocale() {
        String result = Locale.getDefault().getLanguage()
            + "_"
            + Locale.getDefault().getCountry().toLowerCase();
        return result;
    }
    /**
     * Sets the default locale using the Forte format.
     * @param locale
     */
    public static void setLocale(TextData locale){
        setLocale(locale.getValue());
    }
    public static void setLocale(String locale){
        if (locale.length() < 5) {
            UsageException errorVar = new UsageException("Invalid locale string");
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        String language = locale.substring(0,2);
        String country = locale.substring(3,5).toUpperCase();
        Locale.setDefault(new Locale(language, country));
    }
    /**
     * gets the command line arguments
     * @return
     */
    public static Array_Of_TextData<TextData> getCmdLineArgs(){
        return Partition.getCmdLineArgs();
    }
    /**
     * sets the command line arguments
     * @param args
     */
    public static void setCmdLineArgs(String[] args){

        Partition.setCmdLineArgs(args);
    }
    /**
     * returns true if the references is a proxy class to a service object.
     * @param ref
     */
    public static boolean isProxy(Object ref){
        String name = ref.getClass().getName();
        return (name.indexOf("_proxy") != -1);
    }
    /**
     * returns the line number of the method
     * @return
     */
    public static int getMethodLocation(RuntimeException e) {
            return e.getStackTrace()[0].getLineNumber();
    }

    /**
     * The ExpandVars method checks the value of the input object to see if it is an environment
     * variable in the current environment. If so, the method returns an object with the value of
     * the variable.<p>
     * <p>
     * The ExpandVars method checks whether the input object has the format Value. This format
     * indicates that the enclosed value is an environment variable. ExpandVars returns a TextData
     * object that contains the actual value of the environment variable.<p>
     * <p>
     * To expand a variable and convert it to a portable format, use the notation %{value}.<p>
     * <p>
     * @param pVar
     * @return
     */
    public static TextData expandVars(String pVar) {
      // TF:27/3/08:Replaced string buffer with string builder and cleaned up to remove null pointer exceptions
        StringBuilder result = new StringBuilder(pVar.length());
        int index = 0;
        int nextIndex = 0;
        while ((nextIndex = pVar.indexOf("${", index)) >= 0) {
            if (nextIndex > index) {
                // Constant part of the string, copy to the output buffer
                result.append(pVar.substring(index, nextIndex));
            }
            nextIndex += 2;
            int endIndex = pVar.indexOf('}', nextIndex);
            String varToFind = pVar.substring(nextIndex, endIndex).trim();
            if (System.getenv(varToFind) != null)
                result.append(System.getenv(varToFind));
            index = endIndex + 1;
        }
        result.append(pVar.substring(index));

        // Also look for any %{ vars and expand them using file.separator
        index = 0;
        nextIndex = 0;

        while ((nextIndex = result.indexOf("%{", index)) >= 0) {
            int endIndex = result.indexOf("}", nextIndex);
            String varToFind = result.substring(nextIndex+2, endIndex).trim();
            String varValue = "";
            if (varToFind != null) {
              varValue = System.getenv(varToFind);
              if (varValue != null) {
                varValue = varValue.replaceAll("/\\\\", System.getProperty("file.separator"));
              }
              else {
                varValue = "";
              }
            }
            result.replace(nextIndex, endIndex+1, varValue);
            index = nextIndex + varValue.length();
        }

        return new TextData(result);
    }

    /**
     * The ExpandVars method checks the value of the input object to see if it is an environment
     * variable in the current environment. If so, the method returns an object with the value of
     * the variable.<p>
     * <p>
     * The ExpandVars method checks whether the input object has the format Value. This format
     * indicates that the enclosed value is an environment variable. ExpandVars returns a TextData
     * object that contains the actual value of the environment variable.<p>
     * <p>
     * @param pVar
     * @return
     */
    public static TextData expandVars(TextData pVar) {
        if (pVar == null) {
            return null;
        }
        else {
            return FrameworkUtils.expandVars(pVar.getValue());
        }
    }

    /**
     * Returns the a java Class object for the given class name.
     * <p>
     * This method originally existed on the LibraryMgr class.
     *
     * @param className the fully qualified name of the class
     * @return a java Class object
     */
    public static Class<?> getClassTypeFromClassName(String className)
    {
        try
        {
            return Class.forName(className);
        }
        catch (ClassNotFoundException e)
        {
            GenericException errorVar = new GenericException("Unable to load class" + className, e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * returns the parent of a thread
     * @param th
     * @return
     */
    public static Thread getParent(Thread th){
      return Task.getParent(th);
    }

    /**
     * A type safe check to see if the thread/task is cancelled
     * @param th
     * @return
     */
    public static boolean isTaskCancelled(Thread th){
      return Task.isCancelled(th);
    }

    /**
     * determines if the objec tis anchorable and if its anchored
     * @param ref
     * @return
     */
    public static boolean isAnchored(Object ref){

            try {
            Expression expr = new Expression(ref, "getIsAnchored", new Object[0]);
                expr.execute();
                boolean value = Boolean.valueOf(expr.getValue().toString()).booleanValue();
                return value;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Sets the object to ge anchored if it is possible. Does noting if it is not anchorable
     * @param ref
     * @param value
     */
    public static void setAnchored(Object ref, boolean value) {
      if (ref instanceof Anchorable) {
        ((Anchorable)ref).setIsAnchored(value);
      }
      else {
          try {
              Expression expr = new Expression(ref, "setIsAnchored",
                      new Object[] { value });
              expr.execute();
          } catch (Exception e) {
              //Ignore it
          }
      }
    }

    /**
     * Retrieve a constant representing the underlying operting system type.
     * @return
     */
    public static int getOSType() {
        String name = System.getProperty("os.name");
        if (name.equals("Windows 95") || name.equals("Windows 98")) {
            return Constants.OS_OT_WIN95;
        }
        else if (name.equals("Windows NT") ||
            name.equals("Windows 2000") ||
            name.equals("Windows XP") ||
            name.equals("Windows Vista")) {
            return Constants.OS_OT_NT;
        }
        else if (name.equals("Mac OS")) {
            return Constants.OS_OT_MACOS;
        }
        else if (name.equals("Solaris")) {
            return Constants.OS_OT_SOLARIS;
        }
        else if (name.equals("HP UX")) {
            return Constants.OS_OT_HPUX;
        }
        else if (name.equals("VMS")) {
            return Constants.OS_OT_VMS;
        }
        else if (name.equals("Digital Unix")) {
            return Constants.OS_OT_OSF1;
        }
        else {
            //There is no java equivalent OT type for Irix, OS/2, Netware 4.11, Linux nor FreeBSD
            return 0;
        }
    }

    /**
     * Return the major version of the operating system
     * @return
     */
    public static int getOSVersion() {
      String version = System.getProperty("os.version");
      int result = 0;
      for (int i = 0; i < version.length(); i++) {
        if (!Character.isDigit(version.charAt(i))) {
          result = Integer.parseInt(version.substring(0, i));
          break;
        }
      }
      return result;
    }
    /**
   * Retrieve a constant representing the underlying Machine Architecture.
   *
   * No java os.arch found matching to the following constants
   * <li>OS_MA_MIPS 
   * <li>OS_MA_AVIION 
   * <li>OS_MA_AVIION_INTEL
   *
   * @return the int value for the OS_MA constants
   *
   * @author AD:24/12/2008
   */
    public static int getMachineArch() {
        String name = System.getProperty("os.arch");
        if (name.equals("sparc")) {
            return Constants.OS_MA_SPARC;
        }
        else if (name.equals("PowerPC") || name.equals("ppc")) {
            return Constants.OS_MA_MACINTOSH;
        }
        else if (name.equals("x86")) {
            return Constants.OS_MA_PC;
        }
        else if (name.equals("alpha")) {
            return Constants.OS_MA_ALPHA;
        }
        else if (name.startsWith("PA_RISC") || name.startsWith("PA-RISC")) {
            return Constants.OS_MA_PARISC;
        }
        else if (name.equals("02.10.00")) {
            return Constants.OS_MA_S390;
        }
        else if (name.equals("Sequent")) {
            return Constants.OS_MA_SEQUENT;
        }
        else if (name.toLowerCase().startsWith("vax")) {
            return Constants.OS_MA_VAX;
        }
        else if (name.toLowerCase().startsWith("power")) {
            return Constants.OS_MA_RS6000;
        }
        else {
          log.info("No matching constant for java machine architecture " + name);
            return 0;
        }
    }
   
    /**
     * Set the char value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
  public static void setChar(Field pField, Object pObject,
      byte pValue) {
        try {
            pField.setAccessible(true);
            pField.setChar(pObject, (char)pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
  }
    /**
     * Set the char value of the field on the passed object. This method will translate any
     * non-runtime exception into a runtime exception
     * @param pField
     * @param pObject
     * @param pValue
     */
  public static void setChar(Field pField, Object pObject,
      char pValue) {
        try {
            pField.setAccessible(true);
            pField.setChar(pObject, pValue);
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException(e.getMessage());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
  }
 
  /**
   * Return the current working volume from the JVM. If this method is executed on
   * a file system on which volumes don't make sense (eg Unix), this method will
   * return null
   * @return
   */
  public static TextData getWorkingVolume() {
    String userDir = System.getProperty("user.dir");
    int index = userDir.indexOf(File.VOLUME_SEPARATOR);
    if (index >= 0) {
      return new TextData(userDir.substring(0, index));
    }
    else {
      return null;
    }
  }

  /**
   * Execute the passed command in a new process, halting this process until the new process has terminated.
   * Any output or errors from the new process will be silently consumed.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(String command) {
    return runCommand(command, true, null, null, null, false);
  }
 
  /**
   * Execute the passed command in a new process, halting this process until the new process has terminated.
   * Any output or errors from the new process will be silently consumed.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(TextData command) {
    return runCommand(TextData.valueOf(command), true, null, null, null, false);
  }
 
  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(String command, boolean isSynchronous) {
    return runCommand(command, isSynchronous, null, null, null, false);
  }
 
  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(TextData command, boolean isSynchronous) {
    return runCommand(TextData.valueOf(command), isSynchronous, null, null, null, false);
  }
 
  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * The inputFile, outputFile and errorFile are the names of files on file system. If they don't exist then
   * the error will be logged to the FrameworkUtils logger, and the process continues.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * This method creates threads to consume the output and either write them to files or not, depending on whether
   * the passed file parameter is null or not.
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @param inputFile
   *       The file which the new process should use as input, or null if not required.
   * @param outputFile
   *       The file which the new process should write its standard output to, or null if the standard output
   *       is unimportant
   * @param errorFile
   *       The file which the new process should write its standard error to, or null if the standard error
   *       is unimportant
   * @param appendOutput
   *       If this is true and the output/error file exists, then the new output is appended to the file. Otherwise
   *       a new file is created.
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(TextData command, boolean isSynchronous, String inputFile,
      String outputFile, String errorFile, boolean appendOutput) {
    return runCommand(TextData.valueOf(command), isSynchronous, inputFile, outputFile, errorFile, appendOutput);
  }

  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * The inputFile, outputFile and errorFile are the names of files on file system. If they don't exist then
   * the error will be logged to the FrameworkUtils logger, and the process continues.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * This method creates threads to consume the output and either write them to files or not, depending on whether
   * the passed file parameter is null or not.
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @param inputFile
   *       The file which the new process should use as input, or null if not required.
   * @param outputFile
   *       The file which the new process should write its standard output to, or null if the standard output
   *       is unimportant
   * @param errorFile
   *       The file which the new process should write its standard error to, or null if the standard error
   *       is unimportant
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(TextData command, boolean isSynchronous, String inputFile,
      String outputFile, String errorFile) {
    return runCommand(TextData.valueOf(command), isSynchronous, inputFile, outputFile, errorFile, false);
  }

  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * The inputFile, outputFile and errorFile are the names of files on file system. If they don't exist then
   * the error will be logged to the FrameworkUtils logger, and the process continues.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * This method creates threads to consume the output and either write them to files or not, depending on whether
   * the passed file parameter is null or not.
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @param inputFile
   *       The file which the new process should use as input, or null if not required.
   * @param outputFile
   *       The file which the new process should write its standard output to, or null if the standard output
   *       is unimportant
   * @param errorFile
   *       The file which the new process should write its standard error to, or null if the standard error
   *       is unimportant
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(String command, boolean isSynchronous, String inputFile,
      String outputFile, String errorFile) {
    return runCommand(command, isSynchronous, inputFile, outputFile, errorFile, false);
  }

  /**
   * Execute the passed command in a new process. If isSynchronous is true, then this method will not
   * return until the launched process has termninated. Note that there is no guarantee that if isSynchronous
   * is set to false that the newly launched process will not have terminated when this method returns.
   * <p>
   * The inputFile, outputFile and errorFile are the names of files on file system. If they don't exist then
   * the error will be logged to the FrameworkUtils logger, and the process continues.
   * <p>
   * NB: This method provides some advantages over the standard Runtime.getRuntime().exec() (which is used
   * by this method). The exec method requires the standard output and standard error to be read if any characters
   * are written to them. Whilst this is not well documented (ok, it's undocumented) in the JDK it is important.
   * If the invoked process uses stdout or stderr at all, then it will be writing the output to a pipe and will
   * block the process once the buffer for that pipe is full. If nothing reads from the pipe, the process will
   * seem to freeze. If only small amounts of output are produced (less than one buffer side, for instance) then
   * the process will run normally, but will never terminate until the data on the outputs are consumed.<br>
   * <p>
   * This method creates threads to consume the output and either write them to files or not, depending on whether
   * the passed file parameter is null or not.
   * @param command
   *       The name of the command to process, including any arguments to the process. This will be passed to
   *       Runtime.exec(String), which will split them up to call Runtime.exec(String[]). Be warned that this
   *       method will split the commands into separate arguments delimited only by spaces, so a single argument
   *       can never contain spaces.
   * @param isSynchronous
   *       true if this process should wait for the new child process to terminate, false otherwsie. There is no
   *       guarantee that the child process will still be active when this method returns
   * @param inputFile
   *       The file which the new process should use as input, or null if not required.
   * @param outputFile
   *       The file which the new process should write its standard output to, or null if the standard output
   *       is unimportant
   * @param errorFile
   *       The file which the new process should write its standard error to, or null if the standard error
   *       is unimportant
   * @param appendOutput
   *       If this is true and the output/error file exists, then the new output is appended to the file. Otherwise
   *       a new file is created.
   * @return SP_OK if the process completes successfully, or SP_FAIL otherwise. (It would be more useful to return the
   *       actual process, but this integer is returned for backwards compatibility with Forte
   */
  public static int runCommand(String command, boolean isSynchronous, String inputFile,
      String outputFile, String errorFile, boolean appendOutput) {
   
    if (command == null || (command.length() == 0)) {
      UsageException err = new UsageException("Command not specified");
      ErrorMgr.addError(err);
      throw err;
    }
    try {
      if (log.isInfoEnabled()) {
        log.info("Creating new process: " + command);
        if (log.isDebugEnabled()) {
          log.debug(" - " + (inputFile == null ? "Not redirecting input" : "Input coming from " + inputFile));
          log.debug(" - Output going to" + (outputFile == null ? "stdout" : outputFile));
          log.debug(" - Errors going to" + (errorFile == null ? "stderr" : errorFile));
          log.debug(" - " + (appendOutput ? "Appending output" : "Not appending output"));
        }
      }
      Process p;
      if (workingDirectory == null) {
                p = Runtime.getRuntime().exec(command);
      } else {
          p = Runtime.getRuntime().exec(command, null, new java.io.File(workingDirectory));
      }

      // Start readers to consume output. It looks very innocuous with the output from the process
      // being mapped to p.inputStream, but this is correct.
      new Thread(new InputStreamHandler(p.getInputStream(), outputFile, appendOutput)).start();
      new Thread(new InputStreamHandler(p.getErrorStream(), errorFile, appendOutput)).start();
      if (inputFile != null) {
        new Thread(new OutputStreamHandler(p.getOutputStream(), inputFile)).start();
      }
      if (isSynchronous) {
        log.debug("Waiting for process to terminate");
        p.waitFor();
        log.debug("Process terminated.");
      }
      return Constants.SP_OK;
    }
    catch (IOException e) {
      // Forte help describes this as throwing an exception, but in practice it actually just
      // fails silently...
      // CraigM:21/01/2009 - Forte would throw an exception if the command was not found
      if (e.getMessage().indexOf("error=3") != -1) {
        UsageException err = new UsageException("Command not executed", e);
        ErrorMgr.addError(err);
        throw err;
      }
      return Constants.SP_FAIL;
    } catch (InterruptedException e) {
      // Interrupted waiting for this process to terminate, do nothing.
      return Constants.SP_FAIL;
    }
  }
 
  /**
   * This class is designed to redirect the contents of a file to a new processes standard input. Note
   * that the new process accepts the input, and hence gives us an output stream we write to in this
   * process, which will become an input stream (or similar) in the new child process.
   * @author Tim
   */
  public static class OutputStreamHandler implements Runnable {
    private OutputStream stream;
    private String fileName;
    public OutputStreamHandler(OutputStream stream, String fileName) {
      this.stream = stream;
      this.fileName = fileName;
    }
   
    public void run() {
      try {
                java.io.File lFile;
                if (workingDirectory == null) {
                    lFile = new java.io.File(fileName);
                } else {
                    lFile = new java.io.File(workingDirectory, fileName);
                }

                BufferedOutputStream bos = new BufferedOutputStream(stream);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(lFile));
        int c;
        while ((c = bis.read()) != -1) {
          bos.write(c);
        }
        bis.close();
        bos.close();
      }
      catch (IOException e) {
        log.error("Error redirecting input to new process from " + this.fileName, e);
      }
    }
  }

  /**
   * This class will redirect the output or error of a new process to a specified file. If no file is
   * required to hold the output/error, then this class will silently consume all output
   * @author Tim
   *
   */
  public static class InputStreamHandler implements Runnable {
    private InputStream stream;
    private boolean appendOutput;
    private String fileName;
    public InputStreamHandler(InputStream stream, String fileName, boolean appendOutput) {
      this.stream = stream;
      this.fileName = fileName;
      this.appendOutput = appendOutput;
    }
   
    public void run() {
      final int BUFFER_SIZE = 1024;
      // We use random access files in preference to streams as these allow us to append output
      // to the existing files, streams do not.
      RandomAccessFile file = null;
      byte[] buffer = null;
      int bufferBytes = 0;
      if (fileName != null && !(fileName.length() == 0)) {
        try {
                    java.io.File lFile;
                    if (workingDirectory == null) {
                        lFile = new java.io.File(fileName);
                    } else {
                        lFile = new java.io.File(workingDirectory, fileName);
                    }

                    if (!this.appendOutput) {
            if (lFile.canWrite()) {
                lFile.delete();
            }
          }
          file = new RandomAccessFile(lFile, "rw");
          if (this.appendOutput) {
            // If we're appending seek to the end of the file.
            file.seek(file.length());
          }
          buffer = new byte[BUFFER_SIZE];
          bufferBytes = 0;
        }
        catch (IOException e) {
          // If there are any exception in writing out the logs, we just ignore them and
          // continue, allowing the process to continue normally
          log.error("Error initialising output log for new process", e);
        }
      }
      BufferedInputStream bis = new BufferedInputStream(stream);
      boolean hasErrorBeenLogged = false;
      try {
        while ((bufferBytes = bis.read(buffer)) != -1) {
          try {
            file.write(buffer, 0, bufferBytes);
          } catch (IOException e) {
            // If we fail to write to the file, just log it and continue. We cannot throw
            // the exception here, otherwise the process will never have all it's output
            // read and hence fail to terminate.
            if (!hasErrorBeenLogged) {
              hasErrorBeenLogged = true;
              log.error("Error writing process output to file " + fileName, e);
            }
          }
        }
        bis.close();
        file.close();
      }
      catch (Exception e) {
        // Any other exceptions must have happened during reading or closing, in which case we're
        // done here anyway, so just terminate cleanly.
      }
    }
  }

}
TOP

Related Classes of net.helipilot50.stocktrade.framework.FrameworkUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.