001: /*
002: * Copyright 2006 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.dev.jdt;
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.CompilationUnitProvider;
021: import com.google.gwt.core.ext.typeinfo.JClassType;
022: import com.google.gwt.core.ext.typeinfo.TypeOracle;
023: import com.google.gwt.dev.util.Util;
024:
025: import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
026:
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Map;
030: import java.util.Set;
031:
032: /**
033: * Provides a mutable compilation service host on top of a
034: * {@link com.google.gwt.dev.typeinfo.TypeOracle} as well as providing
035: * subclasses an opportunity to substitute their own source to implement
036: * special-handling, such as rewriting JSNI source.
037: */
038: public class StandardSourceOracle implements SourceOracle {
039:
040: private final Map cupsByTypeName = new HashMap();
041:
042: private final Set knownPackages = new HashSet();
043:
044: private final TypeOracle typeOracle;
045:
046: /**
047: * @param typeOracle answers questions about compilation unit locations
048: * @param genDir for compilation units whose location does not correspond to a
049: * URL that can be opened, their source will be written to this
050: * directory to support debugging, or <code>null</code> if the
051: * source need not be written to disk
052: */
053: public StandardSourceOracle(TypeOracle typeOracle) {
054: this .typeOracle = typeOracle;
055: }
056:
057: /**
058: * Attempts to find the compilation unit for the requested type. Often
059: * legitimately returns <code>null</code> because the compilation service
060: * does tests to help determine whether a particular symbol refers to a
061: * package or a type.
062: */
063: public final CompilationUnitProvider findCompilationUnit(
064: TreeLogger logger, String typeName)
065: throws UnableToCompleteException {
066:
067: // Check the cache first.
068: //
069: CompilationUnitProvider cup = (CompilationUnitProvider) cupsByTypeName
070: .get(typeName);
071:
072: if (cup != null) {
073: // Found in cache.
074: //
075: return cup;
076: }
077:
078: // See if the type oracle can find it.
079: //
080: JClassType type = typeOracle.findType(typeName);
081: if (type != null) {
082: // All type oracle types are supposed to know their compilation unit.
083: //
084: cup = type.getCompilationUnit();
085: assert (cup != null);
086: }
087:
088: // Give the subclass a chance to replace the source. This used for JSNI in
089: // hosted mode at present but could be used for any source rewriting trick.
090: //
091: if (cup != null) {
092: try {
093: CompilationUnitProvider specialCup = doFilterCompilationUnit(
094: logger, typeName, cup);
095:
096: if (specialCup != null) {
097: // Use the cup that the subclass returned instead. Note that even
098: // though this file may not exist on disk, it is special so we don't
099: // want users to have to debug into it unless they specifically ask
100: // to.
101: //
102: cup = specialCup;
103: }
104: } catch (UnableToCompleteException e) {
105: String location = cup.getLocation();
106: char[] source = cup.getSource();
107: Util
108: .maybeDumpSource(logger, location, source,
109: typeName);
110: throw e;
111: }
112: }
113:
114: if (cup == null) {
115: // Did not find a cup for the type.
116: // This happens commonly and is not a cause for alarm.
117: //
118: return null;
119: }
120:
121: // Remember its package and cache it.
122: //
123: cupsByTypeName.put(typeName, cup);
124: return cup;
125: }
126:
127: public TypeOracle getTypeOracle() {
128: return typeOracle;
129: }
130:
131: /**
132: * Determines whether or not a particular name is a package name.
133: */
134: public final boolean isPackage(String possiblePackageName) {
135: if (knownPackages.contains(possiblePackageName)) {
136: // The quick route -- we've already answered yes to this question.
137: // OPTIMIZE: cache NOs as well
138: return true;
139: }
140:
141: if (typeOracle.findPackage(possiblePackageName) != null) {
142: // Was a package know on the source path.
143: //
144: rememberPackage(possiblePackageName);
145: return true;
146: } else {
147: // Not a package.
148: //
149: return false;
150: }
151: }
152:
153: /**
154: * Subclasses can override this method if they use a special mechanism to find
155: * the compilation unit for a type. For example, rewriting source code (as
156: * with JSNI) or preempting the source for a given type (as with
157: * <code>GWT.create</code>).
158: * <p>
159: * Note that subclasses should <em>not</em> call
160: * <code>super.{@link #findCompilationUnit(TreeLogger, String)}</code> in
161: * their implementation.
162: *
163: * @return <code>null</code> if you want the superclass to use its normal
164: * mechanism for finding types
165: */
166: protected CompilationUnitProvider doFilterCompilationUnit(
167: TreeLogger logger, String typeName,
168: CompilationUnitProvider existing)
169: throws UnableToCompleteException {
170: return null;
171: }
172:
173: /**
174: * Subclasses can override this method if they use additional mechanisms to
175: * find types magically.
176: * <p>
177: * Note that subclasses should <em>not</em> call
178: * <code>super.{@link #findAdditionalTypesUsingMagic(TreeLogger, CompilationUnitDeclaration)}</code>
179: * in their implementation.
180: *
181: * @return <code>null</code> to indicate that no additional types should be
182: * added
183: */
184: protected String[] doFindAdditionalTypesUsingMagic(
185: TreeLogger logger, CompilationUnitDeclaration unit)
186: throws UnableToCompleteException {
187: return null;
188: }
189:
190: void invalidateCups(Set typeNames) {
191: cupsByTypeName.keySet().removeAll(typeNames);
192: }
193:
194: /**
195: * Remember that this package was added. Used for generated packages.
196: */
197: private void rememberPackage(String packageName) {
198: int i = packageName.lastIndexOf('.');
199: if (i != -1) {
200: // Ensure the parent package is also created.
201: //
202: rememberPackage(packageName.substring(0, i));
203: }
204: knownPackages.add(packageName);
205: }
206: }
|