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;
017:
018: import com.google.gwt.core.ext.GeneratorContext;
019: import com.google.gwt.core.ext.TreeLogger;
020: import com.google.gwt.core.ext.UnableToCompleteException;
021: import com.google.gwt.core.ext.typeinfo.JClassType;
022: import com.google.gwt.core.ext.typeinfo.JMethod;
023: import com.google.gwt.core.ext.typeinfo.NotFoundException;
024: import com.google.gwt.core.ext.typeinfo.TypeOracle;
025: import com.google.gwt.i18n.rebind.util.AbstractResource;
026: import com.google.gwt.i18n.rebind.util.ResourceFactory;
027: import com.google.gwt.user.rebind.AbstractGeneratorClassCreator;
028: import com.google.gwt.user.rebind.AbstractMethodCreator;
029: import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
030: import com.google.gwt.user.rebind.SourceWriter;
031:
032: import java.io.PrintWriter;
033: import java.util.MissingResourceException;
034: import java.util.Set;
035:
036: /**
037: * Represents generic functionality needed for <code>Constants</code> and
038: * <code>Messages</code> classes.
039: */
040: abstract class AbstractLocalizableImplCreator extends
041: AbstractGeneratorClassCreator {
042:
043: static String generateConstantOrMessageClass(TreeLogger logger,
044: GeneratorContext context, String locale,
045: JClassType targetClass) throws UnableToCompleteException {
046: TypeOracle oracle = context.getTypeOracle();
047: JClassType constantsClass;
048: JClassType messagesClass;
049: JClassType constantsWithLookupClass;
050: try {
051: constantsClass = oracle
052: .getType(LocalizableGenerator.CONSTANTS_NAME);
053: constantsWithLookupClass = oracle
054: .getType(LocalizableGenerator.CONSTANTS_WITH_LOOKUP_NAME);
055: messagesClass = oracle
056: .getType(LocalizableGenerator.MESSAGES_NAME);
057: } catch (NotFoundException e) {
058: // Should never happen in practice.
059: throw error(logger, e);
060: }
061:
062: String name = targetClass.getName();
063: String packageName = targetClass.getPackage().getName();
064:
065: // Make sure the interface being rebound extends either Constants or
066: // Messages.
067: boolean assignableToConstants = constantsClass
068: .isAssignableFrom(targetClass);
069: boolean assignableToMessages = messagesClass
070: .isAssignableFrom(targetClass);
071: if (!assignableToConstants && !assignableToMessages) {
072: // Let the implementation generator handle this interface.
073: return null;
074: }
075:
076: // Make sure that they don't try to extend both Messages and Constants.
077: if (assignableToConstants && assignableToMessages) {
078: throw error(logger, name
079: + " cannot extend both Constants and Messages");
080: }
081:
082: // Make sure that the type being rebound is in fact an interface.
083: if (targetClass.isInterface() == null) {
084: throw error(logger, name + " must be an interface");
085: }
086:
087: AbstractResource resource;
088: try {
089: resource = ResourceFactory.getBundle(targetClass, locale);
090: } catch (MissingResourceException e) {
091: throw error(
092: logger,
093: "Localization failed; there must be at least one properties file accessible through the classpath in package '"
094: + packageName
095: + "' whose base name is '"
096: + ResourceFactory
097: .getResourceName(targetClass) + "'");
098: } catch (IllegalArgumentException e) {
099: // A bad key can generate an illegal argument exception.
100: throw error(logger, e.getMessage());
101: }
102:
103: // generated implementations for interface X will be named X_, X_en,
104: // X_en_CA, etc.
105: String realLocale = String
106: .valueOf(ResourceFactory.LOCALE_SEPARATOR);
107: if (!ResourceFactory.DEFAULT_TOKEN.equals(resource
108: .getLocaleName())) {
109: realLocale += resource.getLocaleName();
110: }
111: // Use _ rather than "." in class name, cannot use $
112: String resourceName = targetClass.getName().replace('.', '_');
113: String className = resourceName + realLocale;
114: PrintWriter pw = context.tryCreate(logger, packageName,
115: className);
116: if (pw != null) {
117: ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(
118: packageName, className);
119: factory.addImplementedInterface(targetClass
120: .getQualifiedSourceName());
121: SourceWriter writer = factory.createSourceWriter(context,
122: pw);
123: // Now that we have all the information set up, process the class
124: if (constantsWithLookupClass.isAssignableFrom(targetClass)) {
125: ConstantsWithLookupImplCreator c = new ConstantsWithLookupImplCreator(
126: logger, writer, targetClass, resource, context
127: .getTypeOracle());
128: c.emitClass(logger);
129: } else if (constantsClass.isAssignableFrom(targetClass)) {
130: ConstantsImplCreator c = new ConstantsImplCreator(
131: logger, writer, targetClass, resource, context
132: .getTypeOracle());
133: c.emitClass(logger);
134: } else {
135: MessagesImplCreator messages = new MessagesImplCreator(
136: logger, writer, targetClass, resource, context
137: .getTypeOracle());
138: messages.emitClass(logger);
139: }
140: context.commit(logger, pw);
141: }
142: return packageName + "." + className;
143: }
144:
145: /**
146: * The Dictionary/value bindings used to determine message contents.
147: */
148: private AbstractResource messageBindings;
149:
150: /**
151: * Constructor for <code>AbstractLocalizableImplCreator</code>.
152: *
153: * @param writer writer
154: * @param targetClass current target
155: * @param messageBindings backing resource
156: */
157: public AbstractLocalizableImplCreator(SourceWriter writer,
158: JClassType targetClass, AbstractResource messageBindings) {
159: super (writer, targetClass);
160: this .messageBindings = messageBindings;
161: }
162:
163: /**
164: * Gets the resource associated with this class.
165: *
166: * @return the resource
167: */
168: public AbstractResource getResourceBundle() {
169: return messageBindings;
170: }
171:
172: @Override
173: protected String branchMessage() {
174: return "Processing " + this .getTarget();
175: }
176:
177: /**
178: * Find the creator associated with the given method, and delegate the
179: * creation of the method body to it.
180: *
181: * @param logger
182: * @param method method to be generated
183: * @throws UnableToCompleteException
184: */
185: protected void delegateToCreator(TreeLogger logger, JMethod method)
186: throws UnableToCompleteException {
187: AbstractMethodCreator methodCreator = getMethodCreator(logger,
188: method);
189: String key = getKey(logger, method);
190: String value;
191: try {
192: value = messageBindings.getString(key);
193: } catch (MissingResourceException e) {
194: String s = "Could not find requested resource key '" + key
195: + "'";
196: TreeLogger child = logger.branch(TreeLogger.ERROR, s, null);
197: Set<String> keys = messageBindings.keySet();
198: if (keys.size() < AbstractResource.REPORT_KEYS_THRESHOLD) {
199: String keyString = "Keys found: " + keys;
200: throw error(child, keyString);
201: } else {
202: throw new UnableToCompleteException();
203: }
204: }
205: String info = "When locale is '"
206: + messageBindings.getLocaleName() + "', property '"
207: + key + "' has the value '" + value + "'";
208: TreeLogger branch = logger.branch(TreeLogger.TRACE, info, null);
209: methodCreator.createMethodFor(branch, method, value);
210: }
211:
212: /**
213: * Returns a resource key given a method name.
214: *
215: * @param logger
216: * @param method
217: * @return the key
218: */
219: protected String getKey(TreeLogger logger, JMethod method) {
220: String[][] id = method
221: .getMetaData(LocalizableGenerator.GWT_KEY);
222: if (id.length > 0) {
223: if (id[0].length == 0) {
224: logger
225: .log(
226: TreeLogger.WARN,
227: method
228: + " had a mislabeled gwt.key, using method name as key",
229: null);
230: } else {
231: String tag = id[0][0];
232: return tag;
233: }
234: }
235: return method.getName();
236: }
237: }
|