001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.i18n.rebind.util;
017:
018: import java.util.ArrayList;
019: import java.util.HashSet;
020: import java.util.List;
021: import java.util.MissingResourceException;
022: import java.util.Set;
023:
024: /**
025: * AbstractResource serves the same purpose as java
026: * ResourceBundle/PropertyResourceBundle.
027: * <p>
028: * Each <code>Resource</code> belongs to a resource tree, indicated by the
029: * path attribute.
030: * <p>
031: * AbstractResource uses a Factory pattern rather than a single static method to
032: * load itself given an abstract string path.
033: * <p>
034: * One advanced feature which should not be used outside the core GWT system is
035: * that resources can have more than one parent, for instance pets_en_US could
036: * have pets_en as one parent and animals_en_US as another. The alternative
037: * parents have lower precedence than any primary parent. Each alternative
038: * parent is associated with a separate resource tree.
039: */
040: public abstract class AbstractResource {
041: /**
042: * Error messages concerning missing keys should include the defined keys if
043: * the number of keys is below this threshold.
044: */
045: public static final int REPORT_KEYS_THRESHOLD = 30;
046:
047: private final List<AbstractResource> alternativeParents = new ArrayList<AbstractResource>();
048:
049: private Set<String> keySet;
050:
051: private String localeName;
052:
053: private String path;
054:
055: private AbstractResource primaryParent;
056:
057: /**
058: * @see java.util.ResourceBundle#getLocale()
059: */
060: public String getLocaleName() {
061: return localeName;
062: }
063:
064: /**
065: * @see java.util.ResourceBundle#getObject(java.lang.String)
066: */
067: public final Object getObject(String key) {
068: Object s = getObjectAux(key, true);
069: if (s == null) {
070: String msg = "Cannot find '" + key + "' in " + this ;
071: Set<String> keys = this .keySet();
072: if (keys.size() < REPORT_KEYS_THRESHOLD) {
073: msg = msg + ", keys found:\n\t" + keys;
074: }
075: throw new MissingResourceException(msg, key, key);
076: }
077: return s;
078: }
079:
080: /**
081: * @see java.util.ResourceBundle#getString(java.lang.String)
082: */
083: public final String getString(String key) {
084: return (String) getObject(key);
085: }
086:
087: /**
088: * Keys associated with this resource.
089: *
090: * @return keys
091: */
092: public Set<String> keySet() {
093: if (keySet == null) {
094: keySet = new HashSet<String>();
095: addToKeySet(keySet);
096: if (primaryParent != null) {
097: primaryParent.addToKeySet(keySet);
098: }
099: for (int i = 0; i < alternativeParents.size(); i++) {
100: AbstractResource element = alternativeParents.get(i);
101: keySet.addAll(element.keySet());
102: }
103: }
104: return keySet;
105: }
106:
107: @Override
108: public String toString() {
109: return "resource for " + path;
110: }
111:
112: /**
113: * A multi-line representation of this object.
114: *
115: * @return verbose string
116: */
117: public String toVerboseString() {
118: StringBuffer b = new StringBuffer();
119: toVerboseStringAux(0, b);
120: return b.toString();
121: }
122:
123: void addAlternativeParent(AbstractResource parent) {
124: if (parent != null) {
125: alternativeParents.add(parent);
126: }
127: }
128:
129: abstract void addToKeySet(Set<String> s);
130:
131: void checkKeys() {
132: // If I don't have a parent, then I am a default node so do not need to
133: // conform
134: if (primaryParent == null) {
135: return;
136: }
137: for (String key : keySet()) {
138: if (primaryParent.getObjectAux(key, true) == null) {
139: for (int i = 0; i < alternativeParents.size(); i++) {
140: AbstractResource alt = alternativeParents.get(i);
141: if (alt.getObjectAux(key, true) != null) {
142: break;
143: }
144: }
145:
146: throw new IllegalArgumentException(
147: key
148: + " is not a valid resource key as it does not occur in the default version of "
149: + this + " nor in any of "
150: + alternativeParents);
151: }
152: }
153: }
154:
155: final Object getObjectAux(String key, boolean useAlternativeParents) {
156: Object s = handleGetObject(key);
157: if (s != null) {
158: return s;
159: }
160: AbstractResource parent = this .getPrimaryParent();
161: if (parent != null) {
162: // Primary parents should not look at their alternative parents
163: s = parent.getObjectAux(key, false);
164: }
165: if ((s == null) && (alternativeParents.size() > 0)
166: && (useAlternativeParents)) {
167: for (int i = 0; (i < alternativeParents.size())
168: && (s == null); i++) {
169: // Alternate parents may look at their alternative parents.
170: AbstractResource altParent = alternativeParents.get(i);
171: s = altParent.getObjectAux(key, true);
172: }
173: }
174: return s;
175: }
176:
177: String getPath() {
178: return path;
179: }
180:
181: AbstractResource getPrimaryParent() {
182: return primaryParent;
183: }
184:
185: abstract Object handleGetObject(String key);
186:
187: void setLocaleName(String locale) {
188: this .localeName = locale;
189: }
190:
191: void setPath(String path) {
192: this .path = path;
193: }
194:
195: void setPrimaryParent(AbstractResource primaryParent) {
196: if (primaryParent == null) {
197: return;
198: }
199: this .primaryParent = primaryParent;
200: }
201:
202: private void newLine(int indent, StringBuffer buf) {
203: buf.append("\n");
204: for (int i = 0; i < indent; i++) {
205: buf.append("\t");
206: }
207: }
208:
209: private void toVerboseStringAux(int indent, StringBuffer buf) {
210: newLine(indent, buf);
211: buf.append(toString());
212: if (primaryParent != null) {
213: newLine(indent, buf);
214: buf.append("Primary Parent: ");
215: primaryParent.toVerboseStringAux(indent + 1, buf);
216: }
217: for (int i = 0; i < alternativeParents.size(); i++) {
218: newLine(indent, buf);
219: buf.append("Alternate Parent: ");
220: AbstractResource element = alternativeParents.get(i);
221: element.toVerboseStringAux(indent + 1, buf);
222: }
223: }
224: }
|