/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.collect.io; import java.io.UncheckedIOException; import java.util.Map; import java.util.Properties; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.io.CharSource; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.collect.Unchecked; /** * A properties file. * <p> * Represents a properties file together with the ability to parse it from a {@link CharSource}. * This is similar to {@link Properties} but with a simpler format and without extending {@link Map}. * Duplicate keys are allowed and handled. * <p> * The properties file format used here is deliberately simple. * There is only one element - key-value pairs. * <p> * The key is separated from the value using the '=' symbol. * Duplicate keys are allowed. * For example 'key = value'. * The equals sign and value may be omitted, in which case the value is an empty string. * <p> * Keys and values are trimmed. * Blank lines are ignored. * Whole line comments begin with hash '#' or semicolon ';'. * No escape format is available. * Lookup is case sensitive. * <p> * This example explains the format: * <pre> * # line comment * key = value * month = January * </pre> * <p> * The aim of this class is to parse the basic format. * Interpolation of variables is not supported. */ public final class PropertiesFile { /** * The key-value pairs. */ private final PropertySet keyValueMap; //------------------------------------------------------------------------- /** * Parses the specified source as a properties file. * <p> * This parses the specified character source expecting a properties file format. * The resulting instance can be queried for each key and value. * * @param source the properties file resource * @return the properties file * @throws UncheckedIOException if an IO exception occurs * @throws IllegalArgumentException if the file cannot be parsed */ public static PropertiesFile of(CharSource source) { ArgChecker.notNull(source, "source"); ImmutableList<String> lines = Unchecked.wrap(() -> source.readLines()); PropertySet keyValues = parse(lines); return new PropertiesFile(keyValues); } // parses the properties file format private static PropertySet parse(ImmutableList<String> lines) { // cannot use ArrayListMultiMap as it does not retain the order of the keys // whereas ImmutableListMultimap does retain the order of the keys ImmutableListMultimap.Builder<String, String> parsed = ImmutableListMultimap.builder(); int lineNum = 0; for (String line : lines) { lineNum++; line = line.trim(); if (line.length() == 0 || line.startsWith("#") || line.startsWith(";")) { continue; } int equalsPosition = line.indexOf('='); String key = (equalsPosition < 0 ? line.trim() : line.substring(0, equalsPosition).trim()); String value = (equalsPosition < 0 ? "" : line.substring(equalsPosition + 1).trim()); if (key.length() == 0) { throw new IllegalArgumentException("Invalid properties file, empty key, line " + lineNum); } parsed.put(key, value); } return PropertySet.of(parsed.build()); } //------------------------------------------------------------------------- /** * Obtains an instance from a key-value property set. * * @param keyValueMap the key-value property set * @return the properties file */ public static PropertiesFile of(PropertySet keyValueMap) { ArgChecker.notNull(keyValueMap, "keyValueMap"); return new PropertiesFile(keyValueMap); } //------------------------------------------------------------------------- /** * Restricted constructor. * * @param keyValueMap the values */ private PropertiesFile(PropertySet keyValueMap) { this.keyValueMap = keyValueMap; } //------------------------------------------------------------------------- /** * Gets all the key-value properties of this file. * <p> * The map of key-value properties is exposed by this method. * * @return the key-value properties */ public PropertySet getProperties() { return keyValueMap; } //------------------------------------------------------------------------- /** * Checks if this file equals another. * <p> * The comparison checks the content. * * @param obj the other section, null returns false * @return true if equal */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof PropertiesFile) { return keyValueMap.equals(((PropertiesFile) obj).keyValueMap); } return false; } /** * Returns a suitable hash code for the file. * * @return the hash code */ @Override public int hashCode() { return keyValueMap.hashCode(); } /** * Returns a string describing the file. * * @return the descriptive string */ @Override public String toString() { return keyValueMap.toString(); } }