com.facebook.buck.rules.AbstractBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.rules.AbstractBuilder.java

Source

/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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 com.facebook.buck.rules;

import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.parser.BuildTargetParser;
import com.facebook.buck.util.ProjectFilesystem;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;

import java.lang.reflect.Field;
import java.nio.file.Paths;

/**
 * Support class for writing builders, which can create {@link Buildable} and {@link BuildRule} \
 * instances at test time. It does this by as closely as possible mirroring the behavior seen when
 * running the actual parser.
 *
 * @param <B> The subclass of {@link Buildable} which this builds.
 * @param <A> The type of the constructor arg returned by the Buildable's {@link Description}.
 */
public abstract class AbstractBuilder<B extends Buildable, A extends ConstructorArg> {

    private final Description<A> description;
    private final BuildTarget target;
    protected final A arg;

    protected AbstractBuilder(Description<A> description, BuildTarget target) {
        this.description = Preconditions.checkNotNull(description);
        this.target = Preconditions.checkNotNull(target);
        this.arg = description.createUnpopulatedConstructorArg();
        populateWithDefaultValues(this.arg, this.target);
    }

    @SuppressWarnings("unchecked")
    public final B build() {
        BuildRuleParams params = createBuildRuleParams();

        return (B) description.createBuildable(params, arg);
    }

    public final BuildRule build(BuildRuleResolver resolver) {
        // The BuildRule determines its deps by extracting them from the rule parameters.
        BuildRuleParams params = createBuildRuleParams();

        AbstractBuildable.AnonymousBuildRule rule = new AbstractBuildable.AnonymousBuildRule(
                description.getBuildRuleType(), build(), params);
        resolver.addToIndex(target, rule);
        return rule;
    }

    @SuppressWarnings("unchecked")
    private BuildRuleParams createBuildRuleParams() {
        // Not all rules have deps, but all rules call them deps. When they do, they're always optional.
        // Grab them in the unsafest way I know.
        try {
            Field depsField = arg.getClass().getField("deps");
            Object optional = depsField.get(arg);

            if (optional == null) {
                return new FakeBuildRuleParams(target);
            }
            // Here's a whole series of assumptions in one lump of a Bad Idea.
            ImmutableSortedSet<BuildRule> deps = (ImmutableSortedSet<BuildRule>) ((Optional<?>) optional).get();
            return new FakeBuildRuleParams(target, deps);
        } catch (ReflectiveOperationException ignored) {
            // Field doesn't exist: no deps.
            return new FakeBuildRuleParams(target);
        }
    }

    protected <C extends Comparable<?>> Optional<ImmutableSortedSet<C>> amend(
            Optional<ImmutableSortedSet<C>> existing, C instance) {
        ImmutableSortedSet.Builder<C> toReturn = ImmutableSortedSet.naturalOrder();
        if (existing != null && existing.isPresent()) {
            toReturn.addAll(existing.get());
        }
        toReturn.add(instance);
        return Optional.of(toReturn.build());
    }

    // Thanks to type erasure, this needs a unique name.
    protected <C extends Comparable<?>> Optional<ImmutableSet<C>> amendSet(Optional<ImmutableSet<C>> existing,
            C instance) {
        ImmutableSet.Builder<C> toReturn = ImmutableSet.builder();
        if (existing.isPresent()) {
            toReturn.addAll(existing.get());
        }
        toReturn.add(instance);
        return Optional.of(toReturn.build());
    }

    private void populateWithDefaultValues(A arg, BuildTarget target) {
        BuildRuleResolver resolver = new BuildRuleResolver(Maps.<BuildTarget, BuildRule>newHashMap());
        ProjectFilesystem filesystem = new ProjectFilesystem(Paths.get("."));
        BuildRuleFactoryParams factoryParams = NonCheckingBuildRuleFactoryParams
                .createNonCheckingBuildRuleFactoryParams(Maps.<String, Object>newHashMap(),
                        new BuildTargetParser(filesystem), target);
        new ConstructorArgMarshaller(Paths.get(".")).populate(resolver, filesystem, factoryParams, arg, true);
    }
}