blob: 62689ff5e8892142364a02c1ef934f6d60135e4a [file] [log] [blame]
// Copyright 2017 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.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.android.FullyQualifiedName.Factory;
import com.google.devtools.build.android.ParsedAndroidData.CombiningConsumer;
import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer;
import com.google.devtools.build.android.ParsedAndroidData.OverwritableConsumer;
import com.google.devtools.build.android.resources.Visibility;
import com.google.devtools.build.android.xml.Namespaces;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Build for ParsedAndroidData instance.
*/
public class ParsedAndroidDataBuilder {
private final Path defaultRoot;
private final FullyQualifiedName.Factory fqnFactory;
private final Map<DataKey, DataResource> overwrite = new HashMap<>();
private final Map<DataKey, DataResource> combine = new HashMap<>();
private final Map<DataKey, DataAsset> assets = new HashMap<>();
private final Set<MergeConflict> conflicts = new HashSet<>();
public ParsedAndroidDataBuilder(
@Nullable Path root, @Nullable FullyQualifiedName.Factory fqnFactory) {
this.defaultRoot = root;
this.fqnFactory = fqnFactory;
}
public static ParsedAndroidData empty() {
return ParsedAndroidData.of(
ImmutableSet.<MergeConflict>of(),
ImmutableMap.<DataKey, DataResource>of(),
ImmutableMap.<DataKey, DataResource>of(),
ImmutableMap.<DataKey, DataAsset>of());
}
public static ParsedAndroidDataBuilder buildOn(
Path defaultRoot, FullyQualifiedName.Factory fqnFactory) {
return new ParsedAndroidDataBuilder(defaultRoot, fqnFactory);
}
public static ParsedAndroidDataBuilder buildOn(FullyQualifiedName.Factory fqnFactory) {
return buildOn(null, fqnFactory);
}
public static ParsedAndroidDataBuilder buildOn(Path defaultRoot) {
return buildOn(defaultRoot, null);
}
public static ParsedAndroidDataBuilder builder() {
return buildOn(null, null);
}
public ParsedAndroidDataBuilder overwritable(DataEntry... resourceBuilders) {
OverwritableConsumer<DataKey, DataResource> consumer =
new OverwritableConsumer<>(overwrite, conflicts);
for (DataEntry resourceBuilder : resourceBuilders) {
resourceBuilder.accept(fqnFactory, defaultRoot, consumer);
}
return this;
}
public ParsedAndroidDataBuilder combining(DataEntry... resourceBuilders) {
CombiningConsumer consumer = new CombiningConsumer(combine);
for (DataEntry resourceBuilder : resourceBuilders) {
resourceBuilder.accept(fqnFactory, defaultRoot, consumer);
}
return this;
}
public ParsedAndroidDataBuilder assets(DataEntry... assetBuilders) {
OverwritableConsumer<DataKey, DataAsset> consumer =
new OverwritableConsumer<>(assets, conflicts);
for (DataEntry assetBuilder : assetBuilders) {
assetBuilder.accept(defaultRoot, consumer);
}
return this;
}
public static FileResourceBuilder file(String rawKey) {
return new FileResourceBuilder(rawKey);
}
public static FileResourceBuilder file() {
return new FileResourceBuilder(null);
}
public static XmlResourceBuilder xml(String rawKey) {
return new XmlResourceBuilder(rawKey);
}
public ParsedAndroidData build() {
return ParsedAndroidData.of(
ImmutableSet.copyOf(conflicts),
ImmutableMap.copyOf(overwrite),
ImmutableMap.copyOf(combine),
ImmutableMap.copyOf(assets));
}
static class FileResourceBuilder {
private String rawKey;
private Path root;
FileResourceBuilder(@Nullable String rawKey) {
this.rawKey = rawKey;
}
FileResourceBuilder root(Path root) {
this.root = root;
return this;
}
Path chooseRoot(Path defaultRoot) {
if (defaultRoot != null) {
return defaultRoot;
}
if (root != null) {
return root;
}
throw new IllegalStateException(
"the default root and asset root are null! A root is required!");
}
DataEntry source(final DataSource source) {
return new DataEntry() {
@Override
void accept(
@Nullable Factory factory,
@Nullable Path root,
KeyValueConsumer<DataKey, DataResource> consumer) {
consumer.accept(
factory.parse(rawKey),
DataValueFile.of(
Visibility.UNKNOWN, source, /*fingerprint=*/ null, /*rootXmlNode=*/ null));
}
@Override
void accept(@Nullable Path defaultRoot, KeyValueConsumer<DataKey, DataAsset> target) {
target.accept(
RelativeAssetPath.Factory.of(chooseRoot(defaultRoot).resolve("assets"))
.create(source.getPath()),
DataValueFile.of(
Visibility.UNKNOWN, source, /*fingerprint=*/ null, /*rootXmlNode=*/ null));
}
};
}
DataEntry source(final String path) {
return new DataEntry() {
@Override
public void accept(
FullyQualifiedName.Factory factory,
Path defaultRoot,
KeyValueConsumer<DataKey, DataResource> consumer) {
Path res = chooseRoot(defaultRoot).resolve("res");
consumer.accept(factory.parse(rawKey), DataValueFile.of(res.resolve(path)));
}
@Override
public void accept(
@Nullable Path defaultRoot, KeyValueConsumer<DataKey, DataAsset> consumer) {
Path assets = chooseRoot(defaultRoot).resolve("assets");
Path fullPath = assets.resolve(path);
consumer.accept(
RelativeAssetPath.Factory.of(assets).create(fullPath), DataValueFile.of(fullPath));
}
};
}
}
static class XmlResourceBuilder {
private final String rawFqn;
private Path root;
private final Map<String, String> prefixToUri = new LinkedHashMap<>();
XmlResourceBuilder(String rawFqn) {
this(rawFqn, null);
}
XmlResourceBuilder(String rawFqn, Path root) {
this.rawFqn = rawFqn;
this.root = root;
}
XmlResourceBuilder source(final String path) {
return new XmlResourceBuilder(rawFqn, root) {
@Override
public DataEntry value(final XmlResourceValue value) {
return new DataEntry() {
@Override
public void accept(
FullyQualifiedName.Factory factory,
Path defaultRoot,
KeyValueConsumer<DataKey, DataResource> consumer) {
Path res = (root == null ? defaultRoot : root).resolve("res");
consumer.accept(
factory.parse(rawFqn),
DataResourceXml.createWithNamespaces(
res.resolve(path), value, Namespaces.from(prefixToUri)));
}
};
}
};
}
XmlResourceBuilder source(final DataSource dataSource) {
return new XmlResourceBuilder(rawFqn, root) {
@Override
public DataEntry value(final XmlResourceValue value) {
return new DataEntry() {
@Override
public void accept(
FullyQualifiedName.Factory factory,
Path defaultRoot,
KeyValueConsumer<DataKey, DataResource> consumer) {
consumer.accept(
factory.parse(rawFqn),
DataResourceXml.createWithNamespaces(
dataSource, value, Namespaces.from(prefixToUri)));
}
};
}
};
}
XmlResourceBuilder root(Path root) {
this.root = root;
return this;
}
XmlResourceBuilder namespace(String prefix, String uri) {
prefixToUri.put(prefix, uri);
return this;
}
DataEntry value(final XmlResourceValue value) {
throw new UnsupportedOperationException("A source must be declared!");
}
}
abstract static class DataEntry {
void accept(
@Nullable FullyQualifiedName.Factory factory,
@Nullable Path root,
KeyValueConsumer<DataKey, DataResource> consumer) {
throw new UnsupportedOperationException("assets cannot be resources!");
}
void accept(@Nullable Path root, KeyValueConsumer<DataKey, DataAsset> target) {
throw new UnsupportedOperationException("xml resources cannot be assets!");
}
}
}