001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.util;
011:
012: import java.io.IOException;
013: import java.io.InputStream;
014: import java.lang.reflect.Field;
015: import java.lang.reflect.Modifier;
016: import java.text.MessageFormat;
017: import java.util.ArrayList;
018: import java.util.HashMap;
019: import java.util.Locale;
020: import java.util.Map;
021: import java.util.Properties;
022:
023: public final class Messages {
024: private static class MessagesProperties extends Properties {
025:
026: private static final int MOD_EXPECTED = Modifier.PUBLIC
027: | Modifier.STATIC;
028: private static final int MOD_MASK = MOD_EXPECTED
029: | Modifier.FINAL;
030: private static final long serialVersionUID = 1L;
031:
032: private final Map fields;
033:
034: public MessagesProperties(Field[] fieldArray, String bundleName) {
035: super ();
036: final int len = fieldArray.length;
037: fields = new HashMap(len * 2);
038: for (int i = 0; i < len; i++) {
039: fields.put(fieldArray[i].getName(), fieldArray[i]);
040: }
041: }
042:
043: /* (non-Javadoc)
044: * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
045: */
046: public synchronized Object put(Object key, Object value) {
047: try {
048: Field field = (Field) fields.get(key);
049: if (field == null) {
050: return null;
051: }
052: //can only set value of public static non-final fields
053: if ((field.getModifiers() & MOD_MASK) != MOD_EXPECTED)
054: return null;
055: // Set the value into the field. We should never get an exception here because
056: // we know we have a public static non-final field. If we do get an exception, silently
057: // log it and continue. This means that the field will (most likely) be un-initialized and
058: // will fail later in the code and if so then we will see both the NPE and this error.
059: try {
060: field.set(null, value);
061: } catch (Exception e) {
062: // ignore
063: }
064: } catch (SecurityException e) {
065: // ignore
066: }
067: return null;
068: }
069: }
070:
071: private static String[] nlSuffixes;
072: private static final String EXTENSION = ".properties"; //$NON-NLS-1$
073:
074: private static final String BUNDLE_NAME = "org.eclipse.jdt.internal.compiler.messages";//$NON-NLS-1$
075:
076: private Messages() {
077: // Do not instantiate
078: }
079:
080: public static String compilation_unresolvedProblem;
081: public static String compilation_unresolvedProblems;
082: public static String compilation_request;
083: public static String compilation_loadBinary;
084: public static String compilation_process;
085: public static String compilation_write;
086: public static String compilation_done;
087: public static String compilation_units;
088: public static String compilation_unit;
089: public static String compilation_internalError;
090: public static String output_isFile;
091: public static String output_notValidAll;
092: public static String output_notValid;
093: public static String problem_noSourceInformation;
094: public static String problem_atLine;
095: public static String abort_invalidAttribute;
096: public static String abort_invalidExceptionAttribute;
097: public static String abort_invalidOpcode;
098: public static String abort_missingCode;
099: public static String abort_againstSourceModel;
100: public static String accept_cannot;
101: public static String parser_incorrectPath;
102: public static String parser_moveFiles;
103: public static String parser_syntaxRecovery;
104: public static String parser_regularParse;
105: public static String parser_missingFile;
106: public static String parser_corruptedFile;
107: public static String parser_endOfFile;
108: public static String parser_endOfConstructor;
109: public static String parser_endOfMethod;
110: public static String parser_endOfInitializer;
111: public static String ast_missingCode;
112: public static String constant_cannotCastedInto;
113: public static String constant_cannotConvertedTo;
114:
115: static {
116: initializeMessages(BUNDLE_NAME, Messages.class);
117: }
118:
119: /**
120: * Bind the given message's substitution locations with the given string values.
121: *
122: * @param message the message to be manipulated
123: * @return the manipulated String
124: */
125: public static String bind(String message) {
126: return bind(message, null);
127: }
128:
129: /**
130: * Bind the given message's substitution locations with the given string values.
131: *
132: * @param message the message to be manipulated
133: * @param binding the object to be inserted into the message
134: * @return the manipulated String
135: */
136: public static String bind(String message, Object binding) {
137: return bind(message, new Object[] { binding });
138: }
139:
140: /**
141: * Bind the given message's substitution locations with the given string values.
142: *
143: * @param message the message to be manipulated
144: * @param binding1 An object to be inserted into the message
145: * @param binding2 A second object to be inserted into the message
146: * @return the manipulated String
147: */
148: public static String bind(String message, Object binding1,
149: Object binding2) {
150: return bind(message, new Object[] { binding1, binding2 });
151: }
152:
153: /**
154: * Bind the given message's substitution locations with the given string values.
155: *
156: * @param message the message to be manipulated
157: * @param bindings An array of objects to be inserted into the message
158: * @return the manipulated String
159: */
160: public static String bind(String message, Object[] bindings) {
161: return MessageFormat.format(message, bindings);
162: }
163:
164: /*
165: * Build an array of directories to search
166: */
167: private static String[] buildVariants(String root) {
168: if (nlSuffixes == null) {
169: //build list of suffixes for loading resource bundles
170: String nl = Locale.getDefault().toString();
171: ArrayList result = new ArrayList(4);
172: int lastSeparator;
173: while (true) {
174: result.add('_' + nl + EXTENSION);
175: lastSeparator = nl.lastIndexOf('_');
176: if (lastSeparator == -1)
177: break;
178: nl = nl.substring(0, lastSeparator);
179: }
180: //add the empty suffix last (most general)
181: result.add(EXTENSION);
182: nlSuffixes = (String[]) result.toArray(new String[result
183: .size()]);
184: }
185: root = root.replace('.', '/');
186: String[] variants = new String[nlSuffixes.length];
187: for (int i = 0; i < variants.length; i++)
188: variants[i] = root + nlSuffixes[i];
189: return variants;
190: }
191:
192: public static void initializeMessages(String bundleName, Class clazz) {
193: // load the resource bundle and set the fields
194: final Field[] fields = clazz.getDeclaredFields();
195: load(bundleName, clazz.getClassLoader(), fields);
196:
197: // iterate over the fields in the class to make sure that there aren't any empty ones
198: final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
199: final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
200: final int numFields = fields.length;
201: for (int i = 0; i < numFields; i++) {
202: Field field = fields[i];
203: if ((field.getModifiers() & MOD_MASK) != MOD_EXPECTED)
204: continue;
205: try {
206: // Set the value into the field if its empty. We should never get an exception here because
207: // we know we have a public static non-final field. If we do get an exception, silently
208: // log it and continue. This means that the field will (most likely) be un-initialized and
209: // will fail later in the code and if so then we will see both the NPE and this error.
210: if (field.get(clazz) == null) {
211: String value = "Missing message: " + field.getName() + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
212: field.set(null, value);
213: }
214: } catch (IllegalArgumentException e) {
215: // ignore
216: } catch (IllegalAccessException e) {
217: // ignore
218: }
219: }
220: }
221:
222: /**
223: * Load the given resource bundle using the specified class loader.
224: */
225: public static void load(final String bundleName,
226: final ClassLoader loader, final Field[] fields) {
227: final String[] variants = buildVariants(bundleName);
228: // search the dirs in reverse order so the cascading defaults is set correctly
229: for (int i = variants.length; --i >= 0;) {
230: InputStream input = (loader == null) ? ClassLoader
231: .getSystemResourceAsStream(variants[i]) : loader
232: .getResourceAsStream(variants[i]);
233: if (input == null)
234: continue;
235: try {
236: final MessagesProperties properties = new MessagesProperties(
237: fields, bundleName);
238: properties.load(input);
239: } catch (IOException e) {
240: // ignore
241: } finally {
242: try {
243: input.close();
244: } catch (IOException e) {
245: // ignore
246: }
247: }
248: }
249: }
250: }
|