/* * Licensed 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 openbook.util; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * A set of static utility functions to read properties from file, manage properties with multi-part keys, * array style properties etc. * * @author Pinaki Poddar * */ public class PropertyHelper { /** * Filter the properties by the given name. * * @param name a part of the key * @param prefix if true, property key must begin with the given name. Otherwise, the key merely contains the * name to qualify. * * @return key-value pairs that match. */ public static Map<String,Object> filter(Map<String,Object> props, String name, boolean prefix, boolean includeArrays) { Map<String, Object> result = new HashMap<String, Object>(); for (String key : props.keySet()) { if (key == null) continue; boolean match = prefix ? key.startsWith(name) : (key.indexOf(name) != -1); if (match && !isArray(key)) { result.put(key, props.get(key)); } } if (includeArrays) { Map<String,List<Object>> arrayProperties = filterArrayKeys(props, name, prefix); result.putAll(arrayProperties); } return result; } /** * Select only those property keys which ends with an array marker such as <code>openjpa.DataCache[1]</code>. * The multiple values of the property is inserted into the resultant map as a List of object against the * original key. * <br> * For example, if the original map had three key-value pairs as * <LI><code>openjpa.DataCache[1]=true</code> * <LI><code>openjpa.DataCache[2]=false</code> * <LI><code>openjpa.DataCache[3]=default</code> * <br> * Then that will result into a single entry in the resultant Map under the key <code>openjpa.DataCache</code> * with a value as a List of three elements namely <code>{true, false, default}</code>. The array index values * are not significant other than they must all be different for the same base key. * * @param name part of the property key * @param prefix does the name must appear as a prefix? * * @return key-value pairs that match. */ public static Map<String,List<Object>> filterArrayKeys(Map<String,Object> props, String name, boolean prefix) { Map<String, List<Object>> result = new HashMap<String, List<Object>>(); for (String key : props.keySet()) { boolean match = prefix ? key.startsWith(name) : (key.indexOf(name) != -1); if (match && isArray(key)) { String realKey = removeArray(key); List<Object> values = result.get(realKey); if (values == null) { values = new ArrayList<Object>(); result.put(realKey, values); } values.add(props.get(key)); } } return result; } /** * Load properties from the given name resource. * The given named resource is first looked up as resource on the current thread's context * and if not found as a file input. * * @param resource name a of resource. * * @return empty properties if no resource found. */ public static Map<String,Object> load(String resource) { Properties p = new Properties(); try { InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); if (stream == null) { stream = new FileInputStream(resource); } p.load(stream); } catch (Exception e) { System.err.println("Error reading " + resource + " due to " + e); } return toMap(p); } /** * Affirm if the given resource is available either as a resource in the current thread's context classpath * or as a file. * */ public static boolean canFind(String resource) { if (resource == null) return false; InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); if (stream != null) return true; return new File(resource).exists(); } public static Map<String, Object> toMap(Properties p) { Map<String, Object> result = new HashMap<String, Object>(); for (Object k : p.keySet()) { result.put(k.toString(), p.get(k)); } return result; } /** * Overwrites any key-value pair in the given map for which a System property is available * @param original properties to be overwritten * @return the original property overwritten with System properties */ public static Map<String, Object> overwriteWithSystemProperties(Map<String, Object> original) { Properties properties = System.getProperties(); for (Object syskey : properties.keySet()) { if (original.containsKey(syskey)) { original.put(syskey.toString(), properties.get(syskey)); } } return original; } public static int getInteger(Map<String,Object> props, String key, int def) { int result = def; try { Object value = props.get(key); if (value != null) result = Integer.parseInt(value.toString()); } catch (NumberFormatException nfe) { } return result; } public static double getDouble(Map<String,Object> props, String key, double def) { double result = def; try { Object value = props.get(key); if (value != null) result = Double.parseDouble(value.toString()); } catch (NumberFormatException nfe) { } return result; } public static String getString(Map<String,Object> props, String key, String def) { Object value = props.get(key); if (value != null) return value.toString(); return def; } public static List<String> getStringList(Map<String,Object> props, String key) { return getStringList(props, key, Collections.EMPTY_LIST); } public static List<String> getStringList(Map<String,Object> props, String key, List<String> def) { Object value = props.get(key); if (value != null) return Arrays.asList(value.toString().split("\\,")); return def; } public static Map<String,String> getMap(Map<String,Object> props, String key) { return getMap(props, key, Collections.EMPTY_MAP); } public static Map<String,String> getMap(Map<String,Object> props, String key, Map<String,String> def) { List<String> pairs = getStringList(props, key); if (pairs == null || pairs.isEmpty()) return def; Map<String,String> result = new LinkedHashMap<String, String>(); for (String pair : pairs) { int index = pair.indexOf("->"); if (index != -1) { String name = pair.substring(0, index).trim(); String value = pair.substring(index + 2).trim(); result.put(name, value); } } return result; } /** * Affirms if the given string using array [] symbol at the end. * * @param key a string to check for array symbol. */ private static boolean isArray(String key) { if (key == null || key.length() < 3 || !key.endsWith("]")) return false; int i = key.indexOf("["); if (i == -1 || i != key.lastIndexOf("[")) return false; String index = key.substring(i+1,key.length()-1); try { Integer.parseInt(index); } catch (NumberFormatException e) { System.err.println("Bad index " + index + " in " + key); return false; } return true; } private static String removeArray(String key) { int i = key.indexOf("["); return key.substring(0,i); } public static Set<String> getSubsectionKeys(Set<String> keys, String section) { String prefix = asPrefix(section); Set<String> subsections = new HashSet<String>(); for (String key : keys) { if (key.startsWith(prefix)) { subsections.add(prefix + getPrefix(key.substring(prefix.length()))); } } return subsections; } private static final String DOT = "."; private static String asPrefix(String s) { if (s.endsWith(DOT)) return s; return s + DOT; } public static String getPrefix(String s) { int i = s.indexOf(DOT); return (i == -1) ? s : s.substring(0, i); } /** * Get the portion of the given map whose key has the given section at prefix. * * @param props a set of name-value pair * @param section a string representing section of a key * * @return a new map with only the keys that starts with the given section. */ public static Map<String,Object> getSection(Map<String,Object> props, String section) { return getSection(props, section, false); } /** * Get the portion of the given map whose key has the given section at prefix. * * @param props a set of name-value pair * @param section a string representing section of a key * @param retain if true the key of resultant map is same as the original map. Otherwise * the resultant map keys are without the section prefix. * * @return the map with only the keys that starts with the given section. */ public static Map<String,Object> getSection(Map<String,Object> props, String section, boolean retain) { Map<String,Object> result = new HashMap<String, Object>(props); Set<String> keys = props.keySet(); String prefix = asPrefix(section); for (String key : keys) { if (key.startsWith(prefix)) { String newKey = retain ? key : key.substring(prefix.length()); result.put(newKey, props.get(key)); } } return result; } }