Package com.google.gwt.dev.cfg

Source Code of com.google.gwt.dev.cfg.ModuleDef$LibraryDependencyEdge

/*
* 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.cfg;

import com.google.gwt.core.ext.Linker;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.javac.CompilationProblemReporter;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationStateBuilder;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.resource.ResourceOracle;
import com.google.gwt.dev.resource.impl.DefaultFilters;
import com.google.gwt.dev.resource.impl.PathPrefix;
import com.google.gwt.dev.resource.impl.PathPrefixSet;
import com.google.gwt.dev.resource.impl.ResourceFilter;
import com.google.gwt.dev.resource.impl.ResourceOracleImpl;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import com.google.gwt.dev.util.transitiveclosure.TransitiveClosureSolver;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
import com.google.gwt.thirdparty.guava.common.collect.ArrayListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.Iterators;
import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.ListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Queues;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;

/**
* Represents a module specification. In principle, this could be built without
* XML for unit tests.
*/
public class ModuleDef implements DepsInfoProvider {
  /**
   * Marks a module in a way that can be used to calculate the effective bounds of a library module
   * in a module tree.
   */
  public enum ModuleType {
    /**
     * A module that acts as just a bundle of source code to be included in a referencing library.
     * The contained code may form circular references with other source within the contiguous chunk
     * of modules made up of a library and it's subtree of filesets. As a result a fileset can not
     * be compiled on it's own and must be compiled as part of a library.
     */
    FILESET,
    /**
     * The default type of module. Libraries should be independently compilable and are expected to
     * explicitly declare all of their immediate dependencies and include paths and none of their
     * contained source should be part of a circular reference with any source outside the effective
     * bounds of the library.
     */
    LIBRARY
  }

  /**
   * When walking a module tree of mixed library and fileset modules, attributes are accumulated and
   * stored. But when compiling separately, some attributes should only be collected from the
   * effective target module. AttributeSource is used to classify these "in target module" and "out
   * of target module" times.
   */
  private enum AttributeSource {
    EXTERNAL_LIBRARY, TARGET_LIBRARY
  }

  /**
   * Encapsulates two String names which conceptually represent the starting and ending points of a
   * path.
   * <p>
   * For example in the following module dependency path ["com.acme.MyApp",
   * "com.acme.widgets.Widgets", "com.google.gwt.user.User"] the fromName is "com.acme.MyApp" and
   * the toName is "com.google.gwt.user.User" and together they form a PathEndNames instance.
   * <p>
   * The purpose of the class is to serve as a key by which to lookup the fileset path that connects
   * two libraries. It makes it possible to insert fileset entries into a circular module reference
   * report.
   */
  private static class LibraryDependencyEdge {

    private String fromName;
    private String toName;

    public LibraryDependencyEdge(String fromName, String toName) {
      this.fromName = fromName;
      this.toName = toName;
    }

    @Override
    public boolean equals(Object object) {
      if (object instanceof LibraryDependencyEdge) {
        LibraryDependencyEdge that = (LibraryDependencyEdge) object;
        return Objects.equal(this.fromName, that.fromName)
            && Objects.equal(this.toName, that.toName);
      }
      return false;
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(fromName, toName);
    }
  }

  private static final ResourceFilter NON_JAVA_RESOURCES = new ResourceFilter() {
    @Override
    public boolean allows(String path) {
      return !path.endsWith(".java") && !path.endsWith(".class");
    }
  };

  private static final Comparator<Map.Entry<String, ?>> REV_NAME_CMP =
      new Comparator<Map.Entry<String, ?>>() {
        @Override
        public int compare(Map.Entry<String, ?> entry1, Map.Entry<String, ?> entry2) {
          String key1 = entry1.getKey();
          String key2 = entry2.getKey();
          // intentionally reversed
          return key2.compareTo(key1);
        }
      };

  public static boolean isValidModuleName(String moduleName) {
    // Check for an empty string between two periods.
    if (moduleName.contains("..")) {
      return false;
    }
    // Insure the package name components are a valid Java ident.
    String[] parts = moduleName.split("\\.");
    for (int i = 0; i < parts.length - 1; i++) {
      String part = parts[i];
      if (!Util.isValidJavaIdent(part)) {
        return false;
      }
    }
    return true;
  }

  private static LinkedList<String> createExtendedCopy(LinkedList<String> list,
      String extendingElement) {
    LinkedList<String> extendedCopy = Lists.newLinkedList(list);
    extendedCopy.add(extendingElement);
    return extendedCopy;
  }

  /**
   * All resources found on the public path, specified by <public> directives in
   * modules (or the implicit ./public directory). Marked 'lazy' because it does not
   * start searching for resources until a query is made.
   */
  protected ResourceOracleImpl lazyPublicOracle;

  private final Set<String> activeLinkers = new LinkedHashSet<String>();

  private String activePrimaryLinker;

  /**
   * A set of URLs for <module>.gwtar files found on the classpath that correspond
   * to <module>.gwt.xml files loaded as a part of this module's nested load.
   *
   * @see com.google.gwt.dev.CompileModule
   */
  private final List<URL> archiveURLs = new ArrayList<URL>();

  private boolean collapseAllProperties;

  /**
   * Used to keep track of the current category of attribute source. When the attribute source is
   * TARGET_LIBRARY all attributes are accumulated but when it is EXTERNAL_LIBRARY there are some
   * attributes that are not collected.
   */
  private Deque<AttributeSource> currentAttributeSource = Lists.newLinkedList();

  private final DefaultFilters defaultFilters;

  private TransitiveClosureSolver<String> depsTransitiveClosureSolver;

  /**
   * The canonical module names of dependency library modules per depending library module.
   */
  private LinkedHashMultimap<String, String> directDepModuleNamesByModuleName =
      LinkedHashMultimap.create();

  private final List<String> entryPointTypeNames = new ArrayList<String>();

  /**
   * Names of free-standing compilable library modules that are depended upon by this module.
   */
  private final Set<String> externalLibraryCanonicalModuleNames = Sets.newLinkedHashSet();

  /**
   * A stack that keeps track of the type of the current module in the enterModule/exitModule tree.
   */
  private final Deque<ModuleType> currentModuleType = Lists.newLinkedList();

  /**
   * A stack that keeps track of the name of the current module in the enterModule/exitModule tree.
   */
  private final Deque<String> currentLibraryModuleNames = Lists.newLinkedList();

  /**
   * Records the canonical module names for filesets.
   */
  private Set<String> filesetModuleNames = Sets.newHashSet();

  /**
   * A mapping from a pair of starting and ending module names of a path to the ordered list of
   * names of fileset modules that connect those end points. The mapping allows one to discover what
   * path of filesets are connecting a pair of libraries when those libraries do not directly depend
   * on one another. Multiple paths may exist; this map contains only one of them.
   */
  private ListMultimap<LibraryDependencyEdge, String> filesetPathPerEdge =
      ArrayListMultimap.create();

  private final Set<File> gwtXmlFiles = new HashSet<File>();

  private final Set<String> inheritedModules = new HashSet<String>();

  /**
   * Contains files other than .java and .class files (such as CSS and PNG files) from either the
   * source or resource paths.
   */
  private ResourceOracleImpl lazyResourcesOracle;

  /**
   * Contains all files from the source path, specified by <source> and <super>
   * directives in modules (or the implicit ./client directory).
   */
  private ResourceOracleImpl lazySourceOracle;

  private final Map<String, Class<? extends Linker>> linkerTypesByName =
      new LinkedHashMap<String, Class<? extends Linker>>();

  private final long moduleDefCreationTime = System.currentTimeMillis();

  /**
   * Whether the module tree is being considered to be one contiguous compilable whole as opposed to
   * a tree of independently compilable library modules.
   */
  private final boolean monolithic;

  private final String name;

  /**
   * Must use a separate field to track override, because setNameOverride() will
   * get called every time a module is inherited, but only the last one matters.
   */
  private String nameOverride;

  private final Properties properties = new Properties();

  private PathPrefixSet publicPrefixSet = new PathPrefixSet();

  private Set<PathPrefix> resourcePrefixes = Sets.newHashSet();

  private final ResourceLoader resources;

  private boolean resourcesScanned;

  private final Rules rules = new Rules();

  private final Scripts scripts = new Scripts();

  private final Map<String, String> servletClassNamesByPath = new HashMap<String, String>();

  private final PathPrefixSet sourcePrefixSet;

  private final Styles styles = new Styles();

  /**
   * Canonical names of non-independently-compilable modules (mostly filesets) that together make up
   * the current module.
   */
  private final Set<String> targetLibraryCanonicalModuleNames = Sets.newLinkedHashSet();

  /**
   * A mapping from names of modules to path to their associated .gwt.xml file.
   */
  private Map<String, String> gwtXmlPathByModuleName = Maps.newHashMap();

  public ModuleDef(String name) {
    this(name, ResourceLoaders.forClassLoader(Thread.currentThread()));
  }

  public ModuleDef(String name, ResourceLoader resources) {
    this(name, resources, true, true);
  }

  /**
   * Constructs a ModuleDef.
   */
  public ModuleDef(String name, ResourceLoader resources, boolean monolithic,
      boolean mergePathPrefixes) {
    this.name = name;
    this.resources = resources;
    this.monolithic = monolithic;
    sourcePrefixSet = new PathPrefixSet(mergePathPrefixes);
    defaultFilters = new DefaultFilters();
  }

  /**
   * Register a {@code dependencyModuleName} as a directDependency of {@code currentModuleName}
   */
  public void addDirectDependency(String currentModuleName, String dependencyModuleName) {
    directDepModuleNamesByModuleName.put(currentModuleName, dependencyModuleName);
  }

  public synchronized void addEntryPointTypeName(String typeName) {
    if (!attributeIsForTargetLibrary()) {
      return;
    }
    entryPointTypeNames.add(typeName);
  }

  /**
   * Registers a module as a fileset.
   */
  public void addFileset(String filesetModuleName) {
    filesetModuleNames.add(filesetModuleName);
  }

  public void addGwtXmlFile(File xmlFile) {
    gwtXmlFiles.add(xmlFile);
  }

  public void addLinker(String name) {
    Class<? extends Linker> clazz = getLinker(name);
    assert clazz != null;

    LinkerOrder order = clazz.getAnnotation(LinkerOrder.class);
    if (order.value() == Order.PRIMARY) {
      if (activePrimaryLinker != null) {
        activeLinkers.remove(activePrimaryLinker);
      }
      activePrimaryLinker = name;
    }

    activeLinkers.add(name);
  }

  public synchronized void addPublicPackage(String publicPackage, String[] includeList,
      String[] excludeList, String[] skipList, boolean defaultExcludes, boolean caseSensitive) {
    if (lazyPublicOracle != null) {
      throw new IllegalStateException("Already normalized");
    }
    if (!attributeIsForTargetLibrary()) {
      return;
    }
    publicPrefixSet.add(new PathPrefix(getCurrentLibraryModuleName(), publicPackage, defaultFilters
        .customResourceFilter(includeList, excludeList, skipList, defaultExcludes, caseSensitive),
        true, excludeList));
  }

  public void addResourcePath(String resourcePath) {
    if (lazyResourcesOracle != null) {
      throw new IllegalStateException("Already normalized");
    }
    if (!attributeIsForTargetLibrary()) {
      return;
    }
    resourcePrefixes.add(new PathPrefix(getCurrentLibraryModuleName(), resourcePath,
        NON_JAVA_RESOURCES, false, null));
  }

  public void addRule(Rule rule) {
    if (!attributeIsForTargetLibrary()) {
      return;
    }
    getRules().prepend(rule);
  }

  public void addSourcePackage(String sourcePackage, String[] includeList, String[] excludeList,
      String[] skipList, boolean defaultExcludes, boolean caseSensitive) {
    addSourcePackageImpl(sourcePackage, includeList, excludeList, skipList, defaultExcludes,
        caseSensitive, false);
  }

  public void addSourcePackageImpl(String sourcePackage, String[] includeList,
      String[] excludeList, String[] skipList, boolean defaultExcludes, boolean caseSensitive,
      boolean isSuperSource) {
    if (lazySourceOracle != null) {
      throw new IllegalStateException("Already normalized");
    }
    if (!attributeIsForTargetLibrary()) {
      return;
    }
    PathPrefix pathPrefix = new PathPrefix(getCurrentLibraryModuleName(),
        sourcePackage, defaultFilters.customJavaFilter(includeList, excludeList, skipList,
            defaultExcludes, caseSensitive), isSuperSource, excludeList);
    sourcePrefixSet.add(pathPrefix);
  }

  public void addSuperSourcePackage(String superSourcePackage, String[] includeList,
      String[] excludeList, String[] skipList, boolean defaultExcludes, boolean caseSensitive) {
    addSourcePackageImpl(superSourcePackage, includeList, excludeList, skipList, defaultExcludes,
        caseSensitive, true);
  }

  /**
   * Free up memory no longer needed in later compile stages. After calling this
   * method, the ResourceOracle will be empty and unusable. Calling
   * {@link #ensureResourcesScanned()} will restore them.
   */
  public synchronized void clear() {
    if (lazySourceOracle != null) {
      lazySourceOracle.clear();
    }
    rules.dispose();
  }

  public void clearEntryPoints() {
    entryPointTypeNames.clear();
  }

  /**
   * Associate a Linker class with a symbolic name. If the name had been
   * previously assigned, this method will redefine the name. If the redefined
   * linker had been previously added to the set of active linkers, the old
   * active linker will be replaced with the new linker.
   */
  public void defineLinker(TreeLogger logger, String name, Class<? extends Linker> linker)
      throws UnableToCompleteException {
    Class<? extends Linker> old = getLinker(name);
    if (old != null) {
      // Redefining an existing name
      if (activePrimaryLinker.equals(name)) {
        // Make sure the new one is also a primary linker
        if (!linker.getAnnotation(LinkerOrder.class).value().equals(Order.PRIMARY)) {
          logger.log(TreeLogger.ERROR, "Redefining primary linker " + name
              + " with non-primary implementation " + linker.getName());
          throw new UnableToCompleteException();
        }

      } else if (activeLinkers.contains(name)) {
        // Make sure it's a not a primary linker
        if (linker.getAnnotation(LinkerOrder.class).value().equals(Order.PRIMARY)) {
          logger.log(TreeLogger.ERROR, "Redefining non-primary linker " + name
              + " with primary implementation " + linker.getName());
          throw new UnableToCompleteException();
        }
      }
    }
    linkerTypesByName.put(name, linker);
  }

  /**
   * Called as module tree parsing enters and exits individual module file of various types and so
   * provides the ModuleDef with an opportunity to keep track of what context it is currently
   * operating.<br />
   *
   * At the moment the ModuleDef uses it's monolithic property in combination with the entering
   * modules ModuleType to updates its idea of attribute source.
   */
  public void enterModule(ModuleType moduleType, String canonicalModuleName) {
    currentModuleType.push(moduleType);
    if (moduleType == ModuleType.FILESET) {
      addFileset(canonicalModuleName);
    } else {
      currentLibraryModuleNames.push(canonicalModuleName);
    }
    if (monolithic) {
      // When you're monolithic the module tree is all effectively one giant library.
      currentAttributeSource.push(AttributeSource.TARGET_LIBRARY);
      targetLibraryCanonicalModuleNames.add(canonicalModuleName);
      return;
    }

    // When compiling separately it's not legal to have a fileset module at the root.
    Preconditions.checkArgument(
        !(currentAttributeSource.isEmpty() && moduleType == ModuleType.FILESET));

    // The compilation target always starts at the root of the graph so if there is a library type
    // module at the root of the graph.
    if (currentAttributeSource.isEmpty() && moduleType == ModuleType.LIBRARY) {
      // Then any attributes you see are your own.
      currentAttributeSource.push(AttributeSource.TARGET_LIBRARY);
      targetLibraryCanonicalModuleNames.add(canonicalModuleName);
      return;
    }

    // If at the moment any attributes you see are your own.
    if (currentAttributeSource.peek() == AttributeSource.TARGET_LIBRARY) {
      // And you enter a fileset module
      if (moduleType == ModuleType.FILESET) {
        // Then any attributes you see are still your own.
        currentAttributeSource.push(AttributeSource.TARGET_LIBRARY);
        targetLibraryCanonicalModuleNames.add(canonicalModuleName);
      } else {
        // But if you enter a library module then any attributes you see are external.
        currentAttributeSource.push(AttributeSource.EXTERNAL_LIBRARY);
        addExternalLibraryCanonicalModuleName(canonicalModuleName);
      }
    } else if (currentAttributeSource.peek() == AttributeSource.EXTERNAL_LIBRARY) {
      // If your current attribute source is an external library then regardless of whether you
      // enter another fileset or library your attribute source is still external.
      currentAttributeSource.push(AttributeSource.EXTERNAL_LIBRARY);
    }
  }

  public void exitModule() {
    if (currentModuleType.pop() == ModuleType.LIBRARY) {
      currentLibraryModuleNames.pop();
    }
    currentAttributeSource.pop();
  }

  public synchronized Resource findPublicFile(String partialPath) {
    ensureResourcesScanned();
    return lazyPublicOracle.getResourceMap().get(partialPath);
  }

  public synchronized String findServletForPath(String actual) {
    // Walk in backwards sorted order to find the longest path match first.
    Set<Entry<String, String>> entrySet = servletClassNamesByPath.entrySet();
    Entry<String, String>[] entries = Util.toArray(Entry.class, entrySet);
    Arrays.sort(entries, REV_NAME_CMP);
    for (int i = 0, n = entries.length; i < n; ++i) {
      String mapping = entries[i].getKey();
      /*
       * Ensure that URLs that match the servlet mapping, including those that
       * have additional path_info, get routed to the correct servlet.
       *
       * See "Inside Servlets", Second Edition, pg. 208
       */
      if (actual.equals(mapping) || actual.startsWith(mapping + "/")) {
        return entries[i].getValue();
      }
    }
    return null;
  }

  /**
   * Returns the Resource for a source file if it is found; <code>null</code>
   * otherwise.
   *
   * @param partialPath the partial path of the source file
   * @return the resource for the requested source file
   */
  public synchronized Resource findSourceFile(String partialPath) {
    ensureResourcesScanned();
    return lazySourceOracle.getResourceMap().get(partialPath);
  }

  public Set<String> getActiveLinkerNames() {
    return new LinkedHashSet<String>(activeLinkers);
  }

  public Set<Class<? extends Linker>> getActiveLinkers() {
    Set<Class<? extends Linker>> toReturn = new LinkedHashSet<Class<? extends Linker>>();
    for (String linker : activeLinkers) {
      assert linkerTypesByName.containsKey(linker) : linker;
      toReturn.add(linkerTypesByName.get(linker));
    }
    return toReturn;
  }

  public Class<? extends Linker> getActivePrimaryLinker() {
    assert linkerTypesByName.containsKey(activePrimaryLinker) : activePrimaryLinker;
    return linkerTypesByName.get(activePrimaryLinker);
  }

  /**
   * Returns URLs to fetch archives of precompiled compilation units.
   *
   * @see com.google.gwt.dev.CompileModule
   */
  public Collection<URL> getAllCompilationUnitArchiveURLs() {
    return Collections.unmodifiableCollection(archiveURLs);
  }

  /**
   * Strictly for statistics gathering. There is no guarantee that the source
   * oracle has been initialized.
   */
  public String[] getAllSourceFiles() {
    ensureResourcesScanned();
    return lazySourceOracle.getPathNames().toArray(Empty.STRINGS);
  }

  public synchronized ResourceOracle getBuildResourceOracle() {
    if (lazyResourcesOracle == null) {
      lazyResourcesOracle = new ResourceOracleImpl(TreeLogger.NULL, resources);
      PathPrefixSet pathPrefixes = lazySourceOracle.getPathPrefixes();

      // For backwards compatibility, register source resource paths as build resource paths as
      // well.
      PathPrefixSet newPathPrefixes = new PathPrefixSet();
      for (PathPrefix pathPrefix : pathPrefixes.values()) {
        newPathPrefixes.add(new PathPrefix(pathPrefix.getModuleName(), pathPrefix.getPrefix(),
            NON_JAVA_RESOURCES, pathPrefix.shouldReroot(), null));
      }

      // Register build resource paths.
      for (PathPrefix resourcePathPrefix : resourcePrefixes) {
        newPathPrefixes.add(resourcePathPrefix);
      }
      lazyResourcesOracle.setPathPrefixes(newPathPrefixes);
      lazyResourcesOracle.scanResources(TreeLogger.NULL);
    } else {
      ensureResourcesScanned();
    }
    return lazyResourcesOracle;
  }

  /**
   * Returns the physical name for the module by which it can be found in the
   * classpath.
   */
  public String getCanonicalName() {
    return name;
  }

  public CompilationState getCompilationState(TreeLogger logger, CompilerContext compilerContext)
      throws UnableToCompleteException {
    ensureResourcesScanned();
    CompilationState compilationState = CompilationStateBuilder.buildFrom(
        logger, compilerContext, compilerContext.getSourceResourceOracle().getResources());
    checkForSeedTypes(logger, compilationState);
    return compilationState;
  }

  /**
   * The module names for its direct non fileset dependents.
   */
  public Collection<String> getDirectDependencies(String libraryModuleName) {
    assert !filesetModuleNames.contains(libraryModuleName);
    return directDepModuleNamesByModuleName.get(libraryModuleName);
  }

  public synchronized String[] getEntryPointTypeNames() {
    final int n = entryPointTypeNames.size();
    return entryPointTypeNames.toArray(new String[n]);
  }

  /**
   * Returns the names of free-standing compilable library modules that are depended upon by this
   * module.
   */
  public Set<String> getExternalLibraryCanonicalModuleNames() {
    return externalLibraryCanonicalModuleNames;
  }

  public List<String> getFileSetPathBetween(String fromModuleName, String toModuleName) {
    return filesetPathPerEdge.get(new LibraryDependencyEdge(fromModuleName, toModuleName));
  }

  public synchronized String getFunctionName() {
    return getName().replace('.', '_');
  }

  public List<Rule> getGeneratorRules() {
    return ImmutableList.copyOf(
        Iterators.filter(rules.iterator(), Predicates.instanceOf(RuleGenerateWith.class)));
  }

  @Override
  public String getGwtXmlFilePath(String moduleName) {
    return gwtXmlPathByModuleName.get(moduleName);
  }

  public Class<? extends Linker> getLinker(String name) {
    return linkerTypesByName.get(name);
  }

  public Map<String, Class<? extends Linker>> getLinkers() {
    return linkerTypesByName;
  }

  public synchronized String getName() {
    return nameOverride != null ? nameOverride : name;
  }

  /**
   * The properties that have been defined.
   */
  public synchronized Properties getProperties() {
    return properties;
  }

  public ResourceOracleImpl getPublicResourceOracle() {
    ensureResourcesScanned();
    return lazyPublicOracle;
  }

  public long getResourceLastModified() {
    long resourceLastModified = 1000;
    Set<Resource> allResources = Sets.newHashSet();
    allResources.addAll(getPublicResourceOracle().getResources());
    allResources.addAll(getSourceResourceOracle().getResources());
    allResources.addAll(getBuildResourceOracle().getResources());
    for (Resource resource : allResources) {
      resourceLastModified = Math.max(resourceLastModified, resource.getLastModified());
    }
    return resourceLastModified;
  }

  public Set<Resource> getResourcesNewerThan(long modificationTime) {
    Set<Resource> newerResources = Sets.newHashSet();

    for (Resource resource : getPublicResourceOracle().getResources()) {
      if (resource.getLastModified() > modificationTime) {
        newerResources.add(resource);
      }
    }

    for (Resource resource : getSourceResourceOracle().getResources()) {
      if (resource.getLastModified() > modificationTime) {
        newerResources.add(resource);
      }
    }

    for (Resource resource : getBuildResourceOracle().getResources()) {
      if (resource.getLastModified() > modificationTime) {
        newerResources.add(resource);
      }
    }

    return newerResources;
  }

  /**
   * Gets a reference to the internal rules for this module def.
   */
  public synchronized Rules getRules() {
    return rules;
  }

  /**
   * Gets a reference to the internal scripts list for this module def.
   */
  public Scripts getScripts() {
    return scripts;
  }

  public synchronized String[] getServletPaths() {
    return servletClassNamesByPath.keySet().toArray(Empty.STRINGS);
  }

  @Override
  public Set<String> getSourceModuleNames(String typeSourceName) {
    ensureResourcesScanned();
    return lazySourceOracle.getSourceModulesByTypeSourceName().get(typeSourceName);
  }

  public synchronized ResourceOracle getSourceResourceOracle() {
    ensureResourcesScanned();
    return lazySourceOracle;
  }

  /**
   * Gets a reference to the internal styles list for this module def.
   */
  public Styles getStyles() {
    return styles;
  }

  /**
   * Returns the names of non-independently-compilable modules (mostly filesets) that together make
   * up the current module.
   */
  public Set<String> getTargetLibraryCanonicalModuleNames() {
    return targetLibraryCanonicalModuleNames;
  }

  @Override
  public Set<String> getTransitiveDepModuleNames(String libraryModuleName) {
    return getDepsTransitiveClosureSolver().getReachableNodes(libraryModuleName);
  }

  public boolean isGwtXmlFileStale() {
    return lastModified() > moduleDefCreationTime;
  }

  public boolean isInherited(String moduleName) {
    return inheritedModules.contains(moduleName);
  }

  public long lastModified() {
    long lastModified = 0;
    for (File xmlFile : gwtXmlFiles) {
      if (xmlFile.exists()) {
        lastModified = Math.max(lastModified, xmlFile.lastModified());
      }
    }
    return lastModified > 0 ? lastModified : moduleDefCreationTime;
  }

  /**
   * For convenience in unit tests, servlets can be automatically loaded and
   * mapped in the embedded web server. If a servlet is already mapped to the
   * specified path, it is replaced.
   *
   * @param path the url path at which the servlet resides
   * @param servletClassName the name of the servlet to publish
   */
  public synchronized void mapServlet(String path, String servletClassName) {
    servletClassNamesByPath.put(path, servletClassName);
  }

  public void printOverlappingSourceWarnings(TreeLogger logger) {
    lazySourceOracle.printOverlappingModuleIncludeWarnings(logger);
  }

  public void recordModuleGwtXmlFile(String moduleName, String fileSourcePath) {
    gwtXmlPathByModuleName.put(moduleName, fileSourcePath);
  }

  public synchronized void refresh() {
    resourcesScanned = false;
  }

  /**
   * Mainly for testing and decreasing compile times.
   */
  public void setCollapseAllProperties(boolean collapse) {
    collapseAllProperties = collapse;
  }

  /**
   * Override the module's apparent name. Setting this value to
   * <code>null<code>will disable the name override.
   */
  public synchronized void setNameOverride(String nameOverride) {
    this.nameOverride = nameOverride;
  }

  void addBindingPropertyDefinedValue(BindingProperty bindingProperty, String token) {
    if (attributeIsForTargetLibrary()) {
      bindingProperty.addTargetLibraryDefinedValue(bindingProperty.getRootCondition(), token);
    } else {
      bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), token);
    }
  }

  void addCompilationUnitArchiveURL(URL url) {
    archiveURLs.add(url);
  }

  void addConfigurationPropertyValue(ConfigurationProperty configurationProperty, String value) {
    if (attributeIsForTargetLibrary()) {
      configurationProperty.addTargetLibraryValue(value);
    } else {
      configurationProperty.addValue(value);
    }
  }

  void addInheritedModules(String moduleName) {
    inheritedModules.add(moduleName);
  }

  /**
   * The final method to call when everything is setup. Before calling this
   * method, several of the getter methods may not be called. After calling this
   * method, the add methods may not be called.
   *
   * @param logger Logs the activity.
   */
  synchronized void normalize(TreeLogger logger) {
    Event moduleDefNormalize =
        SpeedTracerLogger.start(CompilerEventType.MODULE_DEF, "phase", "normalize");

    // Compute compact dependency graph;
    computeLibraryDependencyGraph();

    // Normalize property providers.
    //
    for (Property current : getProperties()) {
      if (current instanceof BindingProperty) {
        BindingProperty prop = (BindingProperty) current;

        if (collapseAllProperties) {
          prop.addCollapsedValues("*");
        }

        prop.normalizeCollapsedValues();

        /*
         * Create a default property provider for any properties with more than
         * one possible value and no existing provider.
         */
        if (prop.getProvider() == null && prop.getConstrainedValue() == null) {
          String src = "{";
          src += "return __gwt_getMetaProperty(\"";
          src += prop.getName();
          src += "\"); }";
          prop.setProvider(new PropertyProvider(src));
        }
      }
    }

    // Create the public path.
    TreeLogger branch = Messages.PUBLIC_PATH_LOCATIONS.branch(logger, null);
    lazyPublicOracle = new ResourceOracleImpl(branch, resources);
    lazyPublicOracle.setPathPrefixes(publicPrefixSet);

    // Create the source path.
    branch = Messages.SOURCE_PATH_LOCATIONS.branch(logger, null);
    lazySourceOracle = new ResourceOracleImpl(branch, resources);
    lazySourceOracle.setPathPrefixes(sourcePrefixSet);

    moduleDefNormalize.end();
  }

  void setConfigurationPropertyValue(ConfigurationProperty configurationProperty,
      String value) {
    if (attributeIsForTargetLibrary()) {
      configurationProperty.setTargetLibraryValue(value);
    } else {
      configurationProperty.setValue(value);
    }
  }

  private void addExternalLibraryCanonicalModuleName(String externalLibraryCanonicalModuleName) {
    // Ignore circular dependencies on self.
    if (externalLibraryCanonicalModuleName.equals(getCanonicalName())) {
      return;
    }
    externalLibraryCanonicalModuleNames.add(externalLibraryCanonicalModuleName);
  }

  private boolean attributeIsForTargetLibrary() {
    return currentAttributeSource.isEmpty()
        || currentAttributeSource.peek() == AttributeSource.TARGET_LIBRARY;
  }

  private void checkForSeedTypes(TreeLogger logger, CompilationState compilationState)
      throws UnableToCompleteException {
    // Sanity check the seed types and don't even start it they're missing.
    boolean seedTypesMissing = false;
    TypeOracle typeOracle = compilationState.getTypeOracle();
    if (typeOracle.findType("java.lang.Object") == null) {
      CompilationProblemReporter.logMissingTypeErrorWithHints(logger, "java.lang.Object",
          compilationState);
      seedTypesMissing = true;
    } else {
      TreeLogger branch = logger.branch(TreeLogger.TRACE, "Finding entry point classes", null);
      String[] typeNames = getEntryPointTypeNames();
      for (int i = 0; i < typeNames.length; i++) {
        String typeName = typeNames[i];
        if (typeOracle.findType(typeName) == null) {
          CompilationProblemReporter.logMissingTypeErrorWithHints(branch, typeName,
              compilationState);
          seedTypesMissing = true;
        }
      }
    }

    if (seedTypesMissing) {
      throw new UnableToCompleteException();
    }
  }

  /**
   * Reduce the direct dependency graph to exclude filesets.
   */
  private void computeLibraryDependencyGraph() {
    for (String moduleName : Lists.newArrayList(directDepModuleNamesByModuleName.keySet())) {
      Set<String> libraryModules = Sets.newHashSet();
      Set<String> filesetsProcessed = Sets.newHashSet();

      // Direct dependents might be libraries or fileset, so add them to a queue of modules
      // to process.
      Queue<LinkedList<String>> modulePathsToProcess = Queues.newArrayDeque();
      Collection<String> directDependencyModuleNames =
          directDepModuleNamesByModuleName.get(moduleName);
      for (String directDependencyModuleName : directDependencyModuleNames) {
        modulePathsToProcess.add(Lists.newLinkedList(ImmutableList.of(directDependencyModuleName)));
      }
      while (!modulePathsToProcess.isEmpty()) {
        LinkedList<String> dependentModuleNamePath = modulePathsToProcess.poll();
        String dependentModuleName = dependentModuleNamePath.getLast();

        boolean isLibrary = !filesetModuleNames.contains(dependentModuleName);
        if (isLibrary) {
          // Current dependent is not a fileset so it will be in the list of dependents unless it is
          // the library itself.
          if (!moduleName.equals(dependentModuleName)) {
            libraryModules.add(dependentModuleName);

            dependentModuleNamePath.removeLast();
            filesetPathPerEdge.putAll(new LibraryDependencyEdge(moduleName, dependentModuleName),
                dependentModuleNamePath);
          }
          continue;
        }
        // Mark the module as processed to avoid an infinite loop.
        filesetsProcessed.add(dependentModuleName);

        // Get the dependencies of the dependent module under consideration and add all those
        // that have not been already processed to the queue of modules to process.
        Set<String> unProcessedModules =
            Sets.newHashSet(directDepModuleNamesByModuleName.get(dependentModuleName));
        unProcessedModules.removeAll(filesetsProcessed);
        for (String unProcessedModule : unProcessedModules) {
          modulePathsToProcess.add(createExtendedCopy(dependentModuleNamePath, unProcessedModule));
        }
      }
      // Rewrite the dependents with the set just computed.
      directDepModuleNamesByModuleName.replaceValues(moduleName, libraryModules);
    }
    // Remove all fileset entries.
    directDepModuleNamesByModuleName.removeAll(filesetModuleNames);
  }

  private synchronized void ensureResourcesScanned() {
    if (resourcesScanned) {
      return;
    }
    resourcesScanned = true;

    Event moduleDefEvent = SpeedTracerLogger.start(
        CompilerEventType.MODULE_DEF, "phase", "refresh", "module", getName());
    if (lazyResourcesOracle != null) {
      lazyResourcesOracle.scanResources(TreeLogger.NULL);
    }
    lazyPublicOracle.scanResources(TreeLogger.NULL);
    lazySourceOracle.scanResources(TreeLogger.NULL);
    moduleDefEvent.end();
  }

  private String getCurrentLibraryModuleName() {
    return currentLibraryModuleNames.isEmpty() ? "" : currentLibraryModuleNames.peek();
  }

  private TransitiveClosureSolver<String> getDepsTransitiveClosureSolver() {
    if (depsTransitiveClosureSolver == null) {
      depsTransitiveClosureSolver = new TransitiveClosureSolver<String>();
      for (Entry<String, String> entry : directDepModuleNamesByModuleName.entries()) {
        depsTransitiveClosureSolver.addConnection(entry.getKey(), entry.getValue());
      }
    }
    return depsTransitiveClosureSolver;
  }
}
TOP

Related Classes of com.google.gwt.dev.cfg.ModuleDef$LibraryDependencyEdge

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.