blob: 23ee903828b183b92fc3dc2a142e4ee416e4c703 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.shell;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.dev.cfg.Rule;
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.log.speedtracer.DevModeEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Implements rebind logic in terms of a variety of other well-known oracles.
*/
public class StandardRebindOracle implements RebindOracle {
/**
* Makes the actual deferred binding decision by examining rules.
*/
private final class Rebinder {
private final Set<Rule> usedRules = new HashSet<Rule>();
private final List<String> usedTypeNames = new ArrayList<String>();
public String rebind(TreeLogger logger, String typeName,
ArtifactAcceptor artifactAcceptor) throws UnableToCompleteException {
Event rebindEvent = SpeedTracerLogger.start(DevModeEventType.REBIND, "Type Name", typeName);
try {
genCtx.setPropertyOracle(propOracle);
String result = tryRebind(logger, typeName);
if (artifactAcceptor != null) {
// Go ahead and call finish() to accept new artifacts.
ArtifactSet newlyGeneratedArtifacts = genCtx.finish(logger);
if (!newlyGeneratedArtifacts.isEmpty()) {
artifactAcceptor.accept(logger, newlyGeneratedArtifacts);
}
}
if (result == null) {
result = typeName;
}
return result;
} finally {
rebindEvent.end();
}
}
private String tryRebind(TreeLogger logger, String typeName)
throws UnableToCompleteException {
if (usedTypeNames.contains(typeName)) {
// Found a cycle.
//
String[] cycle = Util.toArray(String.class, usedTypeNames);
Messages.UNABLE_TO_REBIND_DUE_TO_CYCLE_IN_RULES.log(logger, cycle, null);
throw new UnableToCompleteException();
}
// Remember that we've seen this one.
//
usedTypeNames.add(typeName);
// Make the rebind decision.
//
if (rules.isEmpty()) {
logger.log(TreeLogger.DEBUG,
"No rules are defined, so no substitution can occur", null);
return null;
}
for (Iterator<Rule> iter = rules.iterator(); iter.hasNext();) {
Rule rule = iter.next();
// Branch the logger.
//
TreeLogger branch = Messages.TRACE_CHECKING_RULE.branch(logger, rule,
null);
if (rule.isApplicable(branch, genCtx, typeName)) {
// See if this rule has already been used. This is needed to prevent
// infinite loops with 'when-assignable' conditions.
//
if (!usedRules.contains(rule)) {
usedRules.add(rule);
Messages.TRACE_RULE_MATCHED.log(logger, null);
// Invoke the rule.
//
return rule.realize(logger, genCtx, typeName);
} else {
// We are skipping this rule because it has already been used
// in a previous iteration.
//
}
} else {
Messages.TRACE_RULE_DID_NOT_MATCH.log(logger, null);
}
}
// No matching rule for this type.
//
return null;
}
}
private final Map<String, String> cache = new HashMap<String, String>();
private final StandardGeneratorContext genCtx;
private final PropertyOracle propOracle;
private final Rules rules;
public StandardRebindOracle(PropertyOracle propOracle, Rules rules,
StandardGeneratorContext genCtx) {
this.propOracle = propOracle;
this.rules = rules;
this.genCtx = genCtx;
}
public String rebind(TreeLogger logger, String typeName)
throws UnableToCompleteException {
return rebind(logger, typeName, null);
}
public String rebind(TreeLogger logger, String typeName,
ArtifactAcceptor artifactAcceptor) throws UnableToCompleteException {
String result = cache.get(typeName);
if (result == null) {
logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null);
Rebinder rebinder = new Rebinder();
result = rebinder.rebind(logger, typeName, artifactAcceptor);
cache.put(typeName, result);
Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
}
return result;
}
}