/** * $Id: $ * $Date: $ * */ package org.xmlsh.util; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.XdmAtomicValue; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmValue; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.core.CoreException; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.XValue; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.Shell; public class JavaUtils { private static Set< String > mReserved; private static Logger mLogger = LogManager.getLogger() ; // Words that could not make valid class names so cant otherwise be used as commands or functions static { mReserved = new HashSet<String>(); mReserved.add( "class"); mReserved.add( "boolean" ); mReserved.add( "int" ); mReserved.add( "double"); mReserved.add( "true" ); mReserved.add( "false" ); mReserved.add( "long" ); mReserved.add( "char" ); mReserved.add( "null" ); mReserved.add( "float" ); mReserved.add( "byte" ); mReserved.add( "short" ); mReserved.add("package"); mReserved.add("new"); } public static boolean isReserved(String n ) { return mReserved.contains(n); } public static XValue newXValue(Class<?> cls, List<XValue> args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, CoreException { Constructor<?>[] constructors = cls.getConstructors(); return newXValue( cls , constructors , args ); } public static XValue newXValue(Class<?> cls, Constructor<?>[] constructors ,List<XValue> args) throws CoreException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Constructor<?> c = getBestMatch(args, constructors); if (c == null) throw new InvalidArgumentException("No construtor match found for: " + cls.getName() + "(" + getArgClassesString(args) + ")"); Object obj = c.newInstance(getArgs(c.getParameterTypes(), args)); return XValue.newXValue(null,obj); } public static XValue newXValue(String classname, List<XValue> args, ClassLoader classloader) throws Exception { Class<?> cls = findClass(classname, classloader); return newXValue( cls , args ); } public static Class<?> findClass(String classname, ClassLoader classloader) throws ClassNotFoundException { Class<?> cls = Class.forName(classname, true, classloader); return cls; } public static <T> T newObject( Class<T> cls , Object... args ) throws InvalidArgumentException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { @SuppressWarnings("unchecked") Constructor<T>[] constructors = (Constructor<T>[]) cls.getConstructors(); Constructor<T> c = getBestConstructor(constructors, args ); if( c == null ) throw new InvalidArgumentException("Cannot find constructor for: " + cls.getName()); return c.newInstance(args); } public static XValue callStatic(String classname, String methodName, List<XValue> args, ClassLoader classloader) throws Exception { Class<?> cls = findClass(classname, classloader); Method m = getBestMatch(cls,methodName, args, true); if (m == null) throw new InvalidArgumentException("No method match found for: " + classname + "." + methodName + "(" + getArgClassesString(args) + ")"); return callStaticMethod(m, args); } public static XValue callStaticMethod(Method m, List<XValue> args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, CoreException { Object obj = m.invoke(null, getArgs(m.getParameterTypes(), args)); // Special case for null - use formal return type to cast to right XValue type if( obj == null ){ if( String.class.isAssignableFrom(m.getReturnType()) ) return XValue.newXValue((String) null); else return XValue.newXValue(null,(Object)null); } return XValue.newXValue(null,obj); } public static String getArgClassesString(List<XValue> args) { StringBuffer sb = new StringBuffer(); for( XValue arg : args ){ if( sb.length() > 0 ) sb.append(","); sb.append( arg.asObject().getClass().getName() ); } return sb.toString(); } public static XValue callMethod(XValue instance, String methodName, List<XValue> args, ClassLoader classloader) throws Exception { Class<?> cls = instance.asObject().getClass(); Method m = getBestMatch(cls,methodName, args, false); if (m == null) throw new InvalidArgumentException("No method match found for: " + cls.getName() + "." + methodName); return callMethod(m, instance.asObject(), args); } public static XValue callMethod(Method m, Object instance, List<XValue> args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, CoreException { Object obj = m.invoke(instance, getArgs(m.getParameterTypes(), args)); // Special case for null - use formal return type to cast to right XValue type if( obj == null ){ if( String.class.isAssignableFrom(m.getReturnType()) ) return XValue.newXValue((String) null); else return XValue.newXValue( null , (Object) null); } return XValue.newXValue(null , obj) ; } public static Method getBestMatch(Class<?> cls, String methodName, List<XValue> args, boolean bStatic ) throws CoreException { return getBestMatch( cls.getMethods() , methodName , args , bStatic ); } public static Method getBestMatch(Method[] methods, String methodName, List<XValue> args, boolean bStatic ) throws CoreException { Method best = null; int bestConversions = 0; for (Method m : methods) { int conversions = 0; if (m.getName().equals(methodName)) { boolean isStatic = (m.getModifiers() & Modifier.STATIC) == Modifier.STATIC ; if( bStatic && ! isStatic ) continue ; Class<?>[] params = m.getParameterTypes(); if (params.length == args.size()) { int i = 0; for (XValue arg : args) { int conversion = arg.canConvert(params[i]); if( conversion < 0 ) break; i++; conversions += conversion ; } if (i == params.length){ if( best == null || conversions < bestConversions ){ best = m; bestConversions = conversions ; } } } } } return best; } public static Object[] getArgs(Class<?>[] params, List<XValue> args) throws CoreException { Object[] ret = new Object[params.length]; int i = 0; for (XValue arg : args) { ret[i] = arg.convert(params[i]); i++; } return ret; } public static Object[] convertObjects(Class<?>[] params, List<Object> args) throws CoreException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Object[] ret = new Object[params.length]; int i = 0; for (Object arg : args) { ret[i] = convert(arg , params[i]); i++; } return ret; } public static Constructor<?> getBestMatch(List<XValue> args, Constructor<?>[] constructors) throws CoreException { Constructor<?> best = null; int bestConversions = 0; // TODO how to choose best match for (Constructor<?> c : constructors) { Class<?>[] params = c.getParameterTypes(); if (params.length == args.size()) { int conversions = 0; int i = 0; for (XValue arg : args) { int convert = arg.canConvert(params[i]); if( convert < 0 ) break; conversions += convert; i++; } if (i == params.length){ // Find best match if( best == null || conversions < bestConversions ){ best = c ; bestConversions = conversions; } } } } return best; } public static <T> Constructor<T> getBestConstructor(Constructor<T>[] constructors, Object... argValues ) throws InvalidArgumentException { Constructor<T> best = null; int bestConversions = 0; // TODO how to choose best match for (Constructor<T> c : constructors) { Class<?>[] params = c.getParameterTypes(); if (params.length == argValues.length) { int conversions = 0; int i = 0; for (Object obj : argValues) { int convert = canConvertObject( obj , params[i] ); if( convert < 0 ) break; conversions += convert; i++; } if (i == params.length){ // Find best match if( best == null || conversions < bestConversions ){ best = c ; bestConversions = conversions; } } } } return best; } public static int hasBeanConstructor(Class<?> targetClass, Class<?> sourceClass) throws InvalidArgumentException { Constructor<?> c = getBeanConstructor( targetClass , sourceClass ); if( c == null ) return -1; if( c.getParameterTypes()[0].isAssignableFrom( sourceClass) ) return 3; else return 4; } // Does the target hava a single method constructor from a convertable source public static Constructor<?> getBeanConstructor(Class<?> targetClass, Class<?> sourceClass) throws InvalidArgumentException { try { Constructor<?> c = targetClass.getConstructor(sourceClass); if( c !=null ) return c ; } catch (NoSuchMethodException | SecurityException e) { mLogger.info("Exception getting constructors for: " + targetClass.getName(), e ); return null ; } Constructor<?>[] cs = targetClass.getConstructors(); for (Constructor<?> c : cs) { Class<?>[] params = c.getParameterTypes(); if (params.length == 1) { int convert = canConvertClass( sourceClass , params[0] ); if( convert >= 0 ) return c ; } } return null ; } static private final Class<?> mNumberWrappers[] = new Class<?>[] { Integer.class, Long.class, Byte.class, Short.class, Number.class, AtomicInteger.class, AtomicLong.class, BigDecimal.class, BigInteger.class, Double.class, Float.class }; public static boolean isWrappedOrPrimativeNumber( Class<?> c ) { if( c == null ) return false ; if( c.isPrimitive() && ! ( c == Void.TYPE || c == Boolean.TYPE ) ) return true ; for( Class<?> w : mNumberWrappers ) if( c.isAssignableFrom(w) ) return true ; return false ; } public static Class<?> fromPrimativeName( String name ) { switch( name ) { case "boolean" : return java.lang.Boolean.TYPE ; case "char" : return java.lang.Character.TYPE ; case "byte" : return java.lang.Byte.TYPE; case "short" : return java.lang.Short.TYPE; case "int" : return java.lang.Integer.TYPE; case "long" : return java.lang.Long.TYPE; case "float" : return java.lang.Float.TYPE; case "double" :return java.lang.Double.TYPE; case "void" : return java.lang.Void.TYPE; default : return null; } } public static <T> T convert(Object value, Class<T> targetClass) throws InvalidArgumentException { assert( targetClass != null ); assert( value != null ); if( targetClass.isInstance(value)) return targetClass.cast(value); Class<?> sourceClass = value.getClass(); // Asking for an XdmValue class if(XdmValue.class.isAssignableFrom(targetClass)) { if( value instanceof XdmNode ) return (T) (XdmValue) value ; if( targetClass.isAssignableFrom(XdmAtomicValue.class) ) { // Try some smart conversions of all types XdmAtomicValue knows if(value instanceof Boolean) return (T) new XdmAtomicValue(((Boolean) value).booleanValue()); else if(value instanceof Double) return (T) new XdmAtomicValue(((Double) value).doubleValue()); else if(value instanceof Float) return (T) new XdmAtomicValue(((Float) value).floatValue()); else if(value instanceof BigDecimal) return (T) new XdmAtomicValue((BigDecimal) value); else if(value instanceof BigDecimal) return (T) new XdmAtomicValue((BigDecimal) value); else if(value instanceof URI) return (T) new XdmAtomicValue((URI) value); else if(value instanceof Long) return (T) new XdmAtomicValue((Long) value); else if(value instanceof QName) return (T) new XdmAtomicValue((QName) value); // Still wanting an xdm value } if( isAtomic( value ) ) return (T) new XdmAtomicValue(value.toString()); } boolean bAtomic = isAtomic( value ); Class<?> vclass = value.getClass(); if( targetClass.isPrimitive() && bAtomic ){ /* Try to match non-primative types */ if( targetClass == Integer.TYPE ){ if( vclass == Long.class ) value = Integer.valueOf(((Long)value).intValue()); else if( vclass == Short.class ) value = Integer.valueOf( ((Short)value).intValue() ); else if( vclass== Byte.class ) value = Integer.valueOf( ((Byte)value).intValue() ); else value = Integer.valueOf( value.toString() ); } else if( targetClass == Long.TYPE ){ if(vclass == Integer.class ) value = Long.valueOf(((Integer)value).intValue()); else if( vclass == Short.class ) value = Long.valueOf( ((Short)value).intValue() ); else if( vclass == Byte.class ) value = Long.valueOf( ((Byte)value).intValue() ); value = Long.valueOf( value.toString() ); } else if( targetClass == Short.TYPE ){ if( vclass == Integer.class ) value = Short.valueOf((short)((Integer)value).intValue()); else if( vclass == Long.class ) value = Short.valueOf((short) ((Long)value).intValue() ); else if( vclass== Byte.class ) value = Short.valueOf((short) ((Byte)value).intValue() ); value = Short.valueOf( value.toString() ); } else if( targetClass == Byte.TYPE ){ if( vclass == Integer.class ) value = Byte.valueOf((byte)((Integer)value).intValue()); else if(vclass == Long.class ) value = Byte.valueOf((byte) ((Long)value).intValue() ); else if( vclass == Short.class ) value = Byte.valueOf((byte) ((Short)value).intValue() ); value = Byte.valueOf( value.toString() ); } else ; // skip return (T) value ; } // Bean constructor Constructor<?> cnst = getBeanConstructor( targetClass , sourceClass ) ; if( cnst != null ) try { return (T) cnst.newInstance( convert( value , cnst.getParameterTypes()[0] ) ); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { mLogger.debug("Exception converting argument for constructor",e); } // Cast through string String svalue = value.toString(); if( targetClass == Integer.class ) value = Integer.valueOf( svalue ); else if( targetClass == Long.class ) value = Long.valueOf( svalue ); else if( targetClass == Short.class ) value = Short.valueOf(svalue); else if( targetClass == Float.class ) value = Float.valueOf(svalue ); else if( targetClass == Double.class ) value = Double.valueOf(svalue); else value = null ; return (T) value ; } public static Object getFieldValue(String classname, XValue instance, String fieldName, ClassLoader classloader) throws InvalidArgumentException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException { Class<?> cls = findClass(classname, classloader); return getFieldValue( cls , instance == null ? null : instance.asObject() , fieldName , classloader ); } public static Object getFieldValue(Class<?> cls , Object instance, String fieldName, ClassLoader classloader) throws InvalidArgumentException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException { assert( cls != null ); assert( fieldName != null ); Field f = cls.getField(fieldName); if (f == null) throw new InvalidArgumentException("No field match found for: " + cls.getName() + "." + fieldName ); Object obj = f.get(instance == null ? null : instance); return obj ; } public static int canConvertClass( Class<?> sourceClass , Class<?> targetClass) throws InvalidArgumentException { assert( targetClass != null ); if( targetClass == null ) return -1; // Null case if( sourceClass == null ) { if( XdmValue.class.isAssignableFrom(targetClass) ) return 3; return -1; } // Equal class if( sourceClass.equals(targetClass)) return 0 ; // Directly assignable if( targetClass.isAssignableFrom(sourceClass)) return 1 ; boolean sourceNum = isWrappedOrPrimativeNumber(sourceClass) ; boolean targetNum = isWrappedOrPrimativeNumber(targetClass) ; // Boxable // int <-> Integer if(sourceNum && targetNum ) return 2 ; boolean soruceString = isStringClass(sourceClass); // TypeFamily conversion TO XdmValue if( XdmValue.class.isAssignableFrom(targetClass)) { if( XdmNode.class.isAssignableFrom(sourceClass ) ) return 1; if( XdmAtomicValue.class.isAssignableFrom(sourceClass) ) return 2; if( sourceNum || soruceString || isBooleanClass( sourceClass ) ) return 3 ; if( isAtomic( sourceClass ) ) return 3 ; } boolean targetString = isStringClass(targetClass); if( targetClass.isPrimitive() && soruceString ) // convert string to primative, yes return 4 ; if( targetString ) return 5 ; int c = hasBeanConstructor( targetClass , sourceClass ); if( c >= 0 ) return 5 + c ; return -1; } public static int canConvertObject( Object sourceObject , Class<?> targetClass) throws InvalidArgumentException { return canConvertClass( sourceObject.getClass() , targetClass ); } public static Class<?> convertToClass(XValue arg, Shell shell) throws CoreException { return convertToClass(arg, shell.getClassLoader(null)); } public static Class<?> convertToClass(XValue arg, ClassLoader classLoader) { if( arg.isAtomic() && arg.isString() ) try { return findClass(arg.toString(),classLoader); } catch (ClassNotFoundException e) { mLogger.catching( e ); return null; } Object obj = arg.asObject(); if( obj instanceof Class ) return (Class<?>) obj ; return obj.getClass(); } public static byte[] toByteArray(Object value, SerializeOpts opts) throws UnsupportedEncodingException { return value.toString().getBytes( opts.getOutput_text_encoding() ); } public static int getSize(Object obj) { if( obj == null ) if( obj instanceof Collection ) return ((Collection<?>)obj).size(); if( obj instanceof Array ) Array.getLength(obj); return 1; } public static boolean isNullClass(Class<?> cls) { return cls == null ; } public static boolean isContainer( Object obj) { return isContainerClass( obj.getClass()); } public static boolean isContainerClass(Class<?> cls) { if( cls == null ) return false; if( Collection.class.isAssignableFrom(cls) || Array.class.isAssignableFrom(cls) ) return true ; return false ; } public static boolean isArrayClass(Class<?> cls) { if( cls == null ) return false; if( List.class.isAssignableFrom(cls) || Array.class.isAssignableFrom(cls) ) return true ; return false ; } public static boolean isObjectClass(Class<?> cls) { if( cls == null ) return false; return ( ! isAtomicClass( cls ) ); } public static boolean isBooleanClass( Class<?> c ) { return c == Boolean.TYPE || Boolean.class.isAssignableFrom( c ) ; } //DAL: TODO: See 1.3 public static boolean isStringClass( Class<?> cls) { return String.class.isAssignableFrom(cls ) || CharSequence.class.isAssignableFrom(cls ); } public static boolean isAtomicClass(Class<?> cls) { return cls.isEnum() || isWrappedOrPrimativeNumber( cls ) || isStringClass( cls ) ; } public static boolean isClassClass(Class<?> cls) { return Class.class.isAssignableFrom(cls); } public static Object stringConcat(Object o, Object that) { return o.toString() + that.toString() ; } public static boolean isNumber(String v) { return toNumber(v,null) != null ; } // Is a or attomic collection type that is empty public static boolean isEmpty(Object value) { if( isAtomic( value ) ) return isAtomicEmpty(value); else return isObjectEmpty( value ); } public static boolean isObjectEmpty(Object value) { if( value instanceof Collection ) return ((Collection<?>)value).isEmpty() ; if( value instanceof Map) return ((Map<?, ?>)value).isEmpty() ; if( value.getClass().isArray() ) return Array.getLength(value) == 0 ; return false ; } private static boolean isAtomicEmpty(Object value) { return value == null || value.toString().isEmpty(); } private static boolean isAtomic(Object value) { return isAtomicClass( value.getClass() ); } public static void setNameIndexedValue( Object obj, String ind, Object object) throws NoSuchFieldException, SecurityException, InvalidArgumentException, IllegalArgumentException, IllegalAccessException { Field f = obj.getClass().getField(ind); if (f == null) throw new InvalidArgumentException("No field match found for: " + getClassName(obj) + "." + ind ); f.set(obj, object); } public static String getClassName(Object obj) { assert( obj != null ); if( obj == null ) return ""; return obj.getClass().getName(); } @SuppressWarnings("rawtypes") public static void setNamePositionalValue( Object obj, int index, Object object) throws InvalidArgumentException { if(obj.getClass().isArray() ) { Array.set( obj ,index, object); } else if( obj instanceof List) { ((List)obj).set( index, object); } else throw new InvalidArgumentException("No positional index for class: " + getClassName(obj) ); } public static boolean isArrayOf(Object value, Class<?> cls) { return value.getClass().isArray() && cls.isAssignableFrom(value.getClass().getComponentType()); } public static Object getIndexValue(Object obj, int index ) { Object res = null ; if(obj.getClass().isArray() ) { res = Array.get( obj , index); } else if( obj instanceof List) { res = ((List<?>)obj).get(index); } return res; } public static <T> List<T> getValues( Object obj ){ if(obj.getClass().isArray() ) { Object array = obj ; int len = ArrayUtils.getLength(array); ArrayList<T> list = new ArrayList<>(len); for( int i = 0 ;i < len ; i++ ) list.add( (T) Array.get(array, i)); return list ; } if( obj instanceof Collection ){ @SuppressWarnings({ "rawtypes", "unchecked" }) Collection<T> c = ((Collection)obj) ; ArrayList<T> list = new ArrayList<>( c.size() ); list.addAll( c ); return list; } return (List<T>) Collections.singletonList(obj); } public static boolean isCollection(Object obj) { return obj instanceof Collection; } public static String simpleTypeName(Object value) { if( value == null ) return "null"; Class<?> cls = value.getClass(); if( isStringClass( cls ) ) return "string" ; if( isArrayClass( cls ) ) return "array" ; if( isWrappedOrPrimativeNumber( cls ) ) return "number" ; return "object" ; } public static boolean hasKey(Object obj, String key) { if( obj instanceof Map ) return ((Map)obj).containsKey(key); return false ; } public static Object getNameIndexValue(Object obj, String ind) throws CoreException { if( obj instanceof Map ) return ((Map)obj).get(ind); try { return getFieldValue(obj.getClass(), obj, ind, null); } catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException | ClassNotFoundException e) { Util.wrapCoreException("Exception getting value from java class: " + obj.getClass().getName(), e); } return null; } public static boolean isList(Object obj) { return isArrayClass(obj.getClass()) || obj instanceof List ; } public static Package convertToPackage(XValue arg) { if( arg.isAtomic() && arg.isString() ) return Package.getPackage(arg.toString()); Object obj = arg.asObject(); if( obj instanceof Package) return (Package) arg.asObject(); return null ; } public static String convertToCamelCase(String name) { if (name.indexOf('-') < 0) return name; String parts[] = name.split("-"); if (parts.length == 1) return name; StringBuilder result = new StringBuilder(name.length()); for (String p : parts) { if (p.length() == 0) continue; if (result.length() == 0) result.append(p); else { result.append(Character.toUpperCase(p.charAt(0))); result.append(p.substring(1)); } } return result.toString(); } public static String convertFromCamelCase(String name) { StringBuilder result = new StringBuilder(name.length()); for( char c : name.toCharArray() ){ if( Character.isUpperCase(c) ){ result.append('-'); result.append( Character.toLowerCase(c)); } else result.append(c); } return result.toString(); } public static <T> List<T> uniqueList(List<T> list) { if( list == null ) return null ; Set<T> set = new HashSet<T>(list); if( set.size() == list.size() ) return list ; set.clear(); List<T> alist = new ArrayList<>( set.size()); for( T item : list ){ if( set.contains(item)) continue; set.add( item ); alist.add( item ); } return alist ; } public static Class<?> getContainedType(Object value) { Class<?> c = getClass( value ); return c.getComponentType(); } public static Class<?> getClass(Object value) { assert( value != null ); if( value instanceof Class ) return (Class<?>) value ; return value.getClass(); } public static boolean isContainerOf(Object obj, Class<?> c ) { assert( obj != null ); return isArrayOf( obj , c ) || isCollectionOf( obj , c ); } public static Number toNumber(String v, Number def) { try { if( Util.isInt(v, true)) return Long.parseLong(v); return Double.parseDouble(v); } catch (NumberFormatException e) { // silent return def; } } public static boolean isCollectionOf(Object obj, Class<?> c) { if( obj instanceof Collection ){ Collection col = (Collection<?>) obj; if( col.isEmpty()) return true ; // sure WTF return ClassUtils.isAssignable(col.iterator().next().getClass(), c ); } return false ; } public static String toGetterName(String f) { return "get" + toFirstUpperCase(f); } public static String toSetterName(String f) { return "set" + toFirstUpperCase(f); } protected static String toFirstUpperCase(String f) { return Character.toUpperCase(f.charAt(0)) + f.substring(1); } } // // //Copyright (C) 2008-2014 David A. Lee. // //The contents of this file are subject to the "Simplified BSD License" (the "License"); //you may not use this file except in compliance with the License. You may obtain a copy of the //License at http://www.opensource.org/licenses/bsd-license.php // //Software distributed under the License is distributed on an "AS IS" basis, //WITHOUT WARRANTY OF ANY KIND, either express or implied. //See the License for the specific language governing rights and limitations under the License. // //The Original Code is: all this file. // //The Initial Developer of the Original Code is David A. Lee // //Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. // //Contributor(s): none. //