blob: 225406a7e8498ac55a621c7532f8df2232fb4869 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.cauldron.bld.config;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.cauldron.bld.core.util.QuoteUtil;
public class BldConfig {
// control properties
public static final String C_BUNDLES = "-bundles";
public static final String C_REPOSITORIES = "-repositories";
// string properties
public static final String S_ACTIVATOR = "-activator";
public static final String S_DEFAULTS = "-defaults";
public static final String S_ID = "id";
public static final String S_SYM_NAME = "name";
public static final String S_VERSION = "version";
public static final String[] STRING_KEYS = { S_ACTIVATOR, S_DEFAULTS, S_ID, S_SYM_NAME, S_VERSION };
// list properties
public static final String L_COMPOSITES = "-composites";
public static final String L_CONTENTS = "-contents";
public static final String L_DL_CONTENTS = "-downloads";
public static final String L_SRC_CONTENTS = "-sourcedirs";
public static final String L_RESOURCES = "-resources";
public static final String[] LIST_KEYS = {
L_COMPOSITES, L_CONTENTS, L_DL_CONTENTS, L_SRC_CONTENTS, L_RESOURCES };
// map properties
public static final String M_EXPORTS = "-exports";
public static final String M_IMPORTS = "-imports";
public static final String M_REQUIRES = "-requires";
public static final String M_FRAGMENT = "-fragment";
public static final String M_LIBS = "-libs";
public static final String[] MAP_KEYS = { M_EXPORTS, M_IMPORTS, M_REQUIRES, M_FRAGMENT, M_LIBS };
// property properties
public static final String P_HEADER = "header";
public static final String P_OPTION = "option";
public static final String P_PACKAGE_VERSION = "package";
public static final String P_BUNDLE_VERSION = "bundle";
public static final String[] PROP_KEYS = { P_HEADER, P_OPTION, P_PACKAGE_VERSION, P_BUNDLE_VERSION };
// private constants
private static final String LIST_REGEX = ",\\s*";
private static final String MAPATTR_REGEX = ";\\s*";
private static final String MAPATTR_SEP = ";";
private static final String SUBKEY_SEP = ";";
// configuration is stored in typed maps
private Map<String, String> string = new TreeMap<String, String>();
private Map<String, List<String>> list = new TreeMap<String, List<String>>();
private Map<String, Map<String, Map<String, String>>> map = new TreeMap<String, Map<String,Map<String,String>>>();
private Map<String, BldConfig> config = new TreeMap<String, BldConfig>();
private Map<String, Properties> property = new TreeMap<String, Properties>();
// default config - not modified or saved
private BldConfig dflt;
private Properties unknown = new Properties();
private String comment = "";
public BldConfig() {
}
public BldConfig(Properties p) throws IOException {
merge(p);
}
public void setDefault(BldConfig dflt) {
this.dflt = dflt;
}
public void setComment(String comment) {
this.comment = comment;
}
public Properties getUnknown() {
return unknown;
}
public String getString(String id, String key) {
if (id != null && config.containsKey(id)) {
String value = config.get(id).getString(null, key);
if (value != null)
return value;
}
return string.containsKey(key) ? string.get(key) : (dflt != null ? dflt.getString(id, key) : null);
}
public List<String> getList(String id, String key) {
if (id != null && config.containsKey(id)) {
List<String> value = config.get(id).getList(null, key);
if (value != null)
return value;
}
return list.containsKey(key) ? list.get(key) : (dflt != null ? dflt.getList(id, key) : Collections.<String>emptyList());
}
public Map<String, Map<String,String>> getMap(String id, String key) {
if (id != null && config.containsKey(id)) {
Map<String, Map<String,String>> value = config.get(id).getMap(null, key);
if (value != null)
return value;
}
return map.containsKey(key) ? map.get(key)
: (dflt != null ? dflt.getMap(id, key) : Collections.<String, Map<String,String>>emptyMap());
}
public void setString(String id, String key, String value) {
if (!value.equals(getString(id, key))) {
if (id != null) {
if (!config.containsKey(id))
config.put(id, new BldConfig());
config.get(id).setString(null, key, value);
} else {
String dval = (dflt == null ? dflt.getString(null, key) : null);
if (value.equals("") && (dval == null || dval.equals(""))) {
string.remove(key);
} else {
string.put(key, value);
}
}
}
}
public void setList(String id, String key, List<String> value) {
if (!value.equals(getList(id, key))) {
if (id != null) {
if (!config.containsKey(id))
config.put(id, new BldConfig());
config.get(id).setList(null, key, value);
} else if (value.isEmpty() && (dflt == null || dflt.getList(null, key).isEmpty())) {
list.remove(key);
} else {
list.put(key, value);
}
}
}
public void setMap(String id, String key, Map<String, Map<String,String>> value) {
if (!value.equals(getMap(id, key))) {
if (id != null) {
if (!config.containsKey(id))
config.put(id, new BldConfig());
config.get(id).setMap(null, key, value);
} else if (value.isEmpty() && (dflt == null || dflt.getMap(null, key).isEmpty())) {
map.remove(key);
} else {
map.put(key, value);
}
}
}
public Properties getProps(String id, String key) {
// merge main and sub properties
Properties props = new Properties();
if (dflt != null)
props.putAll(dflt.getProps(id, key));
if (property.containsKey(key))
props.putAll(property.get(key));
if (id != null && config.containsKey(id)) {
Properties p2 = config.get(id).getProps(null, key);
if (p2 != null)
props.putAll(p2);
}
return props;
}
// only sets one property at a time
public void setProp(String id, String key, String k2, String v2) {
if (v2 == null)
return;
Properties props = getProps(id, key);
if (!v2.equals(props.getProperty(key))) {
if (id != null) {
if (!config.containsKey(id))
config.put(id, new BldConfig());
config.get(id).setProp(null, key, k2, v2);
} else {
if (property.containsKey(key)) {
property.get(key).put(k2, v2);
} else {
Properties value = new Properties();
value.put(k2, v2);
property.put(key, value);
}
}
}
}
/**
* write config in Property file format.
* This allows us to make it prettier than Properties.store().
*/
public void write(final PrintWriter out) {
out.println(comment);
// Note: don't add date stamp, or file will differ each time it's saved.
out.println("# sigil project file, saved by plugin.\n");
dump("", new Properties() {
private static final long serialVersionUID = 1L; //appease eclipse
@Override
public Object put(Object key, Object value) {
if (value instanceof String) {
out.println(key + ": " + value);
out.println("");
} else if (value instanceof List) {
out.println(key + ": \\");
for (Object k : (List<?>) value) {
out.println("\t" + k + ", \\");
}
out.println("");
}
else if (value instanceof Map) {
out.println(key + ": \\");
StringBuilder b = new StringBuilder();
for (Map.Entry<?, ?> e : ((Map<?,?>) value).entrySet()) {
b.append("\t");
b.append(e.getKey());
Map<?, ?> v = (Map<?, ?>) e.getValue();
if (!v.isEmpty()) {
for (Map.Entry<?, ?> e2 : v.entrySet()) {
b.append(MAPATTR_SEP);
b.append(e2.getKey());
b.append("=");
String v2 = e2.getValue().toString();
if (v2.contains(",")) {
b.append("\"");
b.append(v2);
b.append("\"");
}
else {
b.append(v2);
}
}
}
b.append (", \\\n");
}
out.println(b.toString());
}
return null;
}
});
out.println("# end");
}
/**
* dump config in pseudo Properties format.
* Note: some values are not Strings (they're List<String>).
*/
private void dump(String prefix, Properties p) {
for (String key : string.keySet()) {
p.put(prefix + key, string.get(key));
}
for (String key : list.keySet()) {
List<String> list2 = list.get(key);
p.put(prefix + key, list2);
}
for (String key : map.keySet()) {
Map<String, Map<String,String>> map2 = map.get(key);
p.put(prefix + key, map2);
}
for (String key : property.keySet()) {
Properties props = property.get(key);
for (Object k2 : props.keySet()) {
p.put(prefix + key + SUBKEY_SEP + k2, props.get(k2));
}
}
for (String key : config.keySet()) {
BldConfig config2 = config.get(key);
config2.dump(key + SUBKEY_SEP + prefix, p);
}
for (Object key : unknown.keySet()) {
String value = unknown.getProperty((String)key);
if (value.length() > 0)
p.put(prefix + key, value);
}
}
/**
* merges properties into current configuration.
* @param p
* @throws IOException
*/
public void merge(Properties p) throws IOException {
if (p.isEmpty())
return;
final List<String> strings = Arrays.asList(STRING_KEYS);
final List<String> lists = Arrays.asList(LIST_KEYS);
final List<String> maps = Arrays.asList(MAP_KEYS);
List<String> bundleKeys = new ArrayList<String>();
List<String> repoKeys = new ArrayList<String>();
String bundles = p.getProperty(C_BUNDLES);
if (bundles != null) {
bundleKeys.addAll(Arrays.asList(bundles.split(LIST_REGEX)));
list.put(C_BUNDLES, bundleKeys);
}
String repos = p.getProperty(C_REPOSITORIES);
if (repos != null) {
for ( String s : repos.split(LIST_REGEX) ) {
repoKeys.add(s.trim());
}
list.put(C_REPOSITORIES, repoKeys);
}
List<String> subKeys = new ArrayList<String>();
subKeys.addAll(Arrays.asList(PROP_KEYS));
subKeys.addAll(bundleKeys);
subKeys.addAll(repoKeys);
Map<String, Properties> sub = new TreeMap<String, Properties>();
for (Object k : p.keySet()) {
String key = (String) k;
if (key.equals(C_BUNDLES) || key.equals(C_REPOSITORIES))
continue;
String value = p.getProperty(key);
String[] keys = key.split(SUBKEY_SEP, 2);
if (keys.length > 1) {
Properties p2 = sub.get(keys[0]);
if (p2 == null) {
p2 = new Properties();
sub.put(keys[0], p2);
if (!subKeys.contains(keys[0])) {
unknown.setProperty(keys[0] + SUBKEY_SEP, "");
}
}
p2.setProperty(keys[1], value);
} else if (strings.contains(key)) {
if (!string.containsKey(key))
string.put(key, value);
} else if (lists.contains(key)) {
if (!list.containsKey(key)) {
ArrayList<String> list2 = new ArrayList<String>();
for (String s : value.split(LIST_REGEX)) {
if ( s.trim().length() > 0 ) {
list2.add(s.trim());
}
}
if ( !list2.isEmpty() ) {
list.put(key, list2);
}
}
} else if (maps.contains(key)) {
if (!map.containsKey(key)) {
Map<String, Map<String,String>> map2 = new TreeMap<String, Map<String,String>>();
for (String subValue : QuoteUtil.split(value)) {
if (subValue.trim().length() > 0) {
String[] split = subValue.split(MAPATTR_REGEX);
Map<String,String> map3 = new TreeMap<String,String>();
for (int i = 1; i < split.length; ++i){
String[] keyVal = split[i].split(":?=", 2);
if (keyVal.length != 2) {
throw new IOException("attribute missing '=':" + subValue);
}
map3.put(keyVal[0], keyVal[1]);
}
map2.put(split[0], map3);
}
}
map.put(key, map2);
}
} else {
unknown.setProperty(key, value);
}
}
for (String subKey : sub.keySet()) {
Properties props = sub.get(subKey);
if (!props.isEmpty()) {
if (bundleKeys.contains(subKey)) {
BldConfig config2 = new BldConfig(props);
Properties unkProps = config2.getUnknown();
if (config2.map.containsKey(M_IMPORTS))
unkProps.setProperty(M_IMPORTS, "");
if (config2.map.containsKey(M_REQUIRES))
unkProps.setProperty(M_REQUIRES, "");
for (Object unk : unkProps.keySet()) {
unknown.setProperty(subKey + SUBKEY_SEP + unk, "");
}
config.put(subKey, config2);
} else {
property.put(subKey, props);
}
}
}
}
@Override
public String toString() {
return "string: " + string + " list:" + list + " map: " + map + " prop: " + property + " config:" + config;
}
}