com.google.idea.blaze.java.sync.jdeps.JdepsFileReader.java Source code

Java tutorial

Introduction

Here is the source code for com.google.idea.blaze.java.sync.jdeps.JdepsFileReader.java

Source

/*
 * Copyright 2016 The Bazel Authors. All rights reserved.
 *
 * 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.idea.blaze.java.sync.jdeps;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.base.async.FutureUtil;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.filecache.FileDiffer;
import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.prefetch.PrefetchService;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.Scope;
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.repackaged.devtools.build.lib.view.proto.Deps;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;

/** Reads jdeps from the ide info result. */
public class JdepsFileReader {
    private static final Logger LOG = Logger.getInstance(JdepsFileReader.class);

    static class JdepsState implements Serializable {
        private static final long serialVersionUID = 4L;
        private ImmutableMap<File, Long> fileState = null;
        private Map<File, TargetKey> fileToTargetMap = Maps.newHashMap();
        private Map<TargetKey, List<String>> targetToJdeps = Maps.newHashMap();
    }

    private static class Result {
        File file;
        TargetKey targetKey;
        List<String> dependencies;

        public Result(File file, TargetKey targetKey, List<String> dependencies) {
            this.file = file;
            this.targetKey = targetKey;
            this.dependencies = dependencies;
        }
    }

    /** Loads any updated jdeps files since the last invocation of this method. */
    @Nullable
    public JdepsMap loadJdepsFiles(Project project, BlazeContext parentContext,
            ArtifactLocationDecoder artifactLocationDecoder, Iterable<TargetIdeInfo> targetsToLoad,
            SyncState.Builder syncStateBuilder, @Nullable SyncState previousSyncState) {
        JdepsState oldState = previousSyncState != null ? previousSyncState.get(JdepsState.class) : null;
        JdepsState jdepsState = Scope.push(parentContext, (context) -> {
            context.push(new TimingScope("LoadJdepsFiles"));
            return doLoadJdepsFiles(project, context, artifactLocationDecoder, oldState, targetsToLoad);
        });
        if (jdepsState == null) {
            return null;
        }
        syncStateBuilder.put(JdepsState.class, jdepsState);
        return targetKey -> jdepsState.targetToJdeps.get(targetKey);
    }

    private JdepsState doLoadJdepsFiles(Project project, BlazeContext context,
            ArtifactLocationDecoder artifactLocationDecoder, @Nullable JdepsState oldState,
            Iterable<TargetIdeInfo> targetsToLoad) {
        JdepsState state = new JdepsState();
        if (oldState != null) {
            state.targetToJdeps = Maps.newHashMap(oldState.targetToJdeps);
            state.fileToTargetMap = Maps.newHashMap(oldState.fileToTargetMap);
        }

        Map<File, TargetKey> fileToTargetMap = Maps.newHashMap();
        for (TargetIdeInfo target : targetsToLoad) {
            assert target != null;
            JavaIdeInfo javaIdeInfo = target.javaIdeInfo;
            if (javaIdeInfo != null) {
                ArtifactLocation jdepsFile = javaIdeInfo.jdepsFile;
                if (jdepsFile != null) {
                    fileToTargetMap.put(artifactLocationDecoder.decode(jdepsFile), target.key);
                }
            }
        }

        List<File> updatedFiles = Lists.newArrayList();
        List<File> removedFiles = Lists.newArrayList();
        state.fileState = FileDiffer.updateFiles(oldState != null ? oldState.fileState : null,
                fileToTargetMap.keySet(), updatedFiles, removedFiles);

        ListenableFuture<?> fetchFuture = PrefetchService.getInstance().prefetchFiles(project, updatedFiles);
        if (!FutureUtil.waitForFuture(context, fetchFuture).timed("FetchJdeps")
                .withProgressMessage("Reading jdeps files...").run().success()) {
            return null;
        }

        for (File removedFile : removedFiles) {
            TargetKey targetKey = state.fileToTargetMap.remove(removedFile);
            if (targetKey != null) {
                state.targetToJdeps.remove(targetKey);
            }
        }

        AtomicLong totalSizeLoaded = new AtomicLong(0);

        List<ListenableFuture<Result>> futures = Lists.newArrayList();
        for (File updatedFile : updatedFiles) {
            futures.add(submit(() -> {
                totalSizeLoaded.addAndGet(updatedFile.length());
                try (InputStream inputStream = new FileInputStream(updatedFile)) {
                    Deps.Dependencies dependencies = Deps.Dependencies.parseFrom(inputStream);
                    if (dependencies != null) {
                        List<String> dependencyStringList = Lists.newArrayList();
                        for (Deps.Dependency dependency : dependencies.getDependencyList()) {
                            // We only want explicit or implicit deps that were
                            // actually resolved by the compiler, not ones that are
                            // available for use in the same package
                            if (dependency.getKind() == Deps.Dependency.Kind.EXPLICIT
                                    || dependency.getKind() == Deps.Dependency.Kind.IMPLICIT) {
                                dependencyStringList.add(dependency.getPath());
                            }
                        }
                        TargetKey targetKey = fileToTargetMap.get(updatedFile);
                        return new Result(updatedFile, targetKey, dependencyStringList);
                    }
                } catch (FileNotFoundException e) {
                    LOG.info("Could not open jdeps file: " + updatedFile);
                }
                return null;
            }));
        }
        try {
            for (Result result : Futures.allAsList(futures).get()) {
                if (result != null) {
                    state.fileToTargetMap.put(result.file, result.targetKey);
                    state.targetToJdeps.put(result.targetKey, result.dependencies);
                }
            }
            context.output(PrintOutput.log(String.format("Loaded %d jdeps files, total size %dkB",
                    updatedFiles.size(), totalSizeLoaded.get() / 1024)));
        } catch (InterruptedException | ExecutionException e) {
            LOG.error(e);
            return null;
        }
        return state;
    }

    private static <T> ListenableFuture<T> submit(Callable<T> callable) {
        return BlazeExecutor.getInstance().submit(callable);
    }
}