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.dev.shell;
017:
018: import com.google.gwt.core.ext.PropertyOracle;
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.TypeOracle;
023: import com.google.gwt.dev.cfg.PublicOracle;
024: import com.google.gwt.dev.cfg.Rule;
025: import com.google.gwt.dev.cfg.Rules;
026: import com.google.gwt.dev.cfg.StaticPropertyOracle;
027: import com.google.gwt.dev.jdt.CacheManager;
028: import com.google.gwt.dev.jdt.RebindOracle;
029: import com.google.gwt.dev.util.Util;
030:
031: import java.io.File;
032: import java.util.ArrayList;
033: import java.util.HashSet;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Set;
037:
038: /**
039: * Implements rebind logic in terms of a variety of other well-known oracles.
040: */
041: public class StandardRebindOracle implements RebindOracle {
042:
043: /**
044: * Makes the actual deferred binding decision by examining rules.
045: */
046: private final class Rebinder {
047:
048: private final StandardGeneratorContext genCtx;
049:
050: private final Set<Rule> usedRules = new HashSet<Rule>();
051:
052: private final List<String> usedTypeNames = new ArrayList<String>();
053:
054: public Rebinder(TypeOracle typeOracle,
055: PropertyOracle propOracle, PublicOracle publicOracle) {
056: genCtx = new StandardGeneratorContext(typeOracle,
057: propOracle, publicOracle, genDir, outDir,
058: cacheManager);
059: }
060:
061: public String rebind(TreeLogger logger, String typeName)
062: throws UnableToCompleteException {
063:
064: String result = tryRebind(logger, typeName);
065: if (result == null) {
066: result = typeName;
067: }
068:
069: // Announce the newly-generated types.
070: //
071: JClassType[] genTypes = genCtx.finish(logger);
072: if (genTypes.length > 0) {
073: onGeneratedTypes(result, genTypes);
074: }
075:
076: return result;
077: }
078:
079: private String tryRebind(TreeLogger logger, String typeName)
080: throws UnableToCompleteException {
081: if (usedTypeNames.contains(typeName)) {
082: // Found a cycle.
083: //
084: String[] cycle = Util.toArray(String.class,
085: usedTypeNames);
086: Messages.UNABLE_TO_REBIND_DUE_TO_CYCLE_IN_RULES.log(
087: logger, cycle, null);
088: throw new UnableToCompleteException();
089: }
090:
091: // Remember that we've seen this one.
092: //
093: usedTypeNames.add(typeName);
094:
095: // Make the rebind decision.
096: //
097: if (rules.isEmpty()) {
098: logger
099: .log(
100: TreeLogger.DEBUG,
101: "No rules are defined, so no substitution can occur",
102: null);
103: return null;
104: }
105:
106: for (Iterator<Rule> iter = rules.iterator(); iter.hasNext();) {
107: Rule rule = iter.next();
108:
109: // Branch the logger.
110: //
111: TreeLogger branch = Messages.TRACE_CHECKING_RULE
112: .branch(logger, rule, null);
113:
114: if (rule.isApplicable(branch, genCtx, typeName)) {
115: // See if this rule has already been used. This is needed to prevent
116: // infinite loops with 'when-assignable' conditions.
117: //
118: if (!usedRules.contains(rule)) {
119: usedRules.add(rule);
120: Messages.TRACE_RULE_MATCHED.log(logger, null);
121:
122: // Invoke the rule.
123: //
124: return rule.realize(logger, genCtx, typeName);
125:
126: } else {
127: // We are skipping this rule because it has already been used
128: // in a previous iteration.
129: //
130: }
131: } else {
132: Messages.TRACE_RULE_DID_NOT_MATCH.log(logger, null);
133: }
134: }
135:
136: // No matching rule for this type.
137: //
138: return null;
139: }
140: }
141:
142: private final CacheManager cacheManager;
143:
144: private final File genDir;
145:
146: private final File outDir;
147:
148: private final PropertyOracle propOracle;
149:
150: private final PublicOracle publicOracle;
151:
152: private final Rules rules;
153:
154: private final TypeOracle typeOracle;
155:
156: public StandardRebindOracle(TypeOracle typeOracle,
157: PropertyOracle propOracle, PublicOracle publicOracle,
158: Rules rules, File genDir, File moduleOutDir,
159: CacheManager cacheManager) {
160: this .typeOracle = typeOracle;
161: this .propOracle = propOracle;
162: this .publicOracle = publicOracle;
163: this .rules = rules;
164: this .genDir = genDir;
165: this .outDir = moduleOutDir;
166: if (cacheManager != null) {
167: this .cacheManager = cacheManager;
168: } else {
169: this .cacheManager = new CacheManager(typeOracle);
170: }
171: }
172:
173: public StandardRebindOracle(TypeOracle typeOracle,
174: StaticPropertyOracle propOracle, PublicOracle publicOracle,
175: Rules rules, File genDir, File moduleOutDir) {
176: // This is a path used for non-hosted mode execution; therefore no caching.
177: this (typeOracle, propOracle, publicOracle, rules, genDir,
178: moduleOutDir, null);
179: }
180:
181: public String rebind(TreeLogger logger, String typeName)
182: throws UnableToCompleteException {
183:
184: logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger,
185: typeName, null);
186:
187: Rebinder rebinder = new Rebinder(typeOracle, propOracle,
188: publicOracle);
189: String result = rebinder.rebind(logger, typeName);
190:
191: Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
192:
193: return result;
194: }
195:
196: protected void onGeneratedTypes(String result, JClassType[] genTypes) {
197: }
198:
199: }
|