org.apache.aurora.common.args.Args.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.aurora.common.args.Args.java

Source

/**
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.aurora.common.args;

import java.io.IOException;
import java.lang.reflect.Field;

import javax.annotation.Nullable;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import org.apache.aurora.common.args.apt.Configuration;
import org.apache.aurora.common.args.apt.Configuration.ArgInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.aurora.common.args.apt.Configuration.ConfigurationException;

/**
 * Utility that can load static {@literal @CmdLine} and {@literal @Positional} arg field info from
 * a configuration database or from explicitly listed containing classes or objects.
 */
public final class Args {
    private static final Logger LOG = LoggerFactory.getLogger(Args.class);

    @VisibleForTesting
    static final Function<ArgInfo, Optional<Field>> TO_FIELD = info -> {
        try {
            return Optional.of(Class.forName(info.className).getDeclaredField(info.fieldName));
        } catch (NoSuchFieldException e) {
            throw new ConfigurationException(e);
        } catch (ClassNotFoundException e) {
            throw new ConfigurationException(e);
        } catch (NoClassDefFoundError e) {
            // A compilation had this class available at the time the ArgInfo was deposited, but
            // the classes have been re-bundled with some subset including the class this ArgInfo
            // points to no longer available.  If the re-bundling is correct, then the arg truly is
            // not needed.
            LOG.debug("Not on current classpath, skipping {}", info);
            return Optional.absent();
        }
    };

    private static final Function<Field, OptionInfo<?>> TO_OPTION_INFO = field -> {
        @Nullable
        CmdLine cmdLine = field.getAnnotation(CmdLine.class);
        if (cmdLine == null) {
            throw new ConfigurationException("No @CmdLine Arg annotation for field " + field);
        }
        return OptionInfo.createFromField(field);
    };

    /**
     * An opaque container for all the positional and optional {@link Arg} metadata in-play for a
     * command line parse.
     */
    public static final class ArgsInfo {
        private final Configuration configuration;
        private final ImmutableList<? extends OptionInfo<?>> optionInfos;

        ArgsInfo(Configuration configuration, Iterable<? extends OptionInfo<?>> optionInfos) {
            this.configuration = Preconditions.checkNotNull(configuration);
            this.optionInfos = ImmutableList.copyOf(optionInfos);
        }

        Configuration getConfiguration() {
            return configuration;
        }

        ImmutableList<? extends OptionInfo<?>> getOptionInfos() {
            return optionInfos;
        }
    }

    /**
     * Hydrates configured {@literal @CmdLine} arg fields and selects a desired set with the supplied
     * {@code filter}.
     *
     * @param configuration The configuration to find candidate {@literal @CmdLine} arg fields in.
     * @param filter A predicate to select fields with.
     * @return The desired hydrated {@literal @CmdLine} arg fields and optional {@literal @Positional}
     *     arg field.
     */
    static ArgsInfo fromConfiguration(Configuration configuration, Predicate<Field> filter) {
        Iterable<? extends OptionInfo<?>> optionInfos = Iterables
                .transform(filterFields(configuration.optionInfo(), filter), TO_OPTION_INFO);

        return new ArgsInfo(configuration, optionInfos);
    }

    private static Iterable<Field> filterFields(Iterable<ArgInfo> infos, Predicate<Field> filter) {
        return Iterables.filter(Optional.presentInstances(Iterables.transform(infos, TO_FIELD)), filter);
    }

    /**
     * Equivalent to calling {@code from(Predicates.alwaysTrue(), Arrays.asList(sources)}.
     */
    public static ArgsInfo from(Object... sources) throws IOException {
        return from(ImmutableList.copyOf(sources));
    }

    /**
     * Equivalent to calling {@code from(filter, Arrays.asList(sources)}.
     */
    public static ArgsInfo from(Predicate<Field> filter, Object... sources) throws IOException {
        return from(filter, ImmutableList.copyOf(sources));
    }

    /**
     * Equivalent to calling {@code from(Predicates.alwaysTrue(), sources}.
     */
    public static ArgsInfo from(Iterable<?> sources) throws IOException {
        return from(Predicates.<Field>alwaysTrue(), sources);
    }

    /**
     * Loads arg info from the given sources in addition to the default compile-time configuration.
     *
     * @param filter A predicate to select fields with.
     * @param sources Classes or object instances to scan for {@link Arg} fields.
     * @return The args info describing all discovered {@link Arg args}.
     * @throws IOException If there was a problem loading the default Args configuration.
     */
    public static ArgsInfo from(Predicate<Field> filter, Iterable<?> sources) throws IOException {
        Preconditions.checkNotNull(filter);
        Preconditions.checkNotNull(sources);

        Configuration configuration = Configuration.load();
        ArgsInfo staticInfo = Args.fromConfiguration(configuration, filter);

        final ImmutableSet.Builder<OptionInfo<?>> optionInfos = ImmutableSet.<OptionInfo<?>>builder()
                .addAll(staticInfo.getOptionInfos());

        for (Object source : sources) {
            Class<?> clazz = source instanceof Class ? (Class) source : source.getClass();
            for (Field field : clazz.getDeclaredFields()) {
                if (filter.apply(field) && field.isAnnotationPresent(CmdLine.class)) {
                    optionInfos.add(OptionInfo.createFromField(field, source));
                }
            }
        }

        return new ArgsInfo(configuration, optionInfos.build());
    }

    private Args() {
        // utility
    }
}