1c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort/*
2c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * Copyright (C) 2009 The Android Open Source Project
3c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort *
4c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * Licensed under the Apache License, Version 2.0 (the "License");
5c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * you may not use this file except in compliance with the License.
6c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * You may obtain a copy of the License at
7c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort *
8c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort *      http://www.apache.org/licenses/LICENSE-2.0
9c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort *
10c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * Unless required by applicable law or agreed to in writing, software
11c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * distributed under the License is distributed on an "AS IS" BASIS,
12c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * See the License for the specific language governing permissions and
14c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * limitations under the License.
15c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort */
16c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
17c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortpackage com.android.internal.util;
18c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
19c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.io.IOException;
20c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.io.Reader;
21c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.io.StreamTokenizer;
22c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.util.HashMap;
23c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.util.Map;
24c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortimport java.util.regex.Pattern;
25c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
26c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort/**
27c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * A {@code Map} that publishes a set of typed properties, defined by
28c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort * zero or more {@code Reader}s containing textual definitions and assignments.
29c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort */
30c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bortpublic class TypedProperties extends HashMap<String, Object> {
31c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
32c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Instantiates a {@link java.io.StreamTokenizer} and sets its syntax tables
33c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * appropriately for the {@code TypedProperties} file format.
34c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
35c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param r The {@code Reader} that the {@code StreamTokenizer} will read from
36c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return a newly-created and initialized {@code StreamTokenizer}
37c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
38c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static StreamTokenizer initTokenizer(Reader r) {
39c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        StreamTokenizer st = new StreamTokenizer(r);
40c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
41c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        // Treat everything we don't specify as "ordinary".
42c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.resetSyntax();
43c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
44c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        /* The only non-quoted-string words we'll be reading are:
45c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         * - property names: [._$a-zA-Z0-9]
465b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort         * - type names: [a-zS]
47c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         * - number literals: [-0-9.eExXA-Za-z]  ('x' for 0xNNN hex literals. "NaN", "Infinity")
48c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         * - "true" or "false" (case insensitive): [a-zA-Z]
49c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         */
50c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('0', '9');
51c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('A', 'Z');
52c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('a', 'z');
53c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('_', '_');
54c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('$', '$');
55c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('.', '.');
56c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('-', '-');
57c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.wordChars('+', '+');
58c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
59c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        // Single-character tokens
60c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.ordinaryChar('=');
61c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
62c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        // Other special characters
63c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.whitespaceChars(' ', ' ');
64c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.whitespaceChars('\t', '\t');
655b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        st.whitespaceChars('\n', '\n');
665b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        st.whitespaceChars('\r', '\r');
67c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        st.quoteChar('"');
68c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
695b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        // Java-style comments
705b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        st.slashStarComments(true);
715b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        st.slashSlashComments(true);
72c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
73c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return st;
74c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
75c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
76c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
77c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
78c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * An unchecked exception that is thrown when encountering a syntax
79c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * or semantic error in the input.
80c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
81c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public static class ParseException extends IllegalArgumentException {
82c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        ParseException(StreamTokenizer state, String expected) {
83c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            super("expected " + expected + ", saw " + state.toString());
84c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
85c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
86c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
87c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    // A sentinel instance used to indicate a null string.
88c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final String NULL_STRING = new String("<TypedProperties:NULL_STRING>");
89c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
90c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    // Constants used to represent the supported types.
91c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_UNSET = 'x';
92c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_BOOLEAN = 'Z';
93c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_BYTE = 'I' | 1 << 8;
94c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    // TYPE_CHAR: character literal syntax not supported; use short.
95c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_SHORT = 'I' | 2 << 8;
96c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_INT = 'I' | 4 << 8;
97c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_LONG = 'I' | 8 << 8;
98c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_FLOAT = 'F' | 4 << 8;
99c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_DOUBLE = 'F' | 8 << 8;
100c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_STRING = 'L' | 's' << 8;
101c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static final int TYPE_ERROR = -1;
102c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
103c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
1045b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     * Converts a string to an internal type constant.
105c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
106c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param typeName the type name to convert
107c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the type constant that corresponds to {@code typeName},
108c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *         or {@code TYPE_ERROR} if the type is unknown
109c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
110c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static int interpretType(String typeName) {
1115b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        if ("unset".equals(typeName)) {
112c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_UNSET;
1135b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("boolean".equals(typeName)) {
114c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_BOOLEAN;
1155b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("byte".equals(typeName)) {
116c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_BYTE;
1175b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("short".equals(typeName)) {
118c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_SHORT;
1195b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("int".equals(typeName)) {
120c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_INT;
1215b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("long".equals(typeName)) {
122c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_LONG;
1235b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("float".equals(typeName)) {
124c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_FLOAT;
1255b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("double".equals(typeName)) {
126c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_DOUBLE;
1275b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort        } else if ("String".equals(typeName)) {
128c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return TYPE_STRING;
129c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
130c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return TYPE_ERROR;
131c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
132c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
133c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
134c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Parses the data in the reader.
135c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
136c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param r The {@code Reader} containing input data to parse
137c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param map The {@code Map} to insert parameter values into
138c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws ParseException if the input data is malformed
139c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws IOException if there is a problem reading from the {@code Reader}
140c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
141c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static void parse(Reader r, Map<String, Object> map) throws ParseException, IOException {
142c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        final StreamTokenizer st = initTokenizer(r);
143c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
144c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        /* A property name must be a valid fully-qualified class + package name.
145c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         * We don't support Unicode, though.
146c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort         */
147c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        final String identifierPattern = "[a-zA-Z_$][0-9a-zA-Z_$]*";
148c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        final Pattern propertyNamePattern =
149c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            Pattern.compile("(" + identifierPattern + "\\.)*" + identifierPattern);
150c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
151c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
152c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        while (true) {
153c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            int token;
154c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
1555b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            // Read the next token, which is either the type or EOF.
1565b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            token = st.nextToken();
1575b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if (token == StreamTokenizer.TT_EOF) {
158c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                break;
1595b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            }
1605b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if (token != StreamTokenizer.TT_WORD) {
1615b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                throw new ParseException(st, "type name");
1625b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            }
1635b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            final int type = interpretType(st.sval);
1645b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if (type == TYPE_ERROR) {
1655b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                throw new ParseException(st, "valid type name");
1665b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            }
1675b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            st.sval = null;
1685b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort
1695b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if (type == TYPE_UNSET) {
1705b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                // Expect '('.
1715b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                token = st.nextToken();
1725b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                if (token != '(') {
1735b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                    throw new ParseException(st, "'('");
1745b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                }
175c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
176c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
177c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            // Read the property name.
178c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            token = st.nextToken();
179c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (token != StreamTokenizer.TT_WORD) {
180c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "property name");
181c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
182c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            final String propertyName = st.sval;
183c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (!propertyNamePattern.matcher(propertyName).matches()) {
184c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "valid property name");
185c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
186c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            st.sval = null;
187c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
188c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (type == TYPE_UNSET) {
1895b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                // Expect ')'.
1905b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                token = st.nextToken();
1915b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                if (token != ')') {
1925b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                    throw new ParseException(st, "')'");
1935b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                }
194c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                map.remove(propertyName);
195c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            } else {
196c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                // Expect '='.
197c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                token = st.nextToken();
198c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (token != '=') {
199c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    throw new ParseException(st, "'='");
200c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
201c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
202c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                // Read a value of the appropriate type, and insert into the map.
203c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                final Object value = parseValue(st, type);
204c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                final Object oldValue = map.remove(propertyName);
205c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (oldValue != null) {
206c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    // TODO: catch the case where a string is set to null and then
207c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    //       the same property is defined with a different type.
208c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    if (value.getClass() != oldValue.getClass()) {
209c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                        throw new ParseException(st,
210c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                            "(property previously declared as a different type)");
211c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    }
212c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
213c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                map.put(propertyName, value);
214c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
215c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
2165b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            // Expect ';'.
2175b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            token = st.nextToken();
2185b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if (token != ';') {
2195b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort                throw new ParseException(st, "';'");
2205b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            }
221c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
222c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
223c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
224c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
225c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Parses the next token in the StreamTokenizer as the specified type.
226c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
227c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param st The token source
228c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param type The type to interpret next token as
229c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return a Boolean, Number subclass, or String representing the value.
230c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *         Null strings are represented by the String instance NULL_STRING
231c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws IOException if there is a problem reading from the {@code StreamTokenizer}
232c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
233c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    static Object parseValue(StreamTokenizer st, final int type) throws IOException {
234c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        final int token = st.nextToken();
235c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
236c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (type == TYPE_BOOLEAN) {
237c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (token != StreamTokenizer.TT_WORD) {
238c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "boolean constant");
239c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
240c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
2415b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            if ("true".equals(st.sval)) {
242c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return Boolean.TRUE;
2435b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            } else if ("false".equals(st.sval)) {
244c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return Boolean.FALSE;
245c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
246c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
247c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            throw new ParseException(st, "boolean constant");
248c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        } else if ((type & 0xff) == 'I') {
249c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (token != StreamTokenizer.TT_WORD) {
250c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "integer constant");
251c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
252c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
253c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            /* Parse the string.  Long.decode() handles C-style integer constants
254c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             * ("0x" -> hex, "0" -> octal).  It also treats numbers with a prefix of "#" as
255c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             * hex, but our syntax intentionally does not list '#' as a word character.
256c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             */
257c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            long value;
258c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            try {
259c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                value = Long.decode(st.sval);
260c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            } catch (NumberFormatException ex) {
261c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "integer constant");
262c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
263c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
264c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            // Ensure that the type can hold this value, and return.
265c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            int width = (type >> 8) & 0xff;
266c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            switch (width) {
267c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            case 1:
268c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
269c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    throw new ParseException(st, "8-bit integer constant");
270c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
271c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Byte((byte)value);
272c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            case 2:
273c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
274c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    throw new ParseException(st, "16-bit integer constant");
275c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
276c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Short((short)value);
277c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            case 4:
278c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
279c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    throw new ParseException(st, "32-bit integer constant");
280c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
281c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Integer((int)value);
282c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            case 8:
283c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (value < Long.MIN_VALUE || value > Long.MAX_VALUE) {
284c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    throw new ParseException(st, "64-bit integer constant");
285c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
286c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Long(value);
287c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            default:
288c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new IllegalStateException(
289c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    "Internal error; unexpected integer type width " + width);
290c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
291c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        } else if ((type & 0xff) == 'F') {
292c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (token != StreamTokenizer.TT_WORD) {
293c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "float constant");
294c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
295c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
296c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            // Parse the string.
297c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            /* TODO: Maybe just parse as float or double, losing precision if necessary.
298c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             *       Parsing as double and converting to float can change the value
299c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             *       compared to just parsing as float.
300c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort             */
301c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            double value;
302c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            try {
303c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                /* TODO: detect if the string representation loses precision
304c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                 *       when being converted to a double.
305c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                 */
306c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                value = Double.parseDouble(st.sval);
307c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            } catch (NumberFormatException ex) {
308c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                throw new ParseException(st, "float constant");
309c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
310c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
311c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            // Ensure that the type can hold this value, and return.
312c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (((type >> 8) & 0xff) == 4) {
313c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                // This property is a float; make sure the value fits.
314c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                double absValue = Math.abs(value);
315c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                if (absValue != 0.0 && !Double.isInfinite(value) && !Double.isNaN(value)) {
316c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    if (absValue < Float.MIN_VALUE || absValue > Float.MAX_VALUE) {
317c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                        throw new ParseException(st, "32-bit float constant");
318c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                    }
319c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                }
320c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Float((float)value);
321c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            } else {
322c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                // This property is a double; no need to truncate.
323c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return new Double(value);
324c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
325c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        } else if (type == TYPE_STRING) {
326c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            // Expect a quoted string or the word "null".
327c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            if (token == '"') {
328c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return st.sval;
3295b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort            } else if (token == StreamTokenizer.TT_WORD && "null".equals(st.sval)) {
330c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                return NULL_STRING;
331c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            }
332c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            throw new ParseException(st, "double-quoted string or 'null'");
333c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
334c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
335c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new IllegalStateException("Internal error; unknown type " + type);
336c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
337c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
338c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
339c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
340c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Creates an empty TypedProperties instance.
341c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
342c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public TypedProperties() {
343c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        super();
344c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
345c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
346c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
347c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Loads zero or more properties from the specified Reader.
348c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Properties that have already been loaded are preserved unless
349c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * the new Reader overrides or unsets earlier values for the
350c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * same properties.
3515b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     * <p>
352c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * File syntax:
3535b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     * <blockquote>
3545b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <tt>
3555b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     &lt;type&gt; &lt;property-name&gt; = &lt;value&gt; ;
3565b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <br />
3575b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     unset ( &lt;property-name&gt; ) ;
3585b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     </tt>
3595b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
3605b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     "//" comments everything until the end of the line.
3615b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     "/&#2a;" comments everything until the next appearance of "&#2a;/".
3625b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
363c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *     Blank lines are ignored.
3645b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
3655b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     The only required whitespace is between the type and
3665b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     the property name.
3675b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
3685b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     &lt;type&gt; is one of {boolean, byte, short, int, long,
3695b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     float, double, String}, and is case-sensitive.
3705b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
371c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *     &lt;property-name&gt; is a valid fully-qualified class name
372c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *     (one or more valid identifiers separated by dot characters).
3735b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
374c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *     &lt;value&gt; depends on the type:
3755b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <ul>
3765b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <li> boolean: one of {true, false} (case-sensitive)
3775b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <li> byte, short, int, long: a valid Java integer constant
3785b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          (including non-base-10 constants like 0xabc and 074)
3795b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          whose value does not overflow the type.  NOTE: these are
3805b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          interpreted as Java integer values, so they are all signed.
3815b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <li> float, double: a valid Java floating-point constant.
3825b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          If the type is float, the value must fit in 32 bits.
3835b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <li> String: a double-quoted string value, or the word {@code null}.
3845b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          NOTE: the contents of the string must be 7-bit clean ASCII;
3855b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *          C-style octal escapes are recognized, but Unicode escapes are not.
3865b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     </ul>
3875b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     <p>
3885b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     Passing a property-name to {@code unset()} will unset the property,
3895b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     removing its value and type information, as if it had never been
3905b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     *     defined.
3915b6f8d865d03f37c8c3a9397ca693ac671f39df7Dave Bort     * </blockquote>
392c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
393c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param r The Reader to load properties from
394c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws IOException if an error occurs when reading the data
395c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws IllegalArgumentException if the data is malformed
396c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
397c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public void load(Reader r) throws IOException {
398c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        parse(r, this);
399c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
400c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
401c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    @Override
402c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public Object get(Object key) {
403c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(key);
404c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == NULL_STRING) {
405c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return null;
406c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
407c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return value;
408c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
409c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
410c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /*
411c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Getters with explicit defaults
412c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
413c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
414c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
4154e8620f868e2490782ebb960404140ea9482c91dBen Dodson     * An unchecked exception that is thrown if a {@code get<TYPE>()} method
416c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * is used to retrieve a parameter whose type does not match the method name.
417c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
418c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public static class TypeException extends IllegalArgumentException {
419c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        TypeException(String property, Object value, String requestedType) {
420c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            super(property + " has type " + value.getClass().getName() +
421c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort                ", not " + requestedType);
422c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
423c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
424c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
425c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
426c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a boolean property, or the default if the property
427c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
428c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
429c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
430c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
431c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
432c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a boolean
433c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
434c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public boolean getBoolean(String property, boolean def) {
435c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
436c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
437c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
438c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
439c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Boolean) {
440c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Boolean)value).booleanValue();
441c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
442c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "boolean");
443c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
444c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
445c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
446c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a byte property, or the default if the property
447c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
448c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
449c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
450c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
451c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
452c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a byte
453c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
454c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public byte getByte(String property, byte def) {
455c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
456c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
457c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
458c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
459c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Byte) {
460c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Byte)value).byteValue();
461c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
462c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "byte");
463c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
464c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
465c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
466c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a short property, or the default if the property
467c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
468c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
469c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
470c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
471c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
472c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a short
473c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
474c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public short getShort(String property, short def) {
475c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
476c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
477c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
478c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
479c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Short) {
480c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Short)value).shortValue();
481c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
482c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "short");
483c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
484c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
485c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
486c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of an integer property, or the default if the property
487c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
488c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
489c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
490c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
491c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
492c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not an integer
493c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
494c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public int getInt(String property, int def) {
495c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
496c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
497c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
498c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
499c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Integer) {
500c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Integer)value).intValue();
501c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
502c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "int");
503c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
504c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
505c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
506c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a long property, or the default if the property
507c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
508c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
509c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
510c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
511c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
512c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a long
513c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
514c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public long getLong(String property, long def) {
515c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
516c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
517c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
518c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
519c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Long) {
520c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Long)value).longValue();
521c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
522c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "long");
523c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
524c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
525c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
526c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a float property, or the default if the property
527c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
528c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
529c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
530c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
531c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
532c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a float
533c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
534c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public float getFloat(String property, float def) {
535c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
536c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
537c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
538c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
539c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Float) {
540c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Float)value).floatValue();
541c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
542c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "float");
543c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
544c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
545c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
546c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a double property, or the default if the property
547c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
548c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
549c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
550c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
551c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
552c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a double
553c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
554c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public double getDouble(String property, double def) {
555c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
556c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
557c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
558c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
559c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value instanceof Double) {
560c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return ((Double)value).doubleValue();
561c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
562c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "double");
563c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
564c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
565c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
566c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a string property, or the default if the property
567c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * has not been defined.
568c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
569c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
570c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param def The default value to return if the property is not set
571c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
572c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a string
573c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
574c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public String getString(String property, String def) {
575c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        Object value = super.get(property);
576c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == null) {
577c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return def;
578c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
579c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        if (value == NULL_STRING) {
580c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return null;
581c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        } else if (value instanceof String) {
582c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort            return (String)value;
583c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        }
584c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        throw new TypeException(property, value, "string");
585c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
586c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
587c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /*
588c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Getters with implicit defaults
589c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
590c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
591c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
592c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a boolean property, or false
593c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
594c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
595c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
596c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
597c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a boolean
598c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
599c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public boolean getBoolean(String property) {
600c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getBoolean(property, false);
601c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
602c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
603c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
604c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a byte property, or 0
605c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
606c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
607c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
608c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
609c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a byte
610c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
611c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public byte getByte(String property) {
612c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getByte(property, (byte)0);
613c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
614c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
615c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
616c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a short property, or 0
617c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
618c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
619c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
620c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
621c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a short
622c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
623c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public short getShort(String property) {
624c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getShort(property, (short)0);
625c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
626c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
627c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
628c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of an integer property, or 0
629c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
630c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
631c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
632c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
633c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not an integer
634c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
635c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public int getInt(String property) {
636c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getInt(property, 0);
637c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
638c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
639c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
640c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a long property, or 0
641c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
642c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
643c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
644c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
645c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a long
646c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
647c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public long getLong(String property) {
648c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getLong(property, 0L);
649c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
650c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
651c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
652c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a float property, or 0.0
653c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
654c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
655c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
656c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
657c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a float
658c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
659c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public float getFloat(String property) {
660c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getFloat(property, 0.0f);
661c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
662c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
663c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
664c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a double property, or 0.0
665c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
666c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
667c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
668c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
669c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a double
670c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
671c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public double getDouble(String property) {
672c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getDouble(property, 0.0);
673c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
674c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort
675c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    /**
676c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * Returns the value of a String property, or ""
677c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * if the property has not been defined.
678c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     *
679c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @param property The name of the property to return
680c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @return the value of the property
681c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     * @throws TypeException if the property is set and is not a string
682c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort     */
683c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    public String getString(String property) {
684c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort        return getString(property, "");
685c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort    }
6864b0ebef18defefe6850360cf11498f262a71847dDave Bort
6874b0ebef18defefe6850360cf11498f262a71847dDave Bort    // Values returned by getStringInfo()
6884b0ebef18defefe6850360cf11498f262a71847dDave Bort    public static final int STRING_TYPE_MISMATCH = -2;
6894b0ebef18defefe6850360cf11498f262a71847dDave Bort    public static final int STRING_NOT_SET = -1;
6904b0ebef18defefe6850360cf11498f262a71847dDave Bort    public static final int STRING_NULL = 0;
6914b0ebef18defefe6850360cf11498f262a71847dDave Bort    public static final int STRING_SET = 1;
6924b0ebef18defefe6850360cf11498f262a71847dDave Bort
6934b0ebef18defefe6850360cf11498f262a71847dDave Bort    /**
6944b0ebef18defefe6850360cf11498f262a71847dDave Bort     * Provides string type information about a property.
6954b0ebef18defefe6850360cf11498f262a71847dDave Bort     *
6964b0ebef18defefe6850360cf11498f262a71847dDave Bort     * @param property the property to check
6974b0ebef18defefe6850360cf11498f262a71847dDave Bort     * @return STRING_SET if the property is a string and is non-null.
6984b0ebef18defefe6850360cf11498f262a71847dDave Bort     *         STRING_NULL if the property is a string and is null.
6994b0ebef18defefe6850360cf11498f262a71847dDave Bort     *         STRING_NOT_SET if the property is not set (no type or value).
7004b0ebef18defefe6850360cf11498f262a71847dDave Bort     *         STRING_TYPE_MISMATCH if the property is set but is not a string.
7014b0ebef18defefe6850360cf11498f262a71847dDave Bort     */
7024b0ebef18defefe6850360cf11498f262a71847dDave Bort    public int getStringInfo(String property) {
7034b0ebef18defefe6850360cf11498f262a71847dDave Bort        Object value = super.get(property);
7044b0ebef18defefe6850360cf11498f262a71847dDave Bort        if (value == null) {
7054b0ebef18defefe6850360cf11498f262a71847dDave Bort            return STRING_NOT_SET;
7064b0ebef18defefe6850360cf11498f262a71847dDave Bort        }
7074b0ebef18defefe6850360cf11498f262a71847dDave Bort        if (value == NULL_STRING) {
7084b0ebef18defefe6850360cf11498f262a71847dDave Bort            return STRING_NULL;
7094b0ebef18defefe6850360cf11498f262a71847dDave Bort        } else if (value instanceof String) {
7104b0ebef18defefe6850360cf11498f262a71847dDave Bort            return STRING_SET;
7114b0ebef18defefe6850360cf11498f262a71847dDave Bort        }
7124b0ebef18defefe6850360cf11498f262a71847dDave Bort        return STRING_TYPE_MISMATCH;
7134b0ebef18defefe6850360cf11498f262a71847dDave Bort    }
714c4d6dd0bbce846c3b20e907d6a3016e4adc65e22Dave Bort}
715