| // 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.devtools.build.android; | |
| import com.google.common.base.Function; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.devtools.build.android.proto.SerializeFormat; | |
| import com.google.devtools.build.android.proto.SerializeFormat.Header; | |
| import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.OutputStream; | |
| import java.nio.file.FileSystem; | |
| import java.util.LinkedHashMap; | |
| import java.util.LinkedList; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.NavigableMap; | |
| import java.util.stream.Collectors; | |
| /** | |
| * Tracks mappings from resource source paths (/foo/bar/res/values/colors.xml) to an ID for a more | |
| * compact serialization format. | |
| */ | |
| class DataSourceTable { | |
| private static final Function<DataValue, DataSource> VALUE_TO_SOURCE = DataValue::source; | |
| private final Map<DataSource, Integer> sourceTable = new LinkedHashMap<>(); | |
| private DataSource[] idToSource; | |
| /** | |
| * Creates a DataSourceTable and serialize to the given outstream. Assigns each resource source | |
| * path a number to enable {@link #getSourceId(DataSource)} queries. | |
| * | |
| * @param map the final map of resources | |
| * @param outStream stream to serialize the source table | |
| * @param headerBuilder the header to serialize | |
| * @throws IOException if this fails to serialize the table to the outStream | |
| */ | |
| public static DataSourceTable createAndWrite( | |
| NavigableMap<DataKey, DataValue> map, OutputStream outStream, Header.Builder headerBuilder) | |
| throws IOException { | |
| DataSourceTable sourceTable = new DataSourceTable(); | |
| sourceTable.writeSourceInfo(map, outStream); | |
| sourceTable.setHeader(headerBuilder); | |
| return sourceTable; | |
| } | |
| /** Convert the absolute source path to the source table index */ | |
| public int getSourceId(DataSource source) { | |
| return sourceTable.get(source); | |
| } | |
| private void writeSourceInfo(NavigableMap<DataKey, DataValue> map, OutputStream outStream) | |
| throws IOException { | |
| int sourceNumber = 0; | |
| LinkedList<DataSource> sourceQueue = | |
| map.values() | |
| .stream() | |
| .map(VALUE_TO_SOURCE) | |
| .collect(Collectors.toCollection(LinkedList::new)); | |
| while (!sourceQueue.isEmpty()) { | |
| DataSource source = sourceQueue.pop(); | |
| if (!sourceTable.containsKey(source)) { | |
| sourceTable.put(source, sourceNumber); | |
| ++sourceNumber; | |
| sourceQueue.addAll(source.overrides()); | |
| } | |
| } | |
| for (DataSource dataSource : sourceTable.keySet()) { | |
| ProtoSource.newBuilder() | |
| .setFilename(dataSource.getPath().toString()) | |
| .addAllOverwritten(sourcesToIds(dataSource.overrides())) | |
| .build() | |
| .writeDelimitedTo(outStream); | |
| } | |
| } | |
| private List<Integer> sourcesToIds(ImmutableSet<DataSource> overrides) { | |
| ImmutableList.Builder<Integer> idsBuilder = ImmutableList.builder(); | |
| for (DataSource dataSource : overrides) { | |
| if (!sourceTable.containsKey(dataSource)) { | |
| throw new IllegalArgumentException( | |
| "Cannot find data source: " + dataSource.toString() + " in " + sourceTable.keySet()); | |
| } | |
| idsBuilder.add(sourceTable.get(dataSource)); | |
| } | |
| return idsBuilder.build(); | |
| } | |
| /** Fill in the serialize format header information required to deserialize */ | |
| private Header.Builder setHeader(Header.Builder headerBuilder) { | |
| return headerBuilder.setSourceCount(sourceTable.size()); | |
| } | |
| /** Deserialize the source table and allow {@link #sourceFromId(int)} queries. */ | |
| public static DataSourceTable read( | |
| DependencyInfo dependencyInfo, InputStream in, FileSystem currentFileSystem, Header header) | |
| throws IOException { | |
| DataSourceTable sourceTable = new DataSourceTable(); | |
| sourceTable.readSourceInfo(dependencyInfo, in, currentFileSystem, header); | |
| return sourceTable; | |
| } | |
| /** Convert the source ID to full Path */ | |
| public DataSource sourceFromId(int sourceId) { | |
| return idToSource[sourceId]; | |
| } | |
| private void readSourceInfo( | |
| DependencyInfo dependencyInfo, InputStream in, FileSystem currentFileSystem, Header header) | |
| throws IOException { | |
| int numberOfSources = header.getSourceCount(); | |
| // Read back the sources. | |
| idToSource = new DataSource[numberOfSources]; | |
| for (int i = 0; i < numberOfSources; i++) { | |
| ProtoSource protoSource = SerializeFormat.ProtoSource.parseDelimitedFrom(in); | |
| idToSource[i] = DataSource.from(dependencyInfo, protoSource, currentFileSystem); | |
| } | |
| } | |
| } |