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.TreeLogger;
019: import com.google.gwt.core.ext.UnableToCompleteException;
020: import com.google.gwt.core.ext.typeinfo.JClassType;
021: import com.google.gwt.user.rebind.AbstractSourceCreator;
022: import com.google.gwt.i18n.rebind.util.ResourceFactory;
023:
024: import java.util.HashMap;
025: import java.util.Map;
026:
027: /**
028: * Links classes with their localized counterparts.
029: */
030: class LocalizableLinkageCreator extends AbstractSourceCreator {
031: private static Map<String, JClassType> findDerivedClasses(
032: TreeLogger logger, JClassType baseClass)
033: throws UnableToCompleteException {
034: // Construct valid set of candidates for this type.
035: Map<String, JClassType> matchingClasses = new HashMap<String, JClassType>();
036: // Add base class if possible.
037: if (baseClass.isInterface() == null
038: && baseClass.isAbstract() == false) {
039: matchingClasses.put(ResourceFactory.DEFAULT_TOKEN,
040: baseClass);
041: }
042: String baseName = baseClass.getSimpleSourceName();
043:
044: // Find matching sub types.
045: JClassType[] x = baseClass.getSubtypes();
046: for (int i = 0; i < x.length; i++) {
047: JClassType subType = x[i];
048: if ((subType.isInterface() == null)
049: && (subType.isAbstract() == false)) {
050: String name = subType.getSimpleSourceName();
051: // Strip locale from type,
052: int localeIndex = name
053: .indexOf(ResourceFactory.LOCALE_SEPARATOR);
054: String subTypeBaseName = name;
055: if (localeIndex != -1) {
056: subTypeBaseName = name.substring(0, localeIndex);
057: }
058: boolean matches = subTypeBaseName.equals(baseName);
059: if (matches) {
060: boolean isDefault = localeIndex == -1
061: || localeIndex == name.length() - 1
062: || ResourceFactory.DEFAULT_TOKEN
063: .equals(name
064: .substring(localeIndex + 1));
065: if (isDefault) {
066: // Don't override base as default if present.
067: JClassType defaultClass = matchingClasses
068: .get(ResourceFactory.DEFAULT_TOKEN);
069: if (defaultClass != null) {
070: throw error(
071: logger,
072: defaultClass
073: + " and "
074: + baseName
075: + " are both potential default classes for "
076: + baseClass);
077: } else {
078: matchingClasses.put(
079: ResourceFactory.DEFAULT_TOKEN,
080: subType);
081: }
082: } else {
083: // Don't allow a locale to be ambiguous. Very similar to default
084: // case, different error message.
085: String localeSubString = name
086: .substring(localeIndex + 1);
087: JClassType dopClass = matchingClasses
088: .get(localeSubString);
089: if (dopClass != null) {
090: throw error(logger, dopClass
091: .getQualifiedSourceName()
092: + " and "
093: + subType.getQualifiedSourceName()
094: + " are both potential matches to "
095: + baseClass
096: + " in locale"
097: + localeSubString);
098: }
099: matchingClasses.put(localeSubString, subType);
100: }
101: }
102: }
103: }
104: return matchingClasses;
105: }
106:
107: /**
108: * Map to cache linkages of implementation classes and interfaces.
109: */
110: // Change back to ReferenceMap once apache collections is in.
111: private final Map<String, String> implCache = new HashMap<String, String>();
112:
113: /**
114: * * Finds associated implementation in the current locale. Here are the rules
115: * <p>
116: * </p>
117: * <p>
118: * If class name is X, and locale is z_y, look for X_z_y, then X_z, then X
119: * </p>
120: *
121: * @param baseClass
122: * @return class name to link with
123: * @throws UnableToCompleteException
124: */
125: String linkWithImplClass(TreeLogger logger, JClassType baseClass,
126: String locale) throws UnableToCompleteException {
127:
128: String baseName = baseClass.getQualifiedSourceName();
129: /**
130: * Try to find implementation class, as the current class is not a Constant
131: * or Message.
132: */
133: String className = implCache.get(baseName + locale);
134: if (className != null) {
135: return className;
136: }
137:
138: if (baseClass.getName().indexOf(
139: ResourceFactory.LOCALE_SEPARATOR) != -1) {
140: throw error(logger, "Cannot have a "
141: + ResourceFactory.LOCALE_SEPARATOR
142: + " in the base localizable class " + baseClass);
143: }
144: Map<String, JClassType> matchingClasses = findDerivedClasses(
145: logger, baseClass);
146: // Now that we have all matches, find best class
147: String localeSuffix = locale;
148: JClassType result = null;
149: while (true) {
150: // Check for current result.
151: result = matchingClasses.get(localeSuffix);
152: if (result != null) {
153: break;
154: }
155: if (localeSuffix.equals(ResourceFactory.DEFAULT_TOKEN)) {
156: // No classes matched.
157: throw error(logger,
158: "Cannot find a class to bind to argument type "
159: + baseClass.getQualifiedSourceName());
160: }
161:
162: localeSuffix = ResourceFactory
163: .getParentLocaleName(localeSuffix);
164: }
165: implCache.put(baseName + locale, className);
166: return result.getQualifiedSourceName();
167: }
168: }
|