blob: 21e79dae949f62b86324bdd2715e1cdbf2732469 [file] [log] [blame]
Adam Michael2587a6d2017-03-22 15:33:40 +00001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.android;
15
16import com.google.common.collect.ImmutableMap;
17import com.google.common.collect.ImmutableSet;
18import com.google.devtools.build.android.FullyQualifiedName.Factory;
19import com.google.devtools.build.android.ParsedAndroidData.CombiningConsumer;
20import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer;
21import com.google.devtools.build.android.ParsedAndroidData.OverwritableConsumer;
22import com.google.devtools.build.android.xml.Namespaces;
23import java.nio.file.Path;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.LinkedHashMap;
27import java.util.Map;
28import java.util.Set;
29import javax.annotation.Nullable;
30
31/**
32 * Build for ParsedAndroidData instance.
33 */
34public class ParsedAndroidDataBuilder {
35
36 private final Path defaultRoot;
37 private final FullyQualifiedName.Factory fqnFactory;
38 private final Map<DataKey, DataResource> overwrite = new HashMap<>();
39 private final Map<DataKey, DataResource> combine = new HashMap<>();
40 private final Map<DataKey, DataAsset> assets = new HashMap<>();
41 private final Set<MergeConflict> conflicts = new HashSet<>();
42
43 public ParsedAndroidDataBuilder(
44 @Nullable Path root, @Nullable FullyQualifiedName.Factory fqnFactory) {
45 this.defaultRoot = root;
46 this.fqnFactory = fqnFactory;
47 }
48
49 public static ParsedAndroidData empty() {
50 return ParsedAndroidData.of(
corysmithd18d3e22018-02-16 15:48:49 -080051 ImmutableSet.<MergeConflict>of(),
52 ImmutableMap.<DataKey, DataResource>of(),
53 ImmutableMap.<DataKey, DataResource>of(),
54 ImmutableMap.<DataKey, DataAsset>of());
Adam Michael2587a6d2017-03-22 15:33:40 +000055 }
56
57 public static ParsedAndroidDataBuilder buildOn(
58 Path defaultRoot, FullyQualifiedName.Factory fqnFactory) {
59 return new ParsedAndroidDataBuilder(defaultRoot, fqnFactory);
60 }
61
62 public static ParsedAndroidDataBuilder buildOn(FullyQualifiedName.Factory fqnFactory) {
63 return buildOn(null, fqnFactory);
64 }
65
66 public static ParsedAndroidDataBuilder buildOn(Path defaultRoot) {
67 return buildOn(defaultRoot, null);
68 }
69
70 public static ParsedAndroidDataBuilder builder() {
71 return buildOn(null, null);
72 }
73
74 public ParsedAndroidDataBuilder overwritable(DataEntry... resourceBuilders) {
75 OverwritableConsumer<DataKey, DataResource> consumer =
76 new OverwritableConsumer<>(overwrite, conflicts);
77 for (DataEntry resourceBuilder : resourceBuilders) {
78 resourceBuilder.accept(fqnFactory, defaultRoot, consumer);
79 }
80 return this;
81 }
82
83 public ParsedAndroidDataBuilder combining(DataEntry... resourceBuilders) {
84 CombiningConsumer consumer = new CombiningConsumer(combine);
85 for (DataEntry resourceBuilder : resourceBuilders) {
86 resourceBuilder.accept(fqnFactory, defaultRoot, consumer);
87 }
88 return this;
89 }
90
91 public ParsedAndroidDataBuilder assets(DataEntry... assetBuilders) {
92 OverwritableConsumer<DataKey, DataAsset> consumer =
93 new OverwritableConsumer<>(assets, conflicts);
94 for (DataEntry assetBuilder : assetBuilders) {
95 assetBuilder.accept(defaultRoot, consumer);
96 }
97 return this;
98 }
99
100 public static FileResourceBuilder file(String rawKey) {
101 return new FileResourceBuilder(rawKey);
102 }
103
104 public static FileResourceBuilder file() {
105 return new FileResourceBuilder(null);
106 }
107
108 public static XmlResourceBuilder xml(String rawKey) {
109 return new XmlResourceBuilder(rawKey);
110 }
111
112 public ParsedAndroidData build() {
113 return ParsedAndroidData.of(
114 ImmutableSet.copyOf(conflicts),
115 ImmutableMap.copyOf(overwrite),
116 ImmutableMap.copyOf(combine),
117 ImmutableMap.copyOf(assets));
118 }
119
120 static class FileResourceBuilder {
121 private String rawKey;
122 private Path root;
123
124 FileResourceBuilder(@Nullable String rawKey) {
125 this.rawKey = rawKey;
126 }
127
128 FileResourceBuilder root(Path root) {
129 this.root = root;
130 return this;
131 }
132
133 Path chooseRoot(Path defaultRoot) {
134 if (defaultRoot != null) {
135 return defaultRoot;
136 }
137 if (root != null) {
138 return root;
139 }
140 throw new IllegalStateException(
141 "the default root and asset root are null! A root is required!");
142 }
143
144 DataEntry source(final DataSource source) {
145 return new DataEntry() {
146 @Override
147 void accept(
148 @Nullable Factory factory,
149 @Nullable Path root,
150 KeyValueConsumer<DataKey, DataResource> consumer) {
corysmithdb074362017-10-19 18:22:00 +0200151 consumer.accept(factory.parse(rawKey), DataValueFile.of(source));
Adam Michael2587a6d2017-03-22 15:33:40 +0000152 }
153
154 @Override
155 void accept(@Nullable Path defaultRoot, KeyValueConsumer<DataKey, DataAsset> target) {
corysmithdb074362017-10-19 18:22:00 +0200156 target.accept(
Adam Michael2587a6d2017-03-22 15:33:40 +0000157 RelativeAssetPath.Factory.of(chooseRoot(defaultRoot).resolve("assets"))
158 .create(source.getPath()),
159 DataValueFile.of(source));
160 }
161 };
162 }
163
164 DataEntry source(final String path) {
165 return new DataEntry() {
166 @Override
167 public void accept(
168 FullyQualifiedName.Factory factory,
169 Path defaultRoot,
170 KeyValueConsumer<DataKey, DataResource> consumer) {
171 Path res = chooseRoot(defaultRoot).resolve("res");
corysmithdb074362017-10-19 18:22:00 +0200172 consumer.accept(factory.parse(rawKey), DataValueFile.of(res.resolve(path)));
Adam Michael2587a6d2017-03-22 15:33:40 +0000173 }
174
175 @Override
176 public void accept(
177 @Nullable Path defaultRoot, KeyValueConsumer<DataKey, DataAsset> consumer) {
178 Path assets = chooseRoot(defaultRoot).resolve("assets");
179 Path fullPath = assets.resolve(path);
corysmithdb074362017-10-19 18:22:00 +0200180 consumer.accept(
Adam Michael2587a6d2017-03-22 15:33:40 +0000181 RelativeAssetPath.Factory.of(assets).create(fullPath), DataValueFile.of(fullPath));
182 }
183 };
184 }
185 }
186
187 static class XmlResourceBuilder {
188 private final String rawFqn;
189 private Path root;
190 private final Map<String, String> prefixToUri = new LinkedHashMap<>();
191
192 XmlResourceBuilder(String rawFqn) {
193 this(rawFqn, null);
194 }
195
196 XmlResourceBuilder(String rawFqn, Path root) {
197 this.rawFqn = rawFqn;
198 this.root = root;
199 }
200
201 XmlResourceBuilder source(final String path) {
202 return new XmlResourceBuilder(rawFqn, root) {
203 @Override
204 public DataEntry value(final XmlResourceValue value) {
205 return new DataEntry() {
206 @Override
207 public void accept(
208 FullyQualifiedName.Factory factory,
209 Path defaultRoot,
210 KeyValueConsumer<DataKey, DataResource> consumer) {
211 Path res = (root == null ? defaultRoot : root).resolve("res");
corysmithdb074362017-10-19 18:22:00 +0200212 consumer.accept(
Adam Michael2587a6d2017-03-22 15:33:40 +0000213 factory.parse(rawFqn),
214 DataResourceXml.createWithNamespaces(
215 res.resolve(path), value, Namespaces.from(prefixToUri)));
216 }
217 };
218 }
219 };
220 }
221
222 XmlResourceBuilder source(final DataSource dataSource) {
223 return new XmlResourceBuilder(rawFqn, root) {
224 @Override
225 public DataEntry value(final XmlResourceValue value) {
226 return new DataEntry() {
227 @Override
228 public void accept(
229 FullyQualifiedName.Factory factory,
230 Path defaultRoot,
231 KeyValueConsumer<DataKey, DataResource> consumer) {
corysmithdb074362017-10-19 18:22:00 +0200232 consumer.accept(
Adam Michael2587a6d2017-03-22 15:33:40 +0000233 factory.parse(rawFqn),
234 DataResourceXml.createWithNamespaces(
235 dataSource, value, Namespaces.from(prefixToUri)));
236 }
237 };
238 }
239 };
240 }
241
242 XmlResourceBuilder root(Path root) {
243 this.root = root;
244 return this;
245 }
246
247 XmlResourceBuilder namespace(String prefix, String uri) {
248 prefixToUri.put(prefix, uri);
249 return this;
250 }
251
252 DataEntry value(final XmlResourceValue value) {
253 throw new UnsupportedOperationException("A source must be declared!");
254 }
255 }
256
257 abstract static class DataEntry {
258 void accept(
259 @Nullable FullyQualifiedName.Factory factory,
260 @Nullable Path root,
261 KeyValueConsumer<DataKey, DataResource> consumer) {
262 throw new UnsupportedOperationException("assets cannot be resources!");
263 }
264
265 void accept(@Nullable Path root, KeyValueConsumer<DataKey, DataAsset> target) {
266 throw new UnsupportedOperationException("xml resources cannot be assets!");
267 }
268 }
269}