com.android.build.gradle.internal.ExtraModelInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.android.build.gradle.internal.ExtraModelInfo.java

Source

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.android.build.gradle.internal;

import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY;
import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED;
import static com.android.builder.model.AndroidProject.PROPERTY_INVOKED_FROM_IDE;
import static com.android.ide.common.blame.parser.JsonEncodedGradleMessageParser.STDOUT_ERROR_TAG;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.api.BaseVariant;
import com.android.build.gradle.internal.dsl.CoreBuildType;
import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.build.gradle.internal.model.ArtifactMetaDataImpl;
import com.android.build.gradle.internal.model.JavaArtifactImpl;
import com.android.build.gradle.internal.model.SyncIssueImpl;
import com.android.build.gradle.internal.model.SyncIssueKey;
import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer;
import com.android.builder.core.ErrorReporter;
import com.android.builder.model.AndroidArtifact;
import com.android.builder.model.ArtifactMetaData;
import com.android.builder.model.JavaArtifact;
import com.android.builder.model.SourceProvider;
import com.android.builder.model.SourceProviderContainer;
import com.android.builder.model.SyncIssue;
import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.MessageJsonSerializer;
import com.android.ide.common.blame.SourceFilePosition;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;

import java.io.File;
import java.util.Collection;
import java.util.Map;

/**
 * For storing additional model information.
 */
public class ExtraModelInfo extends ErrorReporter {

    @NonNull
    private final Project project;
    private final boolean isLibrary;

    @NonNull
    private final ErrorFormatMode errorFormatMode;

    private final Map<SyncIssueKey, SyncIssue> syncIssues = Maps.newHashMap();

    private final Map<String, ArtifactMetaData> extraArtifactMap = Maps.newHashMap();
    private final ListMultimap<String, AndroidArtifact> extraAndroidArtifacts = ArrayListMultimap.create();
    private final ListMultimap<String, JavaArtifact> extraJavaArtifacts = ArrayListMultimap.create();

    private final ListMultimap<String, SourceProviderContainer> extraBuildTypeSourceProviders = ArrayListMultimap
            .create();
    private final ListMultimap<String, SourceProviderContainer> extraProductFlavorSourceProviders = ArrayListMultimap
            .create();
    private final ListMultimap<String, SourceProviderContainer> extraMultiFlavorSourceProviders = ArrayListMultimap
            .create();

    @Nullable
    private final Gson mGson;

    public ExtraModelInfo(@NonNull Project project, boolean isLibrary) {
        super(computeModelQueryMode(project));
        this.project = project;
        this.isLibrary = isLibrary;
        errorFormatMode = computeErrorFormatMode(project);
        if (errorFormatMode == ErrorFormatMode.MACHINE_PARSABLE) {
            GsonBuilder gsonBuilder = new GsonBuilder();
            MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
            mGson = gsonBuilder.create();
        } else {
            mGson = null;
        }
    }

    public boolean isLibrary() {
        return isLibrary;
    }

    public Map<SyncIssueKey, SyncIssue> getSyncIssues() {
        return syncIssues;
    }

    @Override
    @NonNull
    public SyncIssue handleSyncError(@NonNull String data, int type, @NonNull String msg) {
        SyncIssue issue;
        switch (getMode()) {
        case STANDARD:
            if (!isDependencyIssue(type)) {
                throw new GradleException(msg);
            }
            // if it's a dependency issue we don't throw right away. we'll
            // throw during build instead.
            // but we do log.
            project.getLogger().warn("WARNING: " + msg);
            issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
            break;
        case IDE_LEGACY:
            // compat mode for the only issue supported before the addition of SyncIssue
            // in the model.
            if (type != SyncIssue.TYPE_UNRESOLVED_DEPENDENCY) {
                throw new GradleException(msg);
            }
            // intended fall-through
        case IDE:
            // new IDE, able to support SyncIssue.
            issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
            syncIssues.put(SyncIssueKey.from(issue), issue);
            break;
        default:
            throw new RuntimeException("Unknown SyncIssue type");
        }

        return issue;
    }

    private static boolean isDependencyIssue(int type) {
        switch (type) {
        case SyncIssue.TYPE_UNRESOLVED_DEPENDENCY:
        case SyncIssue.TYPE_DEPENDENCY_IS_APK:
        case SyncIssue.TYPE_DEPENDENCY_IS_APKLIB:
        case SyncIssue.TYPE_NON_JAR_LOCAL_DEP:
        case SyncIssue.TYPE_NON_JAR_PACKAGE_DEP:
        case SyncIssue.TYPE_NON_JAR_PROVIDED_DEP:
        case SyncIssue.TYPE_JAR_DEPEND_ON_AAR:
        case SyncIssue.TYPE_MISMATCH_DEP:
            return true;
        }

        return false;

    }

    @Override
    public void receiveMessage(@NonNull Message message) {
        StringBuilder errorStringBuilder = new StringBuilder();
        if (errorFormatMode == ErrorFormatMode.HUMAN_READABLE) {
            for (SourceFilePosition pos : message.getSourceFilePositions()) {
                errorStringBuilder.append(pos.toString());
                errorStringBuilder.append(' ');
            }
            if (errorStringBuilder.length() > 0) {
                errorStringBuilder.append(": ");
            }
            errorStringBuilder.append(message.getText()).append("\n");

        } else {
            //noinspection ConstantConditions mGson != null when errorFormatMode == MACHINE_PARSABLE
            errorStringBuilder.append(STDOUT_ERROR_TAG).append(mGson.toJson(message)).append("\n");
        }

        String messageString = errorStringBuilder.toString();

        switch (message.getKind()) {
        case ERROR:
            project.getLogger().error(messageString);
            break;
        case WARNING:
            project.getLogger().warn(messageString);
            break;
        case INFO:
            project.getLogger().info(messageString);
            break;
        case STATISTICS:
            project.getLogger().trace(messageString);
            break;
        case UNKNOWN:
            project.getLogger().debug(messageString);
            break;
        case SIMPLE:
            project.getLogger().info(messageString);
            break;
        }
    }

    public Collection<ArtifactMetaData> getExtraArtifacts() {
        return extraArtifactMap.values();
    }

    public Collection<AndroidArtifact> getExtraAndroidArtifacts(@NonNull String variantName) {
        return extraAndroidArtifacts.get(variantName);
    }

    public Collection<JavaArtifact> getExtraJavaArtifacts(@NonNull String variantName) {
        return extraJavaArtifacts.get(variantName);
    }

    public Collection<SourceProviderContainer> getExtraFlavorSourceProviders(@NonNull String flavorName) {
        return extraProductFlavorSourceProviders.get(flavorName);
    }

    public Collection<SourceProviderContainer> getExtraBuildTypeSourceProviders(@NonNull String buildTypeName) {
        return extraBuildTypeSourceProviders.get(buildTypeName);
    }

    public void registerArtifactType(@NonNull String name, boolean isTest, int artifactType) {

        if (extraArtifactMap.get(name) != null) {
            throw new IllegalArgumentException(String.format("Artifact with name %1$s already registered.", name));
        }

        extraArtifactMap.put(name, new ArtifactMetaDataImpl(name, isTest, artifactType));
    }

    public void registerBuildTypeSourceProvider(@NonNull String name, @NonNull CoreBuildType buildType,
            @NonNull SourceProvider sourceProvider) {
        if (extraArtifactMap.get(name) == null) {
            throw new IllegalArgumentException(String
                    .format("Artifact with name %1$s is not yet registered. Use registerArtifactType()", name));
        }

        extraBuildTypeSourceProviders.put(buildType.getName(),
                new DefaultSourceProviderContainer(name, sourceProvider));

    }

    public void registerProductFlavorSourceProvider(@NonNull String name, @NonNull CoreProductFlavor productFlavor,
            @NonNull SourceProvider sourceProvider) {
        if (extraArtifactMap.get(name) == null) {
            throw new IllegalArgumentException(String
                    .format("Artifact with name %1$s is not yet registered. Use registerArtifactType()", name));
        }

        extraProductFlavorSourceProviders.put(productFlavor.getName(),
                new DefaultSourceProviderContainer(name, sourceProvider));

    }

    public void registerMultiFlavorSourceProvider(@NonNull String name, @NonNull String flavorName,
            @NonNull SourceProvider sourceProvider) {
        if (extraArtifactMap.get(name) == null) {
            throw new IllegalArgumentException(String
                    .format("Artifact with name %1$s is not yet registered. Use registerArtifactType()", name));
        }

        extraMultiFlavorSourceProviders.put(flavorName, new DefaultSourceProviderContainer(name, sourceProvider));
    }

    public void registerJavaArtifact(@NonNull String name, @NonNull BaseVariant variant,
            @NonNull String assembleTaskName, @NonNull String javaCompileTaskName,
            @NonNull Collection<File> generatedSourceFolders, @NonNull Iterable<String> ideSetupTaskNames,
            @NonNull Configuration configuration, @NonNull File classesFolder, @NonNull File javaResourcesFolder,
            @Nullable SourceProvider sourceProvider) {
        ArtifactMetaData artifactMetaData = extraArtifactMap.get(name);
        if (artifactMetaData == null) {
            throw new IllegalArgumentException(String
                    .format("Artifact with name %1$s is not yet registered. Use registerArtifactType()", name));
        }
        if (artifactMetaData.getType() != ArtifactMetaData.TYPE_JAVA) {
            throw new IllegalArgumentException(String.format("Artifact with name %1$s is not of type JAVA", name));
        }

        JavaArtifact artifact = new JavaArtifactImpl(name, assembleTaskName, javaCompileTaskName, ideSetupTaskNames,
                generatedSourceFolders, classesFolder, javaResourcesFolder, null,
                new ConfigurationDependencies(configuration), sourceProvider, null);

        extraJavaArtifacts.put(variant.getName(), artifact);
    }

    /**
     * Returns whether we are just trying to build a model for the IDE instead of building. This
     * means we will attempt to resolve dependencies even if some are broken/unsupported to avoid
     * failing the import in the IDE.
     */
    private static EvaluationMode computeModelQueryMode(@NonNull Project project) {
        if (AndroidGradleOptions.buildModelOnlyAdvanced(project)) {
            return EvaluationMode.IDE;
        }

        if (AndroidGradleOptions.buildModelOnly(project)) {
            return EvaluationMode.IDE_LEGACY;
        }

        return EvaluationMode.STANDARD;
    }

    private static ErrorFormatMode computeErrorFormatMode(@NonNull Project project) {
        if (AndroidGradleOptions.invokedFromIde(project)) {
            return ErrorFormatMode.MACHINE_PARSABLE;
        } else {
            return ErrorFormatMode.HUMAN_READABLE;
        }
    }

    public enum ErrorFormatMode {
        MACHINE_PARSABLE, HUMAN_READABLE
    }
}