Java tutorial
/* * 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.apache.phoenix.util; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; /** * * Read-only properties that avoids unnecessary synchronization in * java.util.Properties. * * * @since 1.2.2 */ public class ReadOnlyProps implements Iterable<Entry<String, String>> { private static final Logger logger = LoggerFactory.getLogger(ReadOnlyProps.class); public static final ReadOnlyProps EMPTY_PROPS = new ReadOnlyProps(); private final Map<String, String> props; public ReadOnlyProps(ReadOnlyProps defaultProps, Iterator<Entry<String, String>> iterator) { Map<String, String> map = new HashMap<String, String>(defaultProps.asMap()); while (iterator.hasNext()) { Entry<String, String> entry = iterator.next(); map.put(entry.getKey(), entry.getValue()); } this.props = ImmutableMap.copyOf(map); } public ReadOnlyProps(Iterator<Entry<String, String>> iterator) { this(EMPTY_PROPS, iterator); } private ReadOnlyProps() { this.props = Collections.emptyMap(); } public ReadOnlyProps(Map<String, String> props) { this.props = ImmutableMap.copyOf(props); } private ReadOnlyProps(ReadOnlyProps defaultProps, Properties overrides) { Map<String, String> combinedProps = Maps .newHashMapWithExpectedSize(defaultProps.props.size() + overrides.size()); combinedProps.putAll(defaultProps.props); for (Entry<Object, Object> entry : overrides.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); combinedProps.put(key, value); } this.props = ImmutableMap.copyOf(combinedProps); } private static Pattern varPat = Pattern.compile("\\$\\{[^\\}\\$\u0020]+\\}"); private static int MAX_SUBST = 20; private String substituteVars(String expr) { if (expr == null) { return null; } Matcher match = varPat.matcher(""); String eval = expr; for (int s = 0; s < MAX_SUBST; s++) { match.reset(eval); if (!match.find()) { return eval; } String var = match.group(); var = var.substring(2, var.length() - 1); // remove ${ .. } String val = null; try { val = System.getProperty(var); } catch (SecurityException se) { } if (val == null) { val = getRaw(var); } if (val == null) { return eval; // return literal ${var}: var is unbound } // substitute eval = eval.substring(0, match.start()) + val + eval.substring(match.end()); } throw new IllegalStateException("Variable substitution depth too large: " + MAX_SUBST + " " + expr); } /** * Get the value of the <code>name</code> property, without doing * <a href="#VariableExpansion">variable expansion</a>. * * @param name the property name. * @return the value of the <code>name</code> property, * or null if no such property exists. */ public String getRaw(String name) { return props.get(name); } public String getRaw(String name, String defaultValue) { String value = getRaw(name); if (value == null) { return defaultValue; } return value; } /** * Get the value of the <code>name</code> property. If no such property * exists, then <code>defaultValue</code> is returned. * * @param name property name. * @param defaultValue default value. * @return property value, or <code>defaultValue</code> if the property * doesn't exist. */ public String get(String name, String defaultValue) { return substituteVars(getRaw(name, defaultValue)); } /** * Get the value of the <code>name</code> property, <code>null</code> if * no such property exists. * * Values are processed for <a href="#VariableExpansion">variable expansion</a> * before being returned. * * @param name the property name. * @return the value of the <code>name</code> property, * or null if no such property exists. */ public String get(String name) { return substituteVars(getRaw(name)); } private String getHexDigits(String value) { boolean negative = false; String str = value; String hexString = null; if (value.startsWith("-")) { negative = true; str = value.substring(1); } if (str.startsWith("0x") || str.startsWith("0X")) { hexString = str.substring(2); if (negative) { hexString = "-" + hexString; } return hexString; } return null; } /** * Get the value of the <code>name</code> property as a <code>boolean</code>. * If no such property is specified, or if the specified value is not a valid * <code>boolean</code>, then <code>defaultValue</code> is returned. * * @param name property name. * @param defaultValue default value. * @return property value as a <code>boolean</code>, * or <code>defaultValue</code>. */ public boolean getBoolean(String name, boolean defaultValue) { String valueString = get(name); if ("true".equals(valueString)) return true; else if ("false".equals(valueString)) return false; else return defaultValue; } /** * Get the value of the <code>name</code> property as an <code>int</code>. * * If no such property exists, or if the specified value is not a valid * <code>int</code>, then <code>defaultValue</code> is returned. * * @param name property name. * @param defaultValue default value. * @return property value as an <code>int</code>, * or <code>defaultValue</code>. */ public int getInt(String name, int defaultValue) { String valueString = get(name); if (valueString == null) return defaultValue; try { String hexString = getHexDigits(valueString); if (hexString != null) { return Integer.parseInt(hexString, 16); } return Integer.parseInt(valueString); } catch (NumberFormatException e) { return defaultValue; } } /** * Get the value of the <code>name</code> property as a <code>long</code>. * If no such property is specified, or if the specified value is not a valid * <code>long</code>, then <code>defaultValue</code> is returned. * * @param name property name. * @param defaultValue default value. * @return property value as a <code>long</code>, * or <code>defaultValue</code>. */ public long getLong(String name, long defaultValue) { String valueString = get(name); if (valueString == null) return defaultValue; try { String hexString = getHexDigits(valueString); if (hexString != null) { return Long.parseLong(hexString, 16); } return Long.parseLong(valueString); } catch (NumberFormatException e) { return defaultValue; } } /** * Get the value of the <code>name</code> property as a <code>float</code>. * If no such property is specified, or if the specified value is not a valid * <code>float</code>, then <code>defaultValue</code> is returned. * * @param name property name. * @param defaultValue default value. * @return property value as a <code>float</code>, * or <code>defaultValue</code>. */ public float getFloat(String name, float defaultValue) { String valueString = get(name); if (valueString == null) return defaultValue; try { return Float.parseFloat(valueString); } catch (NumberFormatException e) { return defaultValue; } } /** * Get the properties as a <code>Map<String,String></code> * * @return Map<String,String> */ public Map<String, String> asMap() { return props; } @Override public Iterator<Entry<String, String>> iterator() { return props.entrySet().iterator(); } public boolean isEmpty() { return props.isEmpty(); } /** * Constructs new map only if necessary for adding the override properties. * @param overrides Map of properties to override current properties. * @return new ReadOnlyProps if in applying the overrides there are * modifications to the current underlying Map, otherwise returns this. */ public ReadOnlyProps addAll(Properties overrides) { for (Entry<Object, Object> entry : overrides.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); String oldValue = props.get(key); if (!Objects.equal(oldValue, value)) { if (logger.isDebugEnabled()) logger.debug("Creating new ReadOnlyProps due to " + key + " with " + oldValue + "!=" + value); return new ReadOnlyProps(this, overrides); } } return this; } }