001: /* Copyright (c) 2007, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.util;
032:
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.HashSet;
036: import java.util.Enumeration;
037: import java.util.Iterator;
038: import java.util.Collection;
039:
040: /**
041: * Purpose of this class is to wrap a RefCapablePropertyResourceBundle to
042: * reliably detect any possible use of a missing property key as soon as
043: * this class is clinitted.
044: * The reason for this is to allow us developers to detect all such errors
045: * before end-users ever use this class.
046: *
047: * See SqltoolRB for an example implementation of this abstract class.
048: */
049: abstract public class ValidatingResourceBundle {
050: protected boolean validated = false;
051:
052: abstract protected Map getKeyIdToString();
053:
054: public static final int THROW_BEHAVIOR = RefCapablePropertyResourceBundle.THROW_BEHAVIOR;
055: public static final int EMPTYSTRING_BEHAVIOR = RefCapablePropertyResourceBundle.EMPTYSTRING_BEHAVIOR;
056: public static final int NOOP_BEHAVIOR = RefCapablePropertyResourceBundle.NOOP_BEHAVIOR;
057: /* Three constants above are only so caller doesn't need to know
058: * details of RefCapablePropertyResourceBundle (and they won't need
059: * to code that God-awfully-long class name). */
060:
061: protected RefCapablePropertyResourceBundle wrappedRCPRB;
062:
063: protected ValidatingResourceBundle(String baseName) {
064: wrappedRCPRB = RefCapablePropertyResourceBundle.getBundle(
065: baseName, getClass().getClassLoader());
066: }
067:
068: // The following methods are a passthru wrappers for the wrapped RCPRB.
069:
070: /** @see RefCapablePropertyResourceBundle#getString(String) */
071: public String getString(int id) {
072: return wrappedRCPRB.getString((String) getKeyIdToString().get(
073: new Integer(id)));
074: }
075:
076: /** @see RefCapablePropertyResourceBundle#getString(String, String[]) */
077: public String getString(int id, String[] sa) {
078: return wrappedRCPRB.getString((String) getKeyIdToString().get(
079: new Integer(id)), sa, missingPosValueBehavior);
080: }
081:
082: /** @see RefCapablePropertyResourceBundle#getExpandedString(String) */
083: public String getExpandedString(int id) {
084: return wrappedRCPRB.getExpandedString(
085: (String) getKeyIdToString().get(new Integer(id)),
086: missingPropertyBehavior);
087: }
088:
089: /** @see RefCapablePropertyResourceBundle#getExpandedString(String, String[]) */
090: public String getExpandedString(int id, String[] sa) {
091: return wrappedRCPRB.getExpandedString(
092: (String) getKeyIdToString().get(new Integer(id)), sa,
093: missingPropertyBehavior, missingPosValueBehavior);
094: }
095:
096: private int missingPropertyBehavior = THROW_BEHAVIOR;
097: private int missingPosValueBehavior = THROW_BEHAVIOR;
098:
099: /**
100: * Set behavior for get*String*() method when a referred-to
101: * System Property is not set. Set to one of
102: * <UL>
103: * <LI>RefCapablePropertyResourceBunele.THROW_BEHAVIOR
104: * <LI>RefCapablePropertyResourceBunele.EMPTYSTRING_BEHAVIOR
105: * <LI>RefCapablePropertyResourceBunele.NOOP_BEHAVIOR
106: * </UL>
107: * The first value is the default.
108: */
109: public void setMissingPropertyBehavior(int missingPropertyBehavior) {
110: this .missingPropertyBehavior = missingPropertyBehavior;
111: }
112:
113: /**
114: * Set behavior for get*String(String, String[]) method when a
115: * positional index (like %{4}) is used but no subs value was given for
116: * that index. Set to one of
117: * <UL>
118: * <LI>RefCapablePropertyResourceBunele.THROW_BEHAVIOR
119: * <LI>RefCapablePropertyResourceBunele.EMPTYSTRING_BEHAVIOR
120: * <LI>RefCapablePropertyResourceBunele.NOOP_BEHAVIOR
121: * </UL>
122: * The first value is the default.
123: */
124: public void setMissingPosValueBehavior(int missingPosValueBehavior) {
125: this .missingPosValueBehavior = missingPosValueBehavior;
126: }
127:
128: public int getMissingPropertyBehavior() {
129: return missingPropertyBehavior;
130: }
131:
132: public int getMissingPosValueBehavior() {
133: return missingPosValueBehavior;
134: }
135:
136: /**
137: * Returns the number of defined (usable) keys.
138: */
139: public int getSize() {
140: if (!validated)
141: throw new RuntimeException(
142: "Method SqltoolRB.getSize() may only be called after "
143: + "calling SqltoolRB.validate()");
144: return getKeyIdToString().size();
145: }
146:
147: public void validate() {
148: String val;
149: if (validated)
150: return;
151: validated = true;
152: Set allIdStrings = new HashSet(getKeyIdToString().values());
153: if (allIdStrings.size() < getKeyIdToString().values().size()) {
154: Collection c = getKeyIdToString().values();
155: Iterator it = allIdStrings.iterator();
156: while (it.hasNext())
157: c.remove(it.next());
158: throw new RuntimeException(
159: "Duplicate property key(s) string in keyIdToString map: "
160: + c);
161: }
162: Enumeration allKeys = wrappedRCPRB.getKeys();
163: while (allKeys.hasMoreElements()) {
164: // We can't test positional parameters, but we can verify that
165: // referenced files exist by reading the values.
166: // Pretty inefficient, but this can be optimized when I have time.
167: val = (String) allKeys.nextElement();
168: wrappedRCPRB.getString(val);
169: // Keep no reference to the returned String
170: allIdStrings.remove(val);
171: }
172: if (allIdStrings.size() > 0)
173: throw new RuntimeException(
174: "Resource Bundle pre-validation failed. "
175: + "Following property key(s) not mapped.\n"
176: + allIdStrings);
177: }
178:
179: /* Convenience wrappers follow for getString(int, String[]) for up to
180: * 3 int and/or String positionals. Java 5 variable-length parameters
181: * can eliminate the repetition here, plus generalize to random
182: * numbers of parameters. */
183: public String getString(int id, String s1) {
184: return getString(id, new String[] { s1 });
185: }
186:
187: public String getString(int id, String s1, String s2) {
188: return getString(id, new String[] { s1, s2 });
189: }
190:
191: public String getString(int id, String s1, String s2, String s3) {
192: return getString(id, new String[] { s1, s2, s3 });
193: }
194:
195: public String getString(int id, int i1) {
196: return getString(id, new String[] { Integer.toString(i1) });
197: }
198:
199: public String getString(int id, int i1, int i2) {
200: return getString(id, new String[] { Integer.toString(i1),
201: Integer.toString(i2) });
202: }
203:
204: public String getString(int id, int i1, int i2, int i3) {
205: return getString(id, new String[] { Integer.toString(i1),
206: Integer.toString(i2), Integer.toString(i3) });
207: }
208:
209: public String getString(int id, int i1, String s2) {
210: return getString(id, new String[] { Integer.toString(i1), s2 });
211: }
212:
213: public String getString(int id, String s1, int i2) {
214: return getString(id, new String[] { s1, Integer.toString(i2) });
215: }
216:
217: public String getString(int id, int i1, int i2, String s3) {
218: return getString(id, new String[] { Integer.toString(i1),
219: Integer.toString(i2), s3 });
220: }
221:
222: public String getString(int id, int i1, String s2, int i3) {
223: return getString(id, new String[] { Integer.toString(i1), s2,
224: Integer.toString(i3) });
225: }
226:
227: public String getString(int id, String s1, int i2, int i3) {
228: return getString(id, new String[] { s1, Integer.toString(i2),
229: Integer.toString(i3) });
230: }
231:
232: public String getString(int id, int i1, String s2, String s3) {
233: return getString(id, new String[] { Integer.toString(i1), s2,
234: s3 });
235: }
236:
237: public String getString(int id, String s1, String s2, int i3) {
238: return getString(id, new String[] { s1, s2,
239: Integer.toString(i3) });
240: }
241:
242: public String getString(int id, String s1, int i2, String s3) {
243: return getString(id, new String[] { s1, Integer.toString(i2),
244: s3 });
245: }
246: }
|