Java tutorial
/* * 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.android.sync.importer.problems; import com.android.SdkConstants; import com.android.ide.common.resources.configuration.FolderConfiguration; import com.android.ide.common.resources.configuration.LocaleQualifier; import com.android.ide.common.resources.configuration.ResourceQualifier; import com.android.resources.ResourceFolderType; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSortedMap; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.idea.blaze.base.ideinfo.ArtifactLocation; import com.google.idea.blaze.base.io.FileAttributeProvider; import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder; import com.intellij.openapi.diagnostic.Logger; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; /** * Classifies generated resource directories as "interesting" or not. * * <p>Uninteresting resources: It is very common to have string translations as the only generated * resources for a rule. If that's the case, we can ignore them since we likely already have a * source variant of the same resource as a baseline for resolving references in Java or XML. * * <p>Other generated resources are interesting. */ class GeneratedResourceClassifier { private static final Logger logger = Logger.getInstance(GeneratedResourceClassifier.class); private final ImmutableSortedMap<ArtifactLocation, Integer> interestingDirectories; GeneratedResourceClassifier(Collection<ArtifactLocation> generatedResourceLocations, ArtifactLocationDecoder artifactLocationDecoder, ListeningExecutorService executorService) { FileAttributeProvider fileAttributeProvider = FileAttributeProvider.getInstance(); List<ListenableFuture<GenResourceClassification>> jobs = generatedResourceLocations.stream() .map(location -> executorService .submit(() -> classifyLocation(location, artifactLocationDecoder, fileAttributeProvider))) .collect(Collectors.toList()); ImmutableSortedMap.Builder<ArtifactLocation, Integer> interesting = ImmutableSortedMap.naturalOrder(); try { for (GenResourceClassification classification : Futures.allAsList(jobs).get()) { if (classification.isInteresting) { interesting.put(classification.artifactLocation, classification.numSubDirs); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); interesting = considerAllInteresting(generatedResourceLocations); } catch (ExecutionException e) { logger.error(e); interesting = considerAllInteresting(generatedResourceLocations); } finally { interestingDirectories = interesting.build(); } } /** * Returns a collection of interesting directories as a sorted map from directory to estimated * size of subdirectory (to estimate cost of indexing). * * @return map of interesting directories and associated sizes */ public ImmutableSortedMap<ArtifactLocation, Integer> getInterestingDirectories() { return interestingDirectories; } private static ImmutableSortedMap.Builder<ArtifactLocation, Integer> considerAllInteresting( Collection<ArtifactLocation> generatedResourceLocations) { ImmutableSortedMap.Builder<ArtifactLocation, Integer> builder = ImmutableSortedMap.naturalOrder(); for (ArtifactLocation location : generatedResourceLocations) { builder.put(location, -1); } return builder; } private static class GenResourceClassification { final boolean isInteresting; final ArtifactLocation artifactLocation; final int numSubDirs; GenResourceClassification(boolean isInteresting, ArtifactLocation artifactLocation, int numSubDirs) { this.isInteresting = isInteresting; this.artifactLocation = artifactLocation; this.numSubDirs = numSubDirs; } static GenResourceClassification uninteresting(ArtifactLocation artifactLocation, int numSubDirs) { return new GenResourceClassification(false, artifactLocation, numSubDirs); } static GenResourceClassification interesting(ArtifactLocation artifactLocation, int numSubDirs) { return new GenResourceClassification(true, artifactLocation, numSubDirs); } } private static GenResourceClassification classifyLocation(ArtifactLocation artifactLocation, ArtifactLocationDecoder artifactLocationDecoder, FileAttributeProvider fileAttributeProvider) { File resDirectory = artifactLocationDecoder.decode(artifactLocation); File[] children = fileAttributeProvider.listFiles(resDirectory); if (children == null) { return GenResourceClassification.uninteresting(artifactLocation, 0); } if (mayHaveNonStringTranslations(children)) { return GenResourceClassification.interesting(artifactLocation, children.length); } else { return GenResourceClassification.uninteresting(artifactLocation, children.length); } } @VisibleForTesting static boolean mayHaveNonStringTranslations(File[] resDirectoryChildren) { return Arrays.stream(resDirectoryChildren).anyMatch(child -> mayHaveNonStringTranslations(child.getName())); } private static boolean mayHaveNonStringTranslations(String dirName) { // String translations only sit in the values-xx-rYY directories, so we can rule out other // directories quickly. if (!dirName.contains(SdkConstants.RES_QUALIFIER_SEP)) { return true; } if (ResourceFolderType.getFolderType(dirName) != ResourceFolderType.VALUES) { return true; } FolderConfiguration config = FolderConfiguration.getConfigForFolder(dirName); // Conservatively say it's interesting if there is an unrecognized configuration. if (config == null) { return true; } // If this is a translation mixed with something else, consider it a translation directory. boolean hasTranslation = false; for (ResourceQualifier qualifier : config.getQualifiers()) { if (qualifier instanceof LocaleQualifier) { hasTranslation = true; } } return !hasTranslation; } }