All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codetaco.cli.impl.variables.VariableAssigner Maven / Gradle / Ivy

There is a newer version: 6.10.4
Show newest version
package com.codetaco.cli.impl.variables;

import com.codetaco.cli.CliException;
import com.codetaco.cli.impl.CmdLineImpl;
import com.codetaco.cli.impl.type.CmdLineCLA;
import com.codetaco.cli.impl.type.EnumCLA;
import com.codetaco.cli.impl.type.ICmdLineArg;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;

public class VariableAssigner implements IVariableAssigner {
    static private IVariableAssigner instance;

    static private void assign(Field field,
                               ICmdLineArg arg,
                               Object target)
      throws ParseException {
        if (arg.getVariable() == null) {
            return;
        }
        if (arg.getValue() == null) {
            return;
        }
        if (field == null) {
            return;
        }

        /*
         * Allows access to non-public fields.
         */
        field.setAccessible(true);

        if (!(arg instanceof CmdLineCLA) && arg.getFactoryMethodName() != null) {
            assignWithInstantiator(field, arg, target);
        } else {
            assignStandard(field, arg, target);
        }
    }

    /**
     * This value may not be appropriate for the list. But since this is at
     * runtime we can't make use of the generics definition they may have
     * provided on the field. If the value is not what they expect then they
     * need to provide a --factory set of methods.
     */
    private static void assignList(Field field,
                                   ICmdLineArg arg,
                                   Object target) throws IllegalAccessException {
        Collection alist = (Collection) field.get(target);

        if (alist == null) {
            alist = new ArrayList<>();
            field.set(target, alist);
        }
        for (int v = 0; v < arg.size(); v++) {
            alist.add(arg.getDelegateOrValue(v));
        }
    }

    static private void assignStandard(Field field,
                                       ICmdLineArg arg,
                                       Object target) throws ParseException {
        String errMsg = "expected: public "
                                + arg.getValue().getClass().getName()
                                + " "
                                + arg.getVariable()
                                + " on "
                                + target.getClass().getName();
        try {
            if (isStringArray(field)) {
                field.set(target, arg.getValueAsStringArray());
            } else if (isIntegerArray(field)) {
                field.set(target, arg.getValueAsIntegerArray());
            } else if (isintArray(field)) {
                field.set(target, arg.getValueAsintArray());
            } else if (isDoubleArray(field)) {
                field.set(target, arg.getValueAsDoubleArray());
            } else if (isdoubleArray(field)) {
                field.set(target, arg.getValueAsdoubleArray());
            } else if (isLongArray(field)) {
                field.set(target, arg.getValueAsLongArray());
            } else if (islongArray(field)) {
                field.set(target, arg.getValueAslongArray());
            } else if (isbyteArray(field)) {
                field.set(target, arg.getValueAsbyteArray());
            } else if (ischarArray(field)) {
                field.set(target, arg.getValueAscharArray());
            } else if (isfloatArray(field)) {
                field.set(target, arg.getValueAsfloatArray());
            } else if (isFloatArray(field)) {
                field.set(target, arg.getValueAsFloatArray());
            } else if (isPatternArray(field)) {
                field.set(target, arg.getValueAsPatternArray());
            } else if (isPattern(field)) {
                field.set(target, arg.getValueAsPattern());
            } else if (isDateTimeFormatterArray(field)) {
                field.set(target, arg.getValueAsDateTimeFormatterArray());
            } else if (isDateTimeFormatter(field)) {
                field.set(target, arg.getValueAsDateTimeFormatter());
            } else if (isSimpleDateFormatArray(field)) {
                field.set(target, arg.getValueAsSimpleDateFormatArray());
            } else if (isSimpleDateFormat(field)) {
                field.set(target, arg.getValueAsSimpleDateFormat());
            } else if (isEquationArray(field)) {
                field.set(target, arg.getValueAsEquationArray());
            } else if (isEquation(field)) {
                field.set(target, arg.getValueAsEquation());
            } else if (isDateArray(field)) {
                field.set(target, arg.getValueAsDateArray());
            } else if (isCalendarArray(field)) {
                field.set(target, arg.getValueAsCalendarArray());
            } else if (isLocalDateTimeArray(field)) {
                field.set(target, arg.getValueAsLocalDateTimeArray());
            } else if (isZonedDateTimeArray(field)) {
                field.set(target, arg.getValueAsZonedDateTimeArray());
            } else if (isLocalDateArray(field)) {
                field.set(target, arg.getValueAsLocalDateArray());
            } else if (isLocalTimeArray(field)) {
                field.set(target, arg.getValueAsLocalTimeArray());
            } else if (isByteArray(field)) {
                field.set(target, arg.getValueAsByteArray());
            } else if (isCharacterArray(field)) {
                field.set(target, arg.getValueAsCharacterArray());
            } else if (isFileArray(field)) {
                field.set(target, arg.getValueAsFileArray());
            } else if (isURLArray(field)) {
                field.set(target, arg.getValueAsURLArray());
            } else if (isEnum(field)) {
                field.set(target, arg.asEnum(field.getName(), field.getType().getEnumConstants()));
            } else if (isEnumArray(field)) {
                throw new ParseException("enum[] is not support at this time. Use String[] and @Arg(inEnum=\"fully qualified enum class name\")", -1);
//                field.set(target,
//                          arg.asEnumArray(field.getName(), field.getType().getComponentType().getEnumConstants()));
            } else if (isList(field)) {
                assignList(field, arg, target);
            } else {
                field.set(target, arg.getDelegateOrValue());
            }
        } catch (SecurityException e) {
            throw new ParseException("SecurityException: " + errMsg, -1);
        } catch (IllegalArgumentException e) {
            throw new ParseException(e.toString() + ": " + errMsg, -1);
        } catch (IllegalAccessException e) {
            throw new ParseException("IllegalAccessException: " + errMsg, -1);
        }

    }

    static private void assignWithInstantiator(Field field,
                                               ICmdLineArg arg,
                                               Object target) throws ParseException {
        Class clazz = null;
        Method method = null;
        String baseClassName;
        if (field.getType().getName().charAt(0) == '[') {
            baseClassName = field.getType().getName().substring(2, field.getType().getName().length() - 1);
        } else if (arg.getInstanceClass() != null) {
            baseClassName = arg.getInstanceClass();
        } else {
            baseClassName = field.getType().getName();
        }

        String errMsg = null;

        try {
            int methodPvt = arg.getFactoryMethodName().lastIndexOf('.');

            Class parmClass = arg.getValue(0).getClass();

            if (methodPvt < 0) {

                /*
                 * It is too bad that the generic info on a field is not public.
                 * So here we can't determine the generic type of a list. To addPattern
                 * multi value items to a list the --class parameter must be
                 * used.
                 */
                /*-
                if (field.getType().getPackage().getName().startsWith("java."))
                    throw new ParseException("Generics with a factory method must use --class", 0);
                */

                errMsg = "expected: public static "
                           + baseClassName
                           + " "
                           + arg.getFactoryMethodName()
                           + "("
                           + parmClass.getName()
                           + ") on "
                           + baseClassName;
                clazz = CmdLineImpl.ClassLoader.loadClass(baseClassName);
                method = clazz.getDeclaredMethod(arg.getFactoryMethodName(), parmClass);
            } else {
                errMsg = "expected: public static "
                           + arg.getFactoryMethodName().substring(0, methodPvt)
                           + " "
                           + arg.getFactoryMethodName().substring(methodPvt + 1)
                           + "("
                           + parmClass.getName()
                           + ") on "
                           + arg.getFactoryMethodName().substring(0, methodPvt);
                clazz = CmdLineImpl.ClassLoader.loadClass(arg.getFactoryMethodName().substring(0, methodPvt));
                method = clazz.getDeclaredMethod(arg.getFactoryMethodName().substring(methodPvt + 1), parmClass);
            }

            if (arg.isMultiple()) {
                if (field.getType().getName().charAt(0) == '[') {
                    for (int r = 0; r < arg.size(); r++) {
                        Object[] array = newArray(target, field);
                        array[array.length - 1] = method.invoke(null, arg.getValue(r));
                    }
                } else {
                    for (int r = 0; r < arg.size(); r++) {
                        ArrayList arrayList = newList(target, field);
                        arrayList.add(method.invoke(null, arg.getValue(r)));
                    }
                }
            } else {
                field.set(target, method.invoke(null, arg.getValue()));
            }

        } catch (InstantiationException e) {
            throw new ParseException("InstantiationException " + errMsg, -1);
        } catch (ClassNotFoundException e) {
            throw new ParseException("ClassNotFoundException " + errMsg, -1);
        } catch (InvocationTargetException e) {
            throw new ParseException("InvocationTargetException " + errMsg, -1);
        } catch (IllegalAccessException e) {
            throw new ParseException("IllegalAccessException " + errMsg, -1);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            throw new ParseException("IllegalArgumentException " + errMsg, -1);
        } catch (SecurityException e) {
            throw new ParseException("SecurityException " + errMsg, -1);
        } catch (NoSuchMethodException e) {
            throw new ParseException("NoSuchMethodException " + errMsg, -1);
        }

    }

    static private String factoryArgValue(ICmdLineArg arg) {
        try {
            if (arg == null) {
                return null;
            }
            if (!arg.isParsed()) {
                return null;
            }
            if (arg instanceof EnumCLA) {
                Class clazz = CmdLineImpl.ClassLoader.loadClass(arg.getInstanceClass());
                Object[] possibleConstants = clazz.getEnumConstants();
                return arg.asEnum((String) arg.getValue(), possibleConstants).toString();
            }
            return (String) arg.getValue();
        } catch (ClassNotFoundException | CliException e) {
            e.printStackTrace();
            return (String) arg.getValue();
        }
    }

    static public Field findFieldInAnyParentOrMyself(ICmdLineArg arg,
                                                     Class targetClass,
                                                     String errMsg) throws ParseException {
        Field field = null;
        try {
            field = targetClass.getDeclaredField(arg.getVariable());
        } catch (SecurityException e) {
            throw new ParseException("SecurityException " + errMsg, -1);
        } catch (NoSuchFieldException e) {
            if (targetClass.getSuperclass() == null) {
                throw new ParseException("NoSuchFieldException " + errMsg, -1);
            }
            /*
             * recursive from here
             */
            return findFieldInAnyParentOrMyself(arg, targetClass.getSuperclass(), errMsg);
        }
        return field;
    }

    static public IVariableAssigner getInstance() {
        if (instance == null) {
            instance = new VariableAssigner();
        }
        return instance;
    }

    private static boolean isbyteArray(Field field) {
        return "[B".equals(field.getType().getName());
    }

    private static boolean isByteArray(Field field) {
        return "[Ljava.lang.Byte;".equals(field.getType().getName());
    }

    private static boolean isCalendarArray(Field field) {
        return "[Ljava.util.Calendar;".equals(field.getType().getName());
    }

    private static boolean isCharacterArray(Field field) {
        return "[Ljava.lang.Character;".equals(field.getType().getName());
    }

    private static boolean ischarArray(Field field) {
        return "[C".equals(field.getType().getName());
    }

    private static boolean isDateArray(Field field) {
        return "[Ljava.util.Date;".equals(field.getType().getName());
    }

    private static boolean isDateTimeFormatter(Field field) {
        return "java.time.format.DateTimeFormatter".equals(field.getType().getName());
    }

    private static boolean isDateTimeFormatterArray(Field field) {
        return "[Ljava.time.format.DateTimeFormatter;".equals(field.getType().getName());
    }

    private static boolean isdoubleArray(Field field) {
        return "[D".equals(field.getType().getName());
    }

    private static boolean isDoubleArray(Field field) {
        return "[Ljava.lang.Double;".equals(field.getType().getName());
    }

    static private boolean isEnum(Field field) {
        return field.getType().isEnum();
    }

    static private boolean isEnumArray(Field field) {
        return field.getType().isArray() && field.getType().getComponentType().isEnum();
    }

    private static boolean isEquation(Field field) {
        return "com.codetaco.math.Equ".equals(field.getType().getName());
    }

    private static boolean isEquationArray(Field field) {
        return "[Lcom.codetaco.math.Equ;".equals(field.getType().getName());
    }

    private static boolean isFileArray(Field field) {
        return "[Ljava.io.File;".equals(field.getType().getName());
    }

    private static boolean isURLArray(Field field) {
        return "[Ljava.net.URL;".equals(field.getType().getName());
    }

    private static boolean isfloatArray(Field field) {
        return "[F".equals(field.getType().getName());
    }

    private static boolean isFloatArray(Field field) {
        return "[Ljava.lang.Float;".equals(field.getType().getName());
    }

    private static boolean isintArray(Field field) {
        return "[I".equals(field.getType().getName());
    }

    private static boolean isIntegerArray(Field field) {
        return "[Ljava.lang.Integer;".equals(field.getType().getName());
    }

    static private boolean isList(Field field) {
        Class[] interfaces = field.getType().getInterfaces();
        if (interfaces.length == 0) {
            return false;
        }
        for (Class iface : interfaces) {
            if (Collection.class.getName().equals(iface.getName())) {
                return true;
            }
        }
        return false;
    }

    private static boolean isLocalDateArray(Field field) {
        return "[Ljava.time.LocalDate;".equals(field.getType().getName());
    }

    private static boolean isLocalDateTimeArray(Field field) {
        return "[Ljava.time.LocalDateTime;".equals(field.getType().getName());
    }

    private static boolean isZonedDateTimeArray(Field field) {
        return "[Ljava.time.ZonedDateTime;".equals(field.getType().getName());
    }

    private static boolean isLocalTimeArray(Field field) {
        return "[Ljava.time.LocalTime;".equals(field.getType().getName());
    }

    private static boolean islongArray(Field field) {
        return "[J".equals(field.getType().getName());
    }

    private static boolean isLongArray(Field field) {
        return "[Ljava.lang.Long;".equals(field.getType().getName());
    }

    private static boolean isPattern(Field field) {
        return "java.util.regex.Pattern".equals(field.getType().getName());
    }

    private static boolean isPatternArray(Field field) {
        return "[Ljava.util.regex.Pattern;".equals(field.getType().getName());
    }

    private static boolean isSimpleDateFormat(Field field) {
        return "java.text.SimpleDateFormat".equals(field.getType().getName());
    }

    private static boolean isSimpleDateFormatArray(Field field) {
        return "[Ljava.text.SimpleDateFormat;".equals(field.getType().getName());
    }

    private static boolean isStringArray(Field field) {
        return "[Ljava.lang.String;".equals(field.getType().getName());
    }

    static private Object[] newArray(Object target,
                                     Field field)
      throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        Object[] oldinstance = (Object[]) field.get(target);
        int oldsize = 0;
        if (oldinstance != null) {
            oldsize = oldinstance.length;
        }

        Object[] arrayinstance = (Object[]) Array.newInstance(field.getType().getComponentType(), oldsize + 1);

        int i = 0;
        if (oldinstance != null) {
            for (; i < oldsize; i++) {
                arrayinstance[i] = oldinstance[i];
            }
        }
        field.set(target, arrayinstance);
        return arrayinstance;
    }

    static private Object newInstanceForGroup(CmdLineCLA group,
                                              Object target,
                                              Field field,
                                              String _baseClassName,
                                              ICmdLineArg factoryValueArg,
                                              boolean reusable)
      throws ClassNotFoundException,
               InstantiationException,
               IllegalAccessException,
               SecurityException,
               NoSuchMethodException,
               IllegalArgumentException,
               InvocationTargetException,
               ParseException {

        String baseClassName = _baseClassName;

        Object groupInstance;
        if (baseClassName == null) {
            if (group.getInstanceClass() != null) {
                baseClassName = group.getInstanceClass();
            } else {
                baseClassName = field.getType().getName();
            }
        }

        /*
         * Allow an instantiated instance variable to be used rather than
         * replaced.
         */
        if (reusable && field.get(target) != null) {
            Object value = field.get(target);
            if (!value.getClass().getName().equals(baseClassName)) {
                throw new ParseException("Error in instance creation for \"" + group.toString() + "\", "
                                           + value.getClass().getName() + " can not be reassigned to " + baseClassName, 0);
            }
            return value;
        }

        Class clazz;
        Method method;

        /*
         * Get the proper constructor and possible cli to create the
         * instance
         */
        if (group.getFactoryMethodName() != null) {
            int methodPvt = group.getFactoryMethodName().lastIndexOf('.');
            String factoryValue = factoryArgValue(factoryValueArg);
            if (methodPvt < 0) {
                clazz = CmdLineImpl.ClassLoader.loadClass(baseClassName);
                if (factoryValue == null) {
                    method = clazz.getDeclaredMethod(group.getFactoryMethodName());
                } else {
                    method = clazz.getDeclaredMethod(group.getFactoryMethodName(), String.class);
                }
            } else {
                clazz = CmdLineImpl.ClassLoader.loadClass(group.getFactoryMethodName().substring(0, methodPvt));
                if (factoryValue == null) {
                    method = clazz.getDeclaredMethod(group.getFactoryMethodName().substring(methodPvt + 1));
                } else {
                    method = clazz
                               .getDeclaredMethod(group.getFactoryMethodName().substring(methodPvt + 1), String.class);
                }
            }
            if (factoryValue == null) {
                groupInstance = method.invoke(clazz);
            } else {
                groupInstance = method.invoke(clazz, factoryValue);
            }

        } else {
            clazz = CmdLineImpl.ClassLoader.loadClass(baseClassName);
            groupInstance = clazz.newInstance();
        }
        return groupInstance;
    }

    static private ArrayList newList(Object target,
                                             Field field)
      throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ArrayList oldinstance = (ArrayList) field.get(target);

        if (oldinstance == null) {
            oldinstance = new ArrayList<>();
            field.set(target, oldinstance);
        }
        return oldinstance;
    }

    static public IVariableAssigner setInstance(IVariableAssigner newInstance) {
        IVariableAssigner previousAssigner = instance;
        instance = newInstance;
        return previousAssigner;
    }

    @Override
    public void assign(ICmdLineArg arg,
                       Object target) throws ParseException {
        if (arg == null) {
            return;
        }
        if (target == null) {
            return;
        }

        String errMsg = "expected: "
                                + arg.getValue().getClass().getName()
                                + " "
                                + arg.getVariable()
                                + " on "
                                + target.getClass().getName();

        Field field = findFieldInAnyParentOrMyself(arg, target.getClass(), errMsg);
        assign(field, arg, target);
    }

    @Override
    public Object newGroupVariable(CmdLineCLA group,
                                   Object target,
                                   ICmdLineArg factoryValueArg) throws ParseException {
        try {
            if (group.getVariable() == null) {
                return null;
            }
            if (target == null) {
                return null;
            }

            Field field = target.getClass().getDeclaredField(group.getVariable());
            /*
             * Allows access to non-public fields.
             */
            field.setAccessible(true);

            Object groupInstance = null;

            if (group.isMultiple()) {
                String baseClassName;
                if (group.getInstanceClass() != null) {
                    if (field.getType().getName().charAt(0) == '[') {
                        baseClassName = group.getInstanceClass();
                        Object[] array = newArray(target, field);
                        array[array.length - 1] = newInstanceForGroup(group, target, field, baseClassName,
                                                                      factoryValueArg, false);
                        groupInstance = array[array.length - 1];
                    } else {
                        ArrayList arrayList = newList(target, field);
                        groupInstance = newInstanceForGroup(group, target, field, null, factoryValueArg, false);
                        arrayList.add(groupInstance);
                    }
                } else if (field.getType().getName().charAt(0) == '[') {
                    baseClassName = field.getType().getName().substring(2, field.getType().getName().length() - 1);
                    Object[] array = newArray(target, field);
                    array[array.length - 1] = newInstanceForGroup(group, target, field, baseClassName, factoryValueArg,
                                                                  false);
                    groupInstance = array[array.length - 1];
                } else {
                    ArrayList arrayList = newList(target, field);
                    groupInstance = newInstanceForGroup(group, target, field, null, factoryValueArg, false);
                    arrayList.add(groupInstance);
                }
            } else {
                groupInstance = newInstanceForGroup(group, target, field, null, factoryValueArg, true);
                field.set(target, groupInstance);
            }
            return groupInstance;
        } catch (ClassNotFoundException e) {
            throw new ParseException("ClassNotFoundException (" + group.getVariable() + ")", -1);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ParseException("InstantiationException (" + group.getVariable() + ")", -1);
        } catch (IllegalAccessException e) {
            throw new ParseException("IllegalAccessException (" + group.getVariable() + ")", -1);
        } catch (SecurityException e) {
            throw new ParseException("SecurityException (" + group.getVariable() + ")", -1);
        } catch (NoSuchFieldException e) {
            throw new ParseException("NoSuchFieldException ("
                                       + target.getClass().getSimpleName()
                                       + " "
                                       + group.getVariable()
                                       + ")", -1);
        } catch (IllegalArgumentException e) {
            throw new ParseException("IllegalArgumentException (" + group.getVariable() + ")", -1);
        } catch (NoSuchMethodException e) {
            throw new ParseException("NoSuchMethodException ("
                                       + target.getClass().getSimpleName()
                                       + " "
                                       + group.getVariable()
                                       + " "
                                       + group.getFactoryMethodName()
                                       + ")", -1);
        } catch (InvocationTargetException e) {
            throw new ParseException("InvocationTargetException (" + group.getVariable() + ")", -1);
        }
    }
}