blob: 225406a7e8498ac55a621c7532f8df2232fb4869 [file] [log] [blame]
Richard S. Hall85bafab2009-07-13 13:25:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package org.cauldron.bld.config;
21
22import java.io.IOException;
23import java.io.PrintWriter;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.List;
28import java.util.Map;
29import java.util.Properties;
30import java.util.TreeMap;
31
32import org.cauldron.bld.core.util.QuoteUtil;
33
34public class BldConfig {
35
36 // control properties
37 public static final String C_BUNDLES = "-bundles";
38 public static final String C_REPOSITORIES = "-repositories";
39
40 // string properties
41 public static final String S_ACTIVATOR = "-activator";
42 public static final String S_DEFAULTS = "-defaults";
43 public static final String S_ID = "id";
44 public static final String S_SYM_NAME = "name";
45 public static final String S_VERSION = "version";
46 public static final String[] STRING_KEYS = { S_ACTIVATOR, S_DEFAULTS, S_ID, S_SYM_NAME, S_VERSION };
47
48 // list properties
49 public static final String L_COMPOSITES = "-composites";
50 public static final String L_CONTENTS = "-contents";
51 public static final String L_DL_CONTENTS = "-downloads";
52 public static final String L_SRC_CONTENTS = "-sourcedirs";
53 public static final String L_RESOURCES = "-resources";
54 public static final String[] LIST_KEYS = {
55 L_COMPOSITES, L_CONTENTS, L_DL_CONTENTS, L_SRC_CONTENTS, L_RESOURCES };
56
57 // map properties
58 public static final String M_EXPORTS = "-exports";
59 public static final String M_IMPORTS = "-imports";
60 public static final String M_REQUIRES = "-requires";
61 public static final String M_FRAGMENT = "-fragment";
62 public static final String M_LIBS = "-libs";
63 public static final String[] MAP_KEYS = { M_EXPORTS, M_IMPORTS, M_REQUIRES, M_FRAGMENT, M_LIBS };
64
65 // property properties
66 public static final String P_HEADER = "header";
67 public static final String P_OPTION = "option";
68 public static final String P_PACKAGE_VERSION = "package";
69 public static final String P_BUNDLE_VERSION = "bundle";
70 public static final String[] PROP_KEYS = { P_HEADER, P_OPTION, P_PACKAGE_VERSION, P_BUNDLE_VERSION };
71
72 // private constants
73 private static final String LIST_REGEX = ",\\s*";
74 private static final String MAPATTR_REGEX = ";\\s*";
75 private static final String MAPATTR_SEP = ";";
76 private static final String SUBKEY_SEP = ";";
77
78 // configuration is stored in typed maps
79 private Map<String, String> string = new TreeMap<String, String>();
80 private Map<String, List<String>> list = new TreeMap<String, List<String>>();
81 private Map<String, Map<String, Map<String, String>>> map = new TreeMap<String, Map<String,Map<String,String>>>();
82 private Map<String, BldConfig> config = new TreeMap<String, BldConfig>();
83 private Map<String, Properties> property = new TreeMap<String, Properties>();
84
85 // default config - not modified or saved
86 private BldConfig dflt;
87
88 private Properties unknown = new Properties();
89 private String comment = "";
90
91 public BldConfig() {
92 }
93
94 public BldConfig(Properties p) throws IOException {
95 merge(p);
96 }
97
98 public void setDefault(BldConfig dflt) {
99 this.dflt = dflt;
100 }
101
102 public void setComment(String comment) {
103 this.comment = comment;
104 }
105
106 public Properties getUnknown() {
107 return unknown;
108 }
109
110 public String getString(String id, String key) {
111 if (id != null && config.containsKey(id)) {
112 String value = config.get(id).getString(null, key);
113 if (value != null)
114 return value;
115 }
116 return string.containsKey(key) ? string.get(key) : (dflt != null ? dflt.getString(id, key) : null);
117 }
118
119 public List<String> getList(String id, String key) {
120 if (id != null && config.containsKey(id)) {
121 List<String> value = config.get(id).getList(null, key);
122 if (value != null)
123 return value;
124 }
125 return list.containsKey(key) ? list.get(key) : (dflt != null ? dflt.getList(id, key) : Collections.<String>emptyList());
126 }
127
128 public Map<String, Map<String,String>> getMap(String id, String key) {
129 if (id != null && config.containsKey(id)) {
130 Map<String, Map<String,String>> value = config.get(id).getMap(null, key);
131 if (value != null)
132 return value;
133 }
134 return map.containsKey(key) ? map.get(key)
135 : (dflt != null ? dflt.getMap(id, key) : Collections.<String, Map<String,String>>emptyMap());
136 }
137
138 public void setString(String id, String key, String value) {
139 if (!value.equals(getString(id, key))) {
140 if (id != null) {
141 if (!config.containsKey(id))
142 config.put(id, new BldConfig());
143 config.get(id).setString(null, key, value);
144 } else {
145 String dval = (dflt == null ? dflt.getString(null, key) : null);
146 if (value.equals("") && (dval == null || dval.equals(""))) {
147 string.remove(key);
148 } else {
149 string.put(key, value);
150 }
151 }
152 }
153 }
154
155 public void setList(String id, String key, List<String> value) {
156 if (!value.equals(getList(id, key))) {
157 if (id != null) {
158 if (!config.containsKey(id))
159 config.put(id, new BldConfig());
160 config.get(id).setList(null, key, value);
161 } else if (value.isEmpty() && (dflt == null || dflt.getList(null, key).isEmpty())) {
162 list.remove(key);
163 } else {
164 list.put(key, value);
165 }
166 }
167 }
168
169 public void setMap(String id, String key, Map<String, Map<String,String>> value) {
170 if (!value.equals(getMap(id, key))) {
171 if (id != null) {
172 if (!config.containsKey(id))
173 config.put(id, new BldConfig());
174 config.get(id).setMap(null, key, value);
175 } else if (value.isEmpty() && (dflt == null || dflt.getMap(null, key).isEmpty())) {
176 map.remove(key);
177 } else {
178 map.put(key, value);
179 }
180 }
181 }
182
183 public Properties getProps(String id, String key) {
184 // merge main and sub properties
185 Properties props = new Properties();
186
187 if (dflt != null)
188 props.putAll(dflt.getProps(id, key));
189
190 if (property.containsKey(key))
191 props.putAll(property.get(key));
192
193 if (id != null && config.containsKey(id)) {
194 Properties p2 = config.get(id).getProps(null, key);
195 if (p2 != null)
196 props.putAll(p2);
197 }
198
199 return props;
200 }
201
202 // only sets one property at a time
203 public void setProp(String id, String key, String k2, String v2) {
204 if (v2 == null)
205 return;
206 Properties props = getProps(id, key);
207 if (!v2.equals(props.getProperty(key))) {
208 if (id != null) {
209 if (!config.containsKey(id))
210 config.put(id, new BldConfig());
211 config.get(id).setProp(null, key, k2, v2);
212 } else {
213 if (property.containsKey(key)) {
214 property.get(key).put(k2, v2);
215 } else {
216 Properties value = new Properties();
217 value.put(k2, v2);
218 property.put(key, value);
219 }
220 }
221 }
222 }
223
224 /**
225 * write config in Property file format.
226 * This allows us to make it prettier than Properties.store().
227 */
228 public void write(final PrintWriter out) {
229 out.println(comment);
230
231 // Note: don't add date stamp, or file will differ each time it's saved.
232 out.println("# sigil project file, saved by plugin.\n");
233
234 dump("", new Properties() {
235 private static final long serialVersionUID = 1L; //appease eclipse
236 @Override
237 public Object put(Object key, Object value) {
238 if (value instanceof String) {
239 out.println(key + ": " + value);
240 out.println("");
241 } else if (value instanceof List) {
242 out.println(key + ": \\");
243 for (Object k : (List<?>) value) {
244 out.println("\t" + k + ", \\");
245 }
246 out.println("");
247 }
248 else if (value instanceof Map) {
249 out.println(key + ": \\");
250 StringBuilder b = new StringBuilder();
251 for (Map.Entry<?, ?> e : ((Map<?,?>) value).entrySet()) {
252 b.append("\t");
253 b.append(e.getKey());
254 Map<?, ?> v = (Map<?, ?>) e.getValue();
255 if (!v.isEmpty()) {
256 for (Map.Entry<?, ?> e2 : v.entrySet()) {
257 b.append(MAPATTR_SEP);
258 b.append(e2.getKey());
259 b.append("=");
260 String v2 = e2.getValue().toString();
261 if (v2.contains(",")) {
262 b.append("\"");
263 b.append(v2);
264 b.append("\"");
265 }
266 else {
267 b.append(v2);
268 }
269 }
270 }
271 b.append (", \\\n");
272 }
273 out.println(b.toString());
274 }
275 return null;
276 }
277 });
278 out.println("# end");
279 }
280
281 /**
282 * dump config in pseudo Properties format.
283 * Note: some values are not Strings (they're List<String>).
284 */
285 private void dump(String prefix, Properties p) {
286 for (String key : string.keySet()) {
287 p.put(prefix + key, string.get(key));
288 }
289
290 for (String key : list.keySet()) {
291 List<String> list2 = list.get(key);
292 p.put(prefix + key, list2);
293 }
294
295 for (String key : map.keySet()) {
296 Map<String, Map<String,String>> map2 = map.get(key);
297 p.put(prefix + key, map2);
298 }
299
300 for (String key : property.keySet()) {
301 Properties props = property.get(key);
302 for (Object k2 : props.keySet()) {
303 p.put(prefix + key + SUBKEY_SEP + k2, props.get(k2));
304 }
305 }
306
307 for (String key : config.keySet()) {
308 BldConfig config2 = config.get(key);
309 config2.dump(key + SUBKEY_SEP + prefix, p);
310 }
311
312 for (Object key : unknown.keySet()) {
313 String value = unknown.getProperty((String)key);
314 if (value.length() > 0)
315 p.put(prefix + key, value);
316 }
317 }
318
319 /**
320 * merges properties into current configuration.
321 * @param p
322 * @throws IOException
323 */
324 public void merge(Properties p) throws IOException {
325 if (p.isEmpty())
326 return;
327
328 final List<String> strings = Arrays.asList(STRING_KEYS);
329 final List<String> lists = Arrays.asList(LIST_KEYS);
330 final List<String> maps = Arrays.asList(MAP_KEYS);
331
332 List<String> bundleKeys = new ArrayList<String>();
333 List<String> repoKeys = new ArrayList<String>();
334
335 String bundles = p.getProperty(C_BUNDLES);
336 if (bundles != null) {
337 bundleKeys.addAll(Arrays.asList(bundles.split(LIST_REGEX)));
338 list.put(C_BUNDLES, bundleKeys);
339 }
340
341 String repos = p.getProperty(C_REPOSITORIES);
342 if (repos != null) {
343 for ( String s : repos.split(LIST_REGEX) ) {
344 repoKeys.add(s.trim());
345 }
346 list.put(C_REPOSITORIES, repoKeys);
347 }
348
349 List<String> subKeys = new ArrayList<String>();
350 subKeys.addAll(Arrays.asList(PROP_KEYS));
351 subKeys.addAll(bundleKeys);
352 subKeys.addAll(repoKeys);
353
354 Map<String, Properties> sub = new TreeMap<String, Properties>();
355
356 for (Object k : p.keySet()) {
357 String key = (String) k;
358 if (key.equals(C_BUNDLES) || key.equals(C_REPOSITORIES))
359 continue;
360
361 String value = p.getProperty(key);
362 String[] keys = key.split(SUBKEY_SEP, 2);
363
364 if (keys.length > 1) {
365 Properties p2 = sub.get(keys[0]);
366 if (p2 == null) {
367 p2 = new Properties();
368 sub.put(keys[0], p2);
369 if (!subKeys.contains(keys[0])) {
370 unknown.setProperty(keys[0] + SUBKEY_SEP, "");
371 }
372 }
373 p2.setProperty(keys[1], value);
374 } else if (strings.contains(key)) {
375 if (!string.containsKey(key))
376 string.put(key, value);
377 } else if (lists.contains(key)) {
378 if (!list.containsKey(key)) {
379 ArrayList<String> list2 = new ArrayList<String>();
380 for (String s : value.split(LIST_REGEX)) {
381 if ( s.trim().length() > 0 ) {
382 list2.add(s.trim());
383 }
384 }
385 if ( !list2.isEmpty() ) {
386 list.put(key, list2);
387 }
388 }
389 } else if (maps.contains(key)) {
390 if (!map.containsKey(key)) {
391 Map<String, Map<String,String>> map2 = new TreeMap<String, Map<String,String>>();
392
393 for (String subValue : QuoteUtil.split(value)) {
394 if (subValue.trim().length() > 0) {
395 String[] split = subValue.split(MAPATTR_REGEX);
396 Map<String,String> map3 = new TreeMap<String,String>();
397 for (int i = 1; i < split.length; ++i){
398 String[] keyVal = split[i].split(":?=", 2);
399 if (keyVal.length != 2) {
400 throw new IOException("attribute missing '=':" + subValue);
401 }
402 map3.put(keyVal[0], keyVal[1]);
403 }
404 map2.put(split[0], map3);
405 }
406 }
407
408 map.put(key, map2);
409 }
410 } else {
411 unknown.setProperty(key, value);
412 }
413 }
414
415 for (String subKey : sub.keySet()) {
416 Properties props = sub.get(subKey);
417 if (!props.isEmpty()) {
418 if (bundleKeys.contains(subKey)) {
419 BldConfig config2 = new BldConfig(props);
420 Properties unkProps = config2.getUnknown();
421
422 if (config2.map.containsKey(M_IMPORTS))
423 unkProps.setProperty(M_IMPORTS, "");
424
425 if (config2.map.containsKey(M_REQUIRES))
426 unkProps.setProperty(M_REQUIRES, "");
427
428 for (Object unk : unkProps.keySet()) {
429 unknown.setProperty(subKey + SUBKEY_SEP + unk, "");
430 }
431 config.put(subKey, config2);
432 } else {
433 property.put(subKey, props);
434 }
435 }
436 }
437 }
438
439 @Override
440 public String toString() {
441 return "string: " + string + " list:" + list + " map: " + map + " prop: " + property + " config:" + config;
442 }
443
444}
445