package restx.common; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.io.CharSource; import com.google.common.io.CharStreams; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.Arrays.asList; /** * A standard implementation of RestxConfig. * * You can build it either from an iterable of ConfigElement, or by parsing a stream taking the form of a * properties file, where docs can be inserted using lines starting with #. */ public class StdRestxConfig implements RestxConfig { private static final Collection TRUE_VALUES = asList("true", "yes", "on", "1", "y"); public static RestxConfig parse(String origin, CharSource charSource) throws IOException { List<ConfigElement> elements = new ArrayList<>(); StringBuilder doc = new StringBuilder(); int lineCount = 0; for (String line : charSource.readLines()) { lineCount++; if (line.startsWith("#")) { doc.append(line.substring(1).trim()).append("\n"); } else if (!line.trim().isEmpty()) { int index = line.indexOf('='); if (index == -1) { throw new IOException("invalid config " + origin + " at line " + lineCount + ":" + " line does not contain the equals sign '='"); } String key = line.substring(0, index).trim(); String value = line.substring(index + 1).trim(); elements.add(ConfigElement.of(origin, doc.toString().trim(), key, value)); doc.setLength(0); } } return new StdRestxConfig(elements); } public static RestxConfig of(Iterable<ConfigElement> configElements) { return new StdRestxConfig(configElements); } private final ImmutableMap<String, ConfigElement> elements; private StdRestxConfig(Iterable<ConfigElement> elements) { Map<String, ConfigElement> m = new LinkedHashMap<>(); for (ConfigElement element : elements) { ConfigElement curElement = m.get(element.getKey()); if (curElement == null) { m.put(element.getKey(), element); } else { if (isNullOrEmpty(curElement.getDoc()) && !isNullOrEmpty(element.getDoc())) { m.put(element.getKey(), curElement.withDoc(element.getDoc())); } } } this.elements = ImmutableMap.copyOf(m); } @Override public Iterable<ConfigElement> elements() { return elements.values(); } @Override public Optional<ConfigElement> getElement(String elementKey) { return Optional.fromNullable(elements.get(elementKey)); } @Override public Optional<String> getString(String elementKey) { ConfigElement element = elements.get(elementKey); if (element == null || isNullOrEmpty(element.getValue())) { return Optional.absent(); } return Optional.of(element.getValue()); } @Override public Optional<Integer> getInt(String elementKey) { ConfigElement element = elements.get(elementKey); if (element == null || isNullOrEmpty(element.getValue())) { return Optional.absent(); } try { return Optional.of(Integer.parseInt(element.getValue())); } catch (NumberFormatException e) { throw new RuntimeException("can't access " + element + " as int" + " (parse exception " + e.getMessage() + ")"); } } @Override public Optional<Long> getLong(String elementKey) { ConfigElement element = elements.get(elementKey); if (element == null || isNullOrEmpty(element.getValue())) { return Optional.absent(); } try { return Optional.of(Long.parseLong(element.getValue())); } catch (NumberFormatException e) { throw new RuntimeException("can't access " + element + " as long" + " (parse exception " + e.getMessage() + ")"); } } @Override public Optional<Boolean> getBoolean(String elementKey) { ConfigElement element = elements.get(elementKey); if (element == null || isNullOrEmpty(element.getValue())) { return Optional.absent(); } return Optional.of(TRUE_VALUES.contains(element.getValue().toLowerCase(Locale.ENGLISH))); } }