| /* |
| * Copyright 2010 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.javac; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.dev.util.StringInterner; |
| import com.google.gwt.dev.util.collect.HashMap; |
| import com.google.gwt.dev.util.collect.Lists; |
| |
| import java.io.Serializable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| /** |
| * Tracks dependencies from a {@link CompilationUnit} to {@link CompiledClass |
| * CompiledClasses}. |
| */ |
| class Dependencies implements Serializable { |
| /** |
| * Represents a {@link Ref} that has been previously persisted. |
| */ |
| static class Ref implements Serializable { |
| private final String internalName; |
| private final String hash; |
| |
| private Ref(CompiledClass cc) { |
| this(cc.getInternalName(), cc.getSignatureHash()); |
| } |
| |
| private Ref(String internalName, String hash) { |
| this.internalName = internalName; |
| this.hash = hash; |
| } |
| |
| public String getInternalName() { |
| return internalName; |
| } |
| |
| public String getSignatureHash() { |
| return hash; |
| } |
| } |
| |
| Map<String, Ref> qualified = new HashMap<String, Ref>(true); |
| Map<String, Ref> simple = new HashMap<String, Ref>(true); |
| private final List<String> apiRefs; |
| private final String myPackage; |
| |
| Dependencies() { |
| this.myPackage = ""; |
| this.apiRefs = Lists.create(); |
| } |
| |
| /** |
| * Initializes the set of simple and qualified dependency names, but does not |
| * resolve them. |
| */ |
| Dependencies(String myPackage, List<String> unresolvedQualified, List<String> unresolvedSimple, |
| List<String> apiRefs) { |
| this.myPackage = |
| StringInterner.get().intern((myPackage.length() == 0) ? "" : (myPackage + '.')); |
| for (String qualifiedRef : unresolvedQualified) { |
| qualified.put(qualifiedRef, null); |
| } |
| for (String simpleRef : unresolvedSimple) { |
| simple.put(simpleRef, null); |
| } |
| this.apiRefs = apiRefs; |
| } |
| |
| /** |
| * Returns the list of API references used by {@link TypeOracle} to determine type availability. |
| */ |
| List<String> getApiRefs() { |
| return apiRefs; |
| } |
| |
| /** |
| * Resolves unqualified dependencies against the global list of all valid |
| * classes. Must be called before {@link #validate(String, Map, Map)}. |
| */ |
| void resolve(Map<String, CompiledClass> allValidClasses) { |
| for (Entry<String, Ref> entry : qualified.entrySet()) { |
| CompiledClass cc = allValidClasses.get(entry.getKey()); |
| if (cc != null) { |
| entry.setValue(new Ref(cc)); |
| } |
| } |
| |
| for (Entry<String, Ref> entry : simple.entrySet()) { |
| CompiledClass cc = findBySimpleName(entry.getKey(), allValidClasses); |
| if (cc != null) { |
| entry.setValue(new Ref(cc)); |
| } |
| } |
| } |
| |
| /** |
| * Validate that all of my existing dependencies can be found in the global |
| * set of valid classes, and resolve to structurally identical APIs. |
| * |
| * @return <code>true</code> if all of my dependencies are valid |
| */ |
| boolean validate(TreeLogger logger, Map<String, CompiledClass> allValidClasses) { |
| for (Entry<String, Ref> entry : qualified.entrySet()) { |
| CompiledClass theirs = allValidClasses.get(entry.getKey()); |
| if (!validateClass(logger, entry, theirs)) { |
| return false; |
| } |
| } |
| for (Entry<String, Ref> entry : simple.entrySet()) { |
| CompiledClass theirs = findBySimpleName(entry.getKey(), allValidClasses); |
| if (!validateClass(logger, entry, theirs)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Tries to resolve a simple name using Java lookup rules, first checking the |
| * current package, then java.lang. |
| */ |
| private CompiledClass findBySimpleName(String ref, Map<String, CompiledClass> allValidClasses) { |
| CompiledClass cc = allValidClasses.get(myPackage + ref); |
| if (cc != null) { |
| return cc; |
| } |
| return allValidClasses.get("java.lang." + ref); |
| } |
| |
| /** |
| * Returns true if my class is the same as their class. Uses caching to avoid |
| * recomputing diffs. Updates the my entry to 'their' class if non-identical |
| * objects have the same structure. |
| */ |
| private boolean validateClass(TreeLogger logger, Entry<String, Ref> entry, CompiledClass theirs) { |
| Ref mine = entry.getValue(); |
| boolean result; |
| if ((mine == null) != (theirs == null)) { |
| if (logger.isLoggable(TreeLogger.DEBUG)) { |
| logger.log(TreeLogger.DEBUG, "Invalid ref: " + entry.getKey() + " mine: " |
| + (mine == null ? "null" : "not null") + " theirs: " |
| + (theirs == null ? "null" : "not null")); |
| } |
| result = false; |
| } else if (mine == null && theirs == null) { |
| // For package dependencies, both references being null is always the case |
| result = true; |
| } else if (mine.getSignatureHash().equals(theirs.getSignatureHash())) { |
| result = true; |
| } else { |
| if (logger.isLoggable(TreeLogger.DEBUG)) { |
| logger.log(TreeLogger.DEBUG, entry.getKey() + " isn't structurally same."); |
| } |
| result = false; |
| } |
| return result; |
| } |
| } |