⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 extendedproperties.java

📁 sea是一个基于seda模式的实现。这个设计模式将系统分为很多stage。每个stage分布不同的任务(基于线程池)。通过任务流的方式提高系统的效率。
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
// copied from commons-collections-3.1
/*
 *  Copyright 2001-2004 The Apache Software Foundation
 *
 *  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 gov.lbl.dsd.sea.nio.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * This class extends normal Java properties by adding the possibility
 * to use the same key many times concatenating the value strings
 * instead of overwriting them.
 * <p>
 * <b>Please consider using the <code>PropertiesConfiguration</code> class in
 * Commons-Configuration as soon as it is released.</b>
 * <p>
 * The Extended Properties syntax is explained here:
 *
 * <ul>
 *  <li>
 *   Each property has the syntax <code>key = value</code>
 *  </li>
 *  <li>
 *   The <i>key</i> may use any character but the equal sign '='.
 *  </li>
 *  <li>
 *   <i>value</i> may be separated on different lines if a backslash
 *   is placed at the end of the line that continues below.
 *  </li>
 *  <li>
 *   If <i>value</i> is a list of strings, each token is separated
 *   by a comma ','.
 *  </li>
 *  <li>
 *   Commas in each token are escaped placing a backslash right before
 *   the comma.
 *  </li>
 *  <li>
 *   Backslashes are escaped by using two consecutive backslashes i.e. \\
 *  </li>
 *  <li>
 *   If a <i>key</i> is used more than once, the values are appended
 *   like if they were on the same line separated with commas.
 *  </li>
 *  <li>
 *   Blank lines and lines starting with character '#' are skipped.
 *  </li>
 *  <li>
 *   If a property is named "include" (or whatever is defined by
 *   setInclude() and getInclude() and the value of that property is
 *   the full path to a file on disk, that file will be included into
 *   the ConfigurationsRepository. You can also pull in files relative
 *   to the parent configuration file. So if you have something
 *   like the following:
 *
 *   include = additional.properties
 *
 *   Then "additional.properties" is expected to be in the same
 *   directory as the parent configuration file.
 * 
 *   Duplicate name values will be replaced, so be careful.
 *
 *  </li>
 * </ul>
 *
 * <p>Here is an example of a valid extended properties file:
 *
 * <p><pre>
 *      # lines starting with # are comments
 *
 *      # This is the simplest property
 *      key = value
 *
 *      # A long property may be separated on multiple lines
 *      longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
 *                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 *
 *      # This is a property with many tokens
 *      tokens_on_a_line = first token, second token
 *
 *      # This sequence generates exactly the same result
 *      tokens_on_multiple_lines = first token
 *      tokens_on_multiple_lines = second token
 *
 *      # commas may be escaped in tokens
 *      commas.escaped = Hi\, what'up?
 * </pre>
 *
 * <p><b>NOTE</b>: this class has <b>not</b> been written for
 * performance nor low memory usage.  In fact, it's way slower than it
 * could be and generates too much memory garbage.  But since
 * performance is not an issue during intialization (and there is not
 * much time to improve it), I wrote it this way.  If you don't like
 * it, go ahead and tune it up!
 *
 * @since Commons Collections 1.0
 * @version $Revision: 1.1 $ $Date: 2004/07/21 20:54:43 $
 * 
 * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a>
 * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
 * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a>
 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
 * @author Janek Bogucki
 * @author Mohan Kishore
 * @author Stephen Colebourne
 */
public class ExtendedProperties extends Hashtable {
    
    /**
     * Default configurations repository.
     */
    private ExtendedProperties defaults;

    /**
     * The file connected to this repository (holding comments and
     * such).
     *
     * @serial
     */
    protected String file;

    /**
     * Base path of the configuration file used to create
     * this ExtendedProperties object.
     */
    protected String basePath;

    /**
     * File separator.
     */
    protected String fileSeparator = System.getProperty("file.separator");

    /**
     * Has this configuration been intialized.
     */
    protected boolean isInitialized = false;

    /**
     * This is the name of the property that can point to other
     * properties file for including other properties files.
     */
    protected static String include = "include";

    /**
     * These are the keys in the order they listed
     * in the configuration file. This is useful when
     * you wish to perform operations with configuration
     * information in a particular order.
     */
    protected ArrayList keysAsListed = new ArrayList();

    protected final static String START_TOKEN="${";
    protected final static String END_TOKEN="}";


    /**
     * Interpolate key names to handle ${key} stuff
     *
     * @param base string to interpolate
     * @return returns the key name with the ${key} substituted
     */
    protected String interpolate(String base) {
        // COPIED from [configuration] 2003-12-29
        return (interpolateHelper(base, null));
    }

    /**
     * Recursive handler for multiple levels of interpolation.
     *
     * When called the first time, priorVariables should be null.
     *
     * @param base string with the ${key} variables
     * @param priorVariables serves two purposes: to allow checking for
     * loops, and creating a meaningful exception message should a loop
     * occur.  It's 0'th element will be set to the value of base from
     * the first call.  All subsequent interpolated variables are added
     * afterward.
     *
     * @return the string with the interpolation taken care of
     */
    protected String interpolateHelper(String base, List priorVariables) {
        // COPIED from [configuration] 2003-12-29
        if (base == null) {
            return null;
        }

        // on the first call initialize priorVariables
        // and add base as the first element
        if (priorVariables == null) {
            priorVariables = new ArrayList();
            priorVariables.add(base);
        }

        int begin = -1;
        int end = -1;
        int prec = 0 - END_TOKEN.length();
        String variable = null;
        StringBuffer result = new StringBuffer();

        // FIXME: we should probably allow the escaping of the start token
        while (((begin = base.indexOf(START_TOKEN, prec + END_TOKEN.length())) > -1)
            && ((end = base.indexOf(END_TOKEN, begin)) > -1)) {
            result.append(base.substring(prec + END_TOKEN.length(), begin));
            variable = base.substring(begin + START_TOKEN.length(), end);

            // if we've got a loop, create a useful exception message and throw
            if (priorVariables.contains(variable)) {
                String initialBase = priorVariables.remove(0).toString();
                priorVariables.add(variable);
                StringBuffer priorVariableSb = new StringBuffer();

                // create a nice trace of interpolated variables like so:
                // var1->var2->var3
                for (Iterator it = priorVariables.iterator(); it.hasNext();) {
                    priorVariableSb.append(it.next());
                    if (it.hasNext()) {
                        priorVariableSb.append("->");
                    }
                }

                throw new IllegalStateException(
                    "infinite loop in property interpolation of " + initialBase + ": " + priorVariableSb.toString());
            }
            // otherwise, add this variable to the interpolation list.
            else {
                priorVariables.add(variable);
            }

            //QUESTION: getProperty or getPropertyDirect
            Object value = getProperty(variable);
            if (value != null) {
                result.append(interpolateHelper(value.toString(), priorVariables));

                // pop the interpolated variable off the stack
                // this maintains priorVariables correctness for
                // properties with multiple interpolations, e.g.
                // prop.name=${some.other.prop1}/blahblah/${some.other.prop2}
                priorVariables.remove(priorVariables.size() - 1);
            } else if (defaults != null && defaults.getString(variable, null) != null) {
                result.append(defaults.getString(variable));
            } else {
                //variable not defined - so put it back in the value
                result.append(START_TOKEN).append(variable).append(END_TOKEN);
            }
            prec = end;
        }
        result.append(base.substring(prec + END_TOKEN.length(), base.length()));

        return result.toString();
    }
    
    /**
     * Inserts a backslash before every comma and backslash. 
     */
    private static String escape(String s) {
        StringBuffer buf = new StringBuffer(s);
        for (int i = 0; i < buf.length(); i++) {
            char c = buf.charAt(i);
            if (c == ',' || c == '\\') {
                buf.insert(i, '\\');
                i++;
            }
        }
        return buf.toString();
    }
    
    /**
     * Removes a backslash from every pair of backslashes. 
     */
    private static String unescape(String s) {
        StringBuffer buf = new StringBuffer(s);
        for (int i = 0; i < buf.length() - 1; i++) {
            char c1 = buf.charAt(i);
            char c2 = buf.charAt(i + 1);
            if (c1 == '\\' && c2 == '\\') {
                buf.deleteCharAt(i);
            }
        }
        return buf.toString();
    }

    /**
     * Counts the number of successive times 'ch' appears in the
     * 'line' before the position indicated by the 'index'.
     */
    private static int countPreceding(String line, int index, char ch) {
        int i;
        for (i = index - 1; i >= 0; i--) {
            if (line.charAt(i) != ch) {
                break;
            }
        }
        return index - 1 - i;
    }

    /**
     * Checks if the line ends with odd number of backslashes 
     */
    private static boolean endsWithSlash(String line) {
        if (!line.endsWith("\\")) {
            return false;
        }
        return (countPreceding(line, line.length() - 1, '\\') % 2 == 0);
    }

    /**
     * This class is used to read properties lines.  These lines do
     * not terminate with new-line chars but rather when there is no
     * backslash sign a the end of the line.  This is used to
     * concatenate multiple lines for readability.
     */
    static class PropertiesReader extends LineNumberReader {
        /**
         * Constructor.
         *
         * @param reader A Reader.
         */
        public PropertiesReader(Reader reader) {
            super(reader);
        }

        /**
         * Read a property.
         *
         * @return a String property
         * @throws IOException if there is difficulty reading the source.
         */
        public String readProperty() throws IOException {
            StringBuffer buffer = new StringBuffer();

            try {
                while (true) {
                    String line = readLine().trim();
                    if ((line.length() != 0) && (line.charAt(0) != '#')) {
                        if (endsWithSlash(line)) {
                            line = line.substring(0, line.length() - 1);
                            buffer.append(line);
                        } else {
                            buffer.append(line);
                            break;
                        }
                    }
                }
            } catch (NullPointerException ex) {
                return null;
            }

            return buffer.toString();
        }
    }

    /**
     * This class divides into tokens a property value.  Token
     * separator is "," but commas into the property value are escaped
     * using the backslash in front.
     */
    static class PropertiesTokenizer extends StringTokenizer {
        /**
         * The property delimiter used while parsing (a comma).
         */
        static final String DELIMITER = ",";

        /**
         * Constructor.
         *
         * @param string A String.
         */
        public PropertiesTokenizer(String string) {
            super(string, DELIMITER);
        }

        /**
         * Check whether the object has more tokens.
         *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -