org.prebake.core.GlobRelation.java Source code

Java tutorial

Introduction

Here is the source code for org.prebake.core.GlobRelation.java

Source

// Copyright 2010, Mike Samuel
//
// 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.prebake.core;

import org.prebake.js.JsonSerializable;
import org.prebake.js.JsonSink;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gxp.com.google.common.base.Objects;

/**
 * A parameterized relationship between sets of globs.
 *
 * @author Mike Samuel <mikesamuel@gmail.com>
 */
public final class GlobRelation {
    public final ImmutableGlobSet inputs;
    public final ImmutableGlobSet outputs;
    public final ImmutableMap<String, Param> parameters;

    /**
     * Meta-data about a named glob parameter,
     * e.g. x in {@code out/arch/*(x)/lib/}.
     */
    public static final class Param implements JsonSerializable {
        public final String name;
        public final @Nullable ImmutableSet<String> allowedValues;
        public final @Nullable String defaultValue;

        public Param(String name, @Nullable Iterable<? extends String> allowedValues,
                @Nullable String defaultValue) {
            this.name = name;
            this.allowedValues = allowedValues != null ? ImmutableSet.copyOf(allowedValues) : null;
            this.defaultValue = defaultValue;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || o.getClass() != getClass()) {
                return false;
            }
            Param that = (Param) o;
            return this.name.equals(that.name) && Objects.equal(this.allowedValues, that.allowedValues);
        }

        @Override
        public int hashCode() {
            return name.hashCode() + 31 * allowedValues.hashCode();
        }

        public void toJson(JsonSink sink) throws IOException {
            sink.write("{").writeValue("name").write(":").writeValue(name).write(",").writeValue("values")
                    .write(":").writeValue(allowedValues).write("}");
        }
    }

    /** The result of binding the free parameters in a glob relation. */
    public static final class Solution {
        public final ImmutableGlobSet inputs;
        public final ImmutableGlobSet outputs;
        public final ImmutableMap<String, String> parameterBindings;

        Solution(GlobSet inputs, GlobSet outputs, Map<String, String> bindings) {
            this.inputs = ImmutableGlobSet.copyOf(inputs);
            this.outputs = ImmutableGlobSet.copyOf(outputs);
            this.parameterBindings = ImmutableMap.copyOf(bindings);
        }
    }

    public GlobRelation(GlobSet inputs, GlobSet outputs) {
        this(inputs, outputs, ImmutableList.<Param>of());
    }

    public GlobRelation(GlobSet inputs, GlobSet outputs, Iterable<? extends Param> parameters) {
        this.inputs = ImmutableGlobSet.copyOf(inputs);
        this.outputs = ImmutableGlobSet.copyOf(outputs);
        Set<String> free = Sets.newHashSet();
        findFreeParameterNames(this.inputs, free);
        findFreeParameterNames(this.outputs, free);
        ImmutableMap.Builder<String, Param> b = ImmutableMap.builder();
        for (Param p : parameters) {
            free.remove(p.name);
            b.put(p.name, p);
        }
        for (String unspecified : free) {
            b.put(unspecified, new Param(unspecified, null, null));
        }
        this.parameters = b.build();
    }

    public @Nullable Solution solveForOutput(Path p) {
        Map<String, String> bindings = Maps.newLinkedHashMap();
        Iterator<Glob> matching = outputs.matching(p).iterator();
        if (!matching.hasNext()) {
            return null;
        }
        if (parameters.isEmpty()) {
            return new Solution(inputs, outputs, ImmutableMap.<String, String>of());
        }
        String pathStr = p.toString();
        do {
            Glob g = matching.next();
            if (!g.match(pathStr, bindings)) {
                return null;
            }
        } while (matching.hasNext());
        // Not all parameters matched.
        if (bindings.size() != parameters.size()) {
            return null;
        }
        return withParameterValues(bindings);
    }

    public @Nonnull Solution withParameterValues(Map<String, String> bindings) {
        ImmutableList.Builder<Glob> solvedInputs = ImmutableList.builder();
        ImmutableList.Builder<Glob> solvedOutputs = ImmutableList.builder();
        for (Glob g : inputs) {
            solvedInputs.add(g.subst(bindings));
        }
        for (Glob g : outputs) {
            solvedOutputs.add(g.subst(bindings));
        }
        return new Solution(ImmutableGlobSet.of(solvedInputs.build()), ImmutableGlobSet.of(solvedOutputs.build()),
                bindings);
    }

    public @Nullable ImmutableList<Solution> allPossibleSolutions() {
        ImmutableList<String> keys;
        ImmutableList<Set<String>> valueSets;
        {
            ImmutableList.Builder<String> kb = ImmutableList.builder();
            ImmutableList.Builder<Set<String>> vb = ImmutableList.builder();
            for (Param p : parameters.values()) {
                if (p.allowedValues == null) {
                    return null;
                }
                kb.add(p.name);
                vb.add(p.allowedValues);
            }
            keys = kb.build();
            valueSets = vb.build();
        }
        Map<String, String> bindings = Maps.newLinkedHashMap();
        ImmutableList.Builder<Solution> solutions = ImmutableList.builder();
        int n = keys.size();
        for (List<String> values : Sets.cartesianProduct(valueSets)) {
            for (int i = 0; i < n; ++i) {
                bindings.put(keys.get(i), values.get(i));
                solutions.add(withParameterValues(bindings));
            }
        }
        return solutions.build();
    }

    private static void findFreeParameterNames(ImmutableGlobSet gset, Collection<? super String> free) {
        for (Glob g : gset) {
            g.enumerateHoleNamesOnto(free);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || o.getClass() != getClass()) {
            return false;
        }
        if (this == o) {
            return true;
        }
        GlobRelation that = (GlobRelation) o;
        return this.inputs.equals(that.inputs) && this.outputs.equals(that.outputs)
                && this.parameters.equals(that.parameters);
    }

    @Override
    public int hashCode() {
        return inputs.hashCode() + 31 * (outputs.hashCode() + parameters.hashCode());
    }
}