com.google.devtools.moe.client.project.ProjectContextFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.moe.client.project.ProjectContextFactory.java

Source

/*
 * Copyright (c) 2011 Google, 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.google.devtools.moe.client.project;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.moe.client.CommandRunner;
import com.google.devtools.moe.client.FileSystem;
import com.google.devtools.moe.client.Ui;
import com.google.devtools.moe.client.editors.Editor;
import com.google.devtools.moe.client.editors.ForwardTranslator;
import com.google.devtools.moe.client.editors.IdentityEditor;
import com.google.devtools.moe.client.editors.InverseEditor;
import com.google.devtools.moe.client.editors.InverseRenamingEditor;
import com.google.devtools.moe.client.editors.InverseScrubbingEditor;
import com.google.devtools.moe.client.editors.InverseTranslator;
import com.google.devtools.moe.client.editors.InverseTranslatorStep;
import com.google.devtools.moe.client.editors.PatchingEditor;
import com.google.devtools.moe.client.editors.RenamingEditor;
import com.google.devtools.moe.client.editors.ScrubbingEditor;
import com.google.devtools.moe.client.editors.ShellEditor;
import com.google.devtools.moe.client.editors.Translator;
import com.google.devtools.moe.client.editors.TranslatorPath;
import com.google.devtools.moe.client.editors.TranslatorStep;
import com.google.devtools.moe.client.migrations.MigrationConfig;
import com.google.devtools.moe.client.repositories.Repositories;
import com.google.devtools.moe.client.repositories.RepositoryType;
import com.google.devtools.moe.client.tools.FileDifference.FileDiffer;

import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * Creates a {@link ProjectContext} given a context file name.
 */
// TODO(cgruber): Move most of the create logic to ProjectConfig, since they're basically accessors
public abstract class ProjectContextFactory {

    private final FileDiffer differ;
    private final CommandRunner cmd;
    private final FileSystem filesystem;
    protected final Ui ui;
    private final Repositories repositories;

    public ProjectContextFactory(FileDiffer differ, CommandRunner cmd, @Nullable FileSystem filesystem, Ui ui,
            Repositories repositories) {
        // TODO(cgruber):push nullability back from this point.
        this.differ = differ;
        this.repositories = Preconditions.checkNotNull(repositories);
        this.cmd = cmd;
        this.filesystem = filesystem;
        this.ui = ui;
    }

    /**
     * Makes a ProjectContext for this config filename.
     *
     * @param configFilename the name of the holding the config
     *
     * @return the ProjectContext to be used
     */
    public final ProjectContext create(String configFilename) throws InvalidProject {
        ProjectConfig config = loadConfiguration(configFilename);
        return new AutoValue_ProjectContext(config, buildRepositories(config), buildEditors(config),
                buildTranslators(config), buildMigrationConfigs(config));
    }

    /**
     * Supplies a ProjectConfig file given a file-name.
     *
     * <p>Implementors might, for example, include factories that load project configs from
     * a filesystem, or from in-memory strings.
     */
    protected abstract ProjectConfig loadConfiguration(String configFilename) throws InvalidProject;

    //TODO(cgruber): Consider making InvalidProject an unchecked exception to eliminate these loops.
    private ImmutableMap<String, RepositoryType> buildRepositories(ProjectConfig config) throws InvalidProject {
        ImmutableMap.Builder<String, RepositoryType> builder = ImmutableMap.builder();

        for (Map.Entry<String, RepositoryConfig> entry : config.repositories().entrySet()) {
            builder.put(entry.getKey(), repositories.create(entry.getKey(), entry.getValue()));
        }
        return builder.build();
    }

    private ImmutableMap<String, Editor> buildEditors(ProjectConfig config) throws InvalidProject {
        ImmutableMap.Builder<String, Editor> builder = ImmutableMap.builder();
        for (Map.Entry<String, EditorConfig> entry : config.editors().entrySet()) {
            builder.put(entry.getKey(), makeEditorFromConfig(entry.getKey(), entry.getValue()));
        }
        return builder.build();
    }

    private ImmutableMap<TranslatorPath, Translator> buildTranslators(ProjectConfig config) throws InvalidProject {
        ImmutableMap.Builder<TranslatorPath, Translator> builder = ImmutableMap.builder();
        for (TranslatorConfig translatorConfig : config.translators()) {
            Translator t = makeTranslatorFromConfig(translatorConfig, config);

            TranslatorPath tPath = new TranslatorPath(translatorConfig.getFromProjectSpace(),
                    translatorConfig.getToProjectSpace());
            builder.put(tPath, t);
        }
        return builder.build();
    }

    private ImmutableMap<String, MigrationConfig> buildMigrationConfigs(ProjectConfig config) {
        ImmutableMap.Builder<String, MigrationConfig> builder = ImmutableMap.builder();
        for (MigrationConfig migrationConfig : config.migrations()) {
            builder.put(migrationConfig.getName(), migrationConfig);
        }
        return builder.build();
    }

    Editor makeEditorFromConfig(String editorName, EditorConfig config) throws InvalidProject {
        switch (config.type()) {
        case identity:
            return IdentityEditor.makeIdentityEditor(editorName, config);
        case scrubber:
            return ScrubbingEditor.makeScrubbingEditor(editorName, config);
        case patcher:
            return PatchingEditor.makePatchingEditor(editorName, config);
        case shell:
            return ShellEditor.makeShellEditor(editorName, config);
        case renamer:
            return RenamingEditor.makeRenamingEditor(editorName, config);
        default:
            throw new InvalidProject("Invalid editor type: \"%s\"", config.type());
        }
    }

    Translator makeTranslatorFromConfig(TranslatorConfig transConfig, ProjectConfig projConfig)
            throws InvalidProject {
        if (transConfig.isInverse()) {
            TranslatorConfig otherTrans = findInverseTranslatorConfig(transConfig, projConfig);
            return new InverseTranslator(makeStepsFromConfigs(otherTrans.getSteps()),
                    makeInverseStepsFromConfigs(otherTrans.getSteps()));
        } else {
            return new ForwardTranslator(makeStepsFromConfigs(transConfig.getSteps()));
        }
    }

    private List<TranslatorStep> makeStepsFromConfigs(List<StepConfig> stepConfigs) throws InvalidProject {
        ImmutableList.Builder<TranslatorStep> steps = ImmutableList.builder();
        for (StepConfig sc : stepConfigs) {
            steps.add(new TranslatorStep(sc.getName(), makeEditorFromConfig(sc.getName(), sc.getEditorConfig())));
        }
        return steps.build();
    }

    private List<InverseTranslatorStep> makeInverseStepsFromConfigs(List<StepConfig> stepConfigs)
            throws InvalidProject {
        ImmutableList.Builder<InverseTranslatorStep> inverseSteps = ImmutableList.builder();
        for (StepConfig sc : Lists.reverse(stepConfigs)) {
            inverseSteps.add(new InverseTranslatorStep("inverse_" + sc.getName(),
                    makeInverseEditorFromConfig("inverse_" + sc.getName(), sc.getEditorConfig())));
        }
        return inverseSteps.build();
    }

    private TranslatorConfig findInverseTranslatorConfig(TranslatorConfig transConfig, ProjectConfig projConfig)
            throws InvalidProject {
        List<TranslatorConfig> otherTranslators = projConfig.translators();
        for (TranslatorConfig otherTrans : otherTranslators) {
            if (otherTrans.getToProjectSpace().equals(transConfig.getFromProjectSpace())
                    && otherTrans.getFromProjectSpace().equals(transConfig.getToProjectSpace())) {
                if (otherTrans.isInverse()) {
                    throw new InvalidProject("Can't have mutually inverse translators!");
                }
                return otherTrans;
            }
        }
        throw new InvalidProject("Couldn't find translator whose path is inverse of %s -> %s",
                transConfig.getFromProjectSpace(), transConfig.getToProjectSpace());
    }

    private InverseEditor makeInverseEditorFromConfig(String editorName, EditorConfig originalConfig)
            throws InvalidProject {
        switch (originalConfig.type()) {
        case identity:
            return IdentityEditor.makeIdentityEditor(editorName, originalConfig);
        case renamer:
            return InverseRenamingEditor.makeInverseRenamingEditor(editorName, originalConfig);
        case scrubber:
            return new InverseScrubbingEditor(differ, cmd, filesystem, ui);
        default:
            throw new InvalidProject("Non-invertible editor type: " + originalConfig.type());
        }
    }
}