private AttributeSetter createAttributeSetter()

in src/main/org/apache/tools/ant/IntrospectionHelper.java [1038:1275]


    private AttributeSetter createAttributeSetter(final Method m,
                                                  final Class<?> arg,
                                                  final String attrName) {
        if (Optional.class.equals(arg)) {
            Type gpt = m.getGenericParameterTypes()[0];
            Class<?> payload = Object.class;
            if (gpt instanceof ParameterizedType ) {
                Type ata = ((ParameterizedType) gpt).getActualTypeArguments()[0];
                if (ata instanceof Class<?>) {
                    payload = (Class<?>) ata;
                } else if (ata instanceof ParameterizedType) {
                    payload = (Class<?>) ((ParameterizedType) ata).getRawType();
                }
            }
            final AttributeSetter wrapped = createAttributeSetter(m, payload, attrName);
            return new AttributeSetter(m, arg, Optional::empty) {
                @Override
                Optional<?> toTargetType(Project project, String value)
                    throws BuildException {
                    return Optional.ofNullable(wrapped.toTargetType(project, value));
                }
            };
        }
        if (OptionalInt.class.equals(arg)) {
            final AttributeSetter wrapped = createAttributeSetter(m, Integer.class, attrName);
            return new AttributeSetter(m, arg, OptionalInt::empty) {
                @Override
                OptionalInt toTargetType(Project project, String value)
                    throws BuildException {
                    return Optional.ofNullable((Integer) wrapped.toTargetType(project, value))
                        .map(OptionalInt::of).orElseGet(OptionalInt::empty);
                }
            };
        }
        if (OptionalLong.class.equals(arg)) {
            final AttributeSetter wrapped = createAttributeSetter(m, Long.class, attrName);
            return new AttributeSetter(m, arg, OptionalLong::empty) {
                @Override
                OptionalLong toTargetType(Project project, String value)
                    throws BuildException {
                    return Optional.ofNullable((Long) wrapped.toTargetType(project, value))
                        .map(OptionalLong::of).orElseGet(OptionalLong::empty);
                }
            };
        }
        if (OptionalDouble.class.equals(arg)) {
            final AttributeSetter wrapped = createAttributeSetter(m, Double.class, attrName);
            return new AttributeSetter(m, arg, OptionalDouble::empty) {
                @Override
                Object toTargetType(Project project, String value)
                    throws BuildException {
                    return Optional.ofNullable((Double) wrapped.toTargetType(project, value))
                        .map(OptionalDouble::of).orElseGet(OptionalDouble::empty);
                }
            };
        }
        // use wrappers for primitive classes, e.g. int and
        // Integer are treated identically
        final Class<?> reflectedArg = PRIMITIVE_TYPE_MAP.getOrDefault(arg, arg);

        // Object.class - it gets handled differently by AttributeSetter
        if (Object.class == reflectedArg) {
            return new AttributeSetter(m, arg) {
                @Override
                Object toTargetType(Project project, String value)
                    throws BuildException {
                    throw new BuildException(
                            "Internal ant problem - this should not get called");
                }
            };
        }
        // simplest case - setAttribute expects String
        if (String.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public String toTargetType(Project project, String t) {
                    return t;
                }
            };
        }
        // char and Character get special treatment - take the first character
        if (Character.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public Character toTargetType(Project project, String value) {
                    if (value.isEmpty()) {
                        throw new BuildException("The value \"\" is not a "
                                + "legal value for attribute \"" + attrName + "\"");
                    }
                    return Character.valueOf(value.charAt(0));
                }
            };
        }
        // boolean and Boolean get special treatment because we have a nice method in Project
        if (Boolean.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public Boolean toTargetType(Project project, String value) {
                    return Boolean.valueOf(Project.toBoolean(value));
                }
            };
        }
        // Class doesn't have a String constructor but a decent factory method
        if (Class.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public Class<?> toTargetType(Project project, String value) {
                    try {
                        return Class.forName(value);
                    } catch (ClassNotFoundException e) {
                        throw new BuildException(e);
                    }
                }
            };
        }
        // resolve relative paths through Project
        if (File.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                Object toTargetType(Project project, String value)
                    throws BuildException {
                    return project.resolveFile(value);
                }
            };
        }
        // resolve relative nio paths through Project
        if (java.nio.file.Path.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                Object toTargetType(Project project, String value)
                    throws BuildException {
                    return project.resolveFile(value).toPath();
                }
            };
        }
        // resolve Resources/FileProviders as FileResources relative to Project:
        if (Resource.class.equals(reflectedArg) || FileProvider.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                Object toTargetType(Project project, String value)
                    throws BuildException {
                    return new FileResource(project.resolveFile(value));
                }
            };
        }
        // EnumeratedAttributes have their own helper class
        if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public EnumeratedAttribute toTargetType(Project project, String value) {
                    EnumeratedAttribute ea;
                    try {
                        ea = (EnumeratedAttribute) reflectedArg.getDeclaredConstructor().newInstance();
                    } catch (InstantiationException | IllegalAccessException
                            | IllegalArgumentException | InvocationTargetException
                            | NoSuchMethodException | SecurityException e) {
                        throw BuildException.of(e);
                    }
                    ea.setValue(value);
                    return ea;
                }
            };
        }
        final AttributeSetter setter = getEnumSetter(reflectedArg, m, arg);
        if (setter != null) {
            return setter;
        }
        if (Long.class.equals(reflectedArg)) {
            return new AttributeSetter(m, arg) {
                @Override
                public Long toTargetType(Project project, String value) {
                    try {
                        return Long.valueOf(StringUtils.parseHumanSizes(value));
                    } catch (final NumberFormatException e) {
                        throw new BuildException(
                            String.format("Can't assign non-numeric value '%s' to attribute %s",
                                value, attrName));
                    } catch (final Exception e) {
                        throw new BuildException(e);
                    }
                }
            };
        }
        // worst case. look for a public String constructor and use it
        // also supports new Whatever(Project, String) as for Path or Reference
        // This is used (deliberately) for all primitives/wrappers other than
        // char, boolean, and long.
        boolean includeProject;
        Constructor<?> c;
        try {
            // First try with Project.
            c = reflectedArg.getConstructor(Project.class, String.class);
            includeProject = true;
        } catch (final NoSuchMethodException nme) {
            // OK, try without.
            try {
                c = reflectedArg.getConstructor(String.class);
                includeProject = false;
            } catch (final NoSuchMethodException nme2) {
                // Well, no matching constructor.
                return null;
            }
        }
        final boolean finalIncludeProject = includeProject;
        final Constructor<?> finalConstructor = c;

        return new AttributeSetter(m, arg) {
            @Override
            public Object toTargetType(Project project, String value) {
                try {
                    final Object[] args = finalIncludeProject
                            ? new Object[] {project, value} : new Object[] {value};

                    final Object attribute = finalConstructor.newInstance(args);
                    if (project != null) {
                        project.setProjectReference(attribute);
                    }
                    return attribute;
                } catch (final Exception e) {
                    Throwable thw = e;
                    while (true) {
                        if (thw instanceof IllegalArgumentException) {
                            throw new BuildException(String.format(
                                "Can't convert value '%s' to type %s, reason: %s with message '%s'",
                                value, reflectedArg, thw.getClass(), thw.getMessage()));
                        }
                        final Throwable _thw = thw;
                        Optional<Throwable> next = Optional.of(thw).map(Throwable::getCause).filter(t -> t != _thw);
                        if (!next.isPresent()) {
                            break;
                        }
                        thw = next.get();
                    }
                    throw BuildException.of(e);
                }
            }
        };
    }