Package com.google.gwt.core.ext.linker.impl

Source Code of com.google.gwt.core.ext.linker.impl.SelectionScriptLinker

/*
* 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.core.ext.linker.impl;

import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.AbstractLinker;
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.SoftPermutation;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.TextOutput;
import com.google.gwt.dev.util.Util;
import com.google.gwt.util.tools.Utility;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
* A base class for Linkers that use an external script to boostrap the GWT
* module. This implementation injects JavaScript Snippets into a JS program
* defined in an external file.
*/
public abstract class SelectionScriptLinker extends AbstractLinker {
  /**
   * TODO(bobv): Move this class into c.g.g.core.linker when HostedModeLinker
   * goes away?
   */


  /**
   * File name for computeScriptBase.js.
   */
  protected static final String COMPUTE_SCRIPT_BASE_JS = "com/google/gwt/core/ext/linker/impl/computeScriptBase.js";

  /**
   * The extension added to demand-loaded fragment files.
   */
  protected static final String FRAGMENT_EXTENSION = ".cache.js";

  /**
   * A subdirectory to hold all the generated fragments.
   */
  protected static final String FRAGMENT_SUBDIR = "deferredjs";

  /**
   * Utility class to handle insertion of permutations code.
   */
  protected static PermutationsUtil permutationsUtil = new PermutationsUtil();
 
  /**
   * File name for processMetas.js.
   */
  protected static final String PROCESS_METAS_JS = "com/google/gwt/core/ext/linker/impl/processMetasOld.js";


  protected static void replaceAll(StringBuffer buf, String search,
      String replace) {
    int len = search.length();
    for (int pos = buf.indexOf(search); pos >= 0; pos = buf.indexOf(search,
        pos + 1)) {
      buf.replace(pos, pos + len, replace);
    }
  }

  /**
   * This method is left in place for existing subclasses of
   * SelectionScriptLinker that have not been upgraded for the sharding API.
   */
  @Override
  public ArtifactSet link(TreeLogger logger, LinkerContext context,
      ArtifactSet artifacts) throws UnableToCompleteException {
    ArtifactSet toReturn = link(logger, context, artifacts, true);
    toReturn = link(logger, context, toReturn, false);
    return toReturn;
  }

  @Override
  public ArtifactSet link(TreeLogger logger, LinkerContext context,
      ArtifactSet artifacts, boolean onePermutation)
      throws UnableToCompleteException {
    if (onePermutation) {
      ArtifactSet toReturn = new ArtifactSet(artifacts);

      /*
       * Support having multiple compilation results because this method is also
       * called from the legacy link method.
       */
      for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
        toReturn.addAll(doEmitCompilation(logger, context, compilation, artifacts));
        maybeAddHostedModeFile(logger, context, toReturn, compilation);
      }
      return toReturn;
    } else {
      permutationsUtil.setupPermutationsMap(artifacts);
      ArtifactSet toReturn = new ArtifactSet(artifacts);
      EmittedArtifact art = emitSelectionScript(logger, context, artifacts);
      if (art != null) {
        toReturn.add(art);
      }
      maybeOutputPropertyMap(logger, context, toReturn);
      maybeAddHostedModeFile(logger, context, toReturn, null);
      return toReturn;
    }
  }

  @Override
  public boolean supportsDevModeInJunit(LinkerContext context) {
    return (getHostedFilename() != "");
  }
 
  protected Collection<Artifact<?>> doEmitCompilation(TreeLogger logger,
      LinkerContext context, CompilationResult result, ArtifactSet artifacts)
      throws UnableToCompleteException {
    String[] js = result.getJavaScript();
    byte[][] bytes = new byte[js.length][];
    bytes[0] = generatePrimaryFragment(logger, context, result, js, artifacts);
    for (int i = 1; i < js.length; i++) {
      bytes[i] = Util.getBytes(generateDeferredFragment(logger, context, i,
          js[i]));
    }

    Collection<Artifact<?>> toReturn = new ArrayList<Artifact<?>>();
    toReturn.add(emitBytes(logger, bytes[0], result.getStrongName()
        + getCompilationExtension(logger, context)));
    for (int i = 1; i < js.length; i++) {
      toReturn.add(emitBytes(logger, bytes[i], FRAGMENT_SUBDIR + File.separator
          + result.getStrongName() + File.separator + i + FRAGMENT_EXTENSION));
    }

    toReturn.addAll(emitSelectionInformation(result.getStrongName(), result));
    return toReturn;
  }

  protected EmittedArtifact emitSelectionScript(TreeLogger logger,
      LinkerContext context, ArtifactSet artifacts)
      throws UnableToCompleteException {
    /*
     * Last modified is important to keep Development Mode refreses from
     * clobbering Production Mode compiles. We set the timestamp on the
     * Development Mode selection script to the same mod time as the module (to
     * allow updates). For Production Mode, we just set it to now.
     */
    long lastModified;
    if (permutationsUtil.getPermutationsMap().isEmpty()) {
      lastModified = context.getModuleLastModified();
    } else {
      lastModified = System.currentTimeMillis();
    }
    String ss = generateSelectionScript(logger, context, artifacts);
    return emitString(logger, ss, context.getModuleName()
        + ".nocache.js", lastModified);
  }
 
  /**
   * Generate a selection script. The selection information should previously
   * have been scanned using {@link #setupPermutationsMap(ArtifactSet)}.
   */
  protected String fillSelectionScriptTemplate(StringBuffer selectionScript,
      TreeLogger logger, LinkerContext context, ArtifactSet artifacts,
      CompilationResult result) throws
      UnableToCompleteException {
    String computeScriptBase;
    String processMetas;
    try {
      computeScriptBase = Utility.getFileFromClassPath(COMPUTE_SCRIPT_BASE_JS);
      processMetas = Utility.getFileFromClassPath(PROCESS_METAS_JS);
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Unable to read selection script template",
          e);
      throw new UnableToCompleteException();
    }
    replaceAll(selectionScript, "__COMPUTE_SCRIPT_BASE__", computeScriptBase);
    replaceAll(selectionScript, "__PROCESS_METAS__", processMetas);
   
    selectionScript = ResourceInjectionUtil.injectResources(selectionScript, artifacts);
    permutationsUtil.addPermutationsJs(selectionScript, logger, context);
   
    replaceAll(selectionScript, "__MODULE_FUNC__",
        context.getModuleFunctionName());
    replaceAll(selectionScript, "__MODULE_NAME__", context.getModuleName());
    replaceAll(selectionScript, "__HOSTED_FILENAME__", getHostedFilename());

    return selectionScript.toString();
  }
 
  /**
   * @param logger a TreeLogger
   * @param context a LinkerContext
   * @param fragment the fragment number
   */
  protected String generateDeferredFragment(TreeLogger logger,
      LinkerContext context, int fragment, String js) {
    return js;
  }

  /**
   * Generate the primary fragment. The default implementation is based on
   * {@link #getModulePrefix(TreeLogger, LinkerContext, String, int)} and
   * {@link #getModuleSuffix(TreeLogger, LinkerContext)}.
   */
  protected byte[] generatePrimaryFragment(TreeLogger logger,
      LinkerContext context, CompilationResult result, String[] js,
      ArtifactSet artifacts) throws UnableToCompleteException {
    TextOutput to = new DefaultTextOutput(context.isOutputCompact());
    to.print(generatePrimaryFragmentString(
        logger, context, result, js[0], js.length, artifacts));
    return Util.getBytes(to.toString());
  }
 
  protected String generatePrimaryFragmentString(TreeLogger logger,
      LinkerContext context, CompilationResult result, String js, int length,
      ArtifactSet artifacts)
  throws UnableToCompleteException {
    StringBuffer b = new StringBuffer();
    String strongName = result == null ? "" : result.getStrongName();
    b.append(getModulePrefix(logger, context, strongName, length));
    b.append(js);
    b.append(getModuleSuffix(logger, context));
    return wrapPrimaryFragment(logger, context, b.toString(), artifacts, result);
  }
 
  protected String generateSelectionScript(TreeLogger logger,
      LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
    return generateSelectionScript(logger, context, artifacts, null);
  }
 
  protected String generateSelectionScript(TreeLogger logger,
        LinkerContext context, ArtifactSet artifacts, CompilationResult result)
        throws UnableToCompleteException {
    String selectionScriptText;
    StringBuffer buffer = readFileToStringBuffer(
        getSelectionScriptTemplate(logger,context), logger);
    selectionScriptText = fillSelectionScriptTemplate(
        buffer, logger, context, artifacts, result);
    selectionScriptText =
      context.optimizeJavaScript(logger, selectionScriptText);
    return selectionScriptText;
  }
 
  protected abstract String getCompilationExtension(TreeLogger logger,
      LinkerContext context) throws UnableToCompleteException;

  protected String getHostedFilename() {
    return "";
  }
 
  /**
   * Compute the beginning of a JavaScript file that will hold the main module
   * implementation.
   */
  protected abstract String getModulePrefix(TreeLogger logger,
      LinkerContext context, String strongName)
      throws UnableToCompleteException;
 
  /**
   * Compute the beginning of a JavaScript file that will hold the main module
   * implementation. By default, calls
   * {@link #getModulePrefix(TreeLogger, LinkerContext, String)}.
   *
   * @param strongName strong name of the module being emitted
   * @param numFragments the number of fragments for this module, including the
   *          main fragment (fragment 0)
   */
  protected String getModulePrefix(TreeLogger logger, LinkerContext context,
      String strongName, int numFragments) throws UnableToCompleteException {
    return getModulePrefix(logger, context, strongName);
  }

  protected abstract String getModuleSuffix(TreeLogger logger,
      LinkerContext context) throws UnableToCompleteException;
 
  protected abstract String getSelectionScriptTemplate(TreeLogger logger,
      LinkerContext context) throws UnableToCompleteException;

 
  /**
   * Add the Development Mode file to the artifact set.
   */
  protected void maybeAddHostedModeFile(TreeLogger logger,
      LinkerContext context, ArtifactSet artifacts, CompilationResult result)
      throws UnableToCompleteException {
    String hostedFilename = getHostedFilename();
    if ("".equals(hostedFilename) || result != null) {
      return;
    }
    try {
      URL resource = SelectionScriptLinker.class.getResource(hostedFilename);
      if (resource == null) {
        logger.log(TreeLogger.ERROR,
            "Unable to find support resource: " + hostedFilename);
        throw new UnableToCompleteException();
      }

      final URLConnection connection = resource.openConnection();
      // TODO: extract URLArtifact class?
      EmittedArtifact hostedFile = new EmittedArtifact(
          SelectionScriptLinker.class, hostedFilename) {
        @Override
        public InputStream getContents(TreeLogger logger)
            throws UnableToCompleteException {
          try {
            return connection.getInputStream();
          } catch (IOException e) {
            logger.log(TreeLogger.ERROR, "Unable to copy support resource", e);
            throw new UnableToCompleteException();
          }
        }

        @Override
        public long getLastModified() {
          return connection.getLastModified();
        }
      };
      artifacts.add(hostedFile);
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Unable to copy support resource", e);
      throw new UnableToCompleteException();
    }
  }

  protected void maybeOutputPropertyMap(TreeLogger logger,
      LinkerContext context, ArtifactSet toReturn) {
    return;
  }

  protected StringBuffer readFileToStringBuffer(String filename,
      TreeLogger logger) throws UnableToCompleteException {
    StringBuffer buffer;
    try {
      buffer = new StringBuffer(Utility.getFileFromClassPath(filename));
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Unable to read file: " + filename, e);
      throw new UnableToCompleteException();
    }
    return buffer;
  }

  protected String wrapPrimaryFragment(TreeLogger logger,
      LinkerContext context, String script, ArtifactSet artifacts,
      CompilationResult result) {
    return script;
  }

  private List<Artifact<?>> emitSelectionInformation(String strongName,
      CompilationResult result) {
    List<Artifact<?>> emitted = new ArrayList<Artifact<?>>();

    for (SortedMap<SelectionProperty, String> propertyMap : result.getPropertyMap()) {
      TreeMap<String, String> propMap = new TreeMap<String, String>();
      for (Map.Entry<SelectionProperty, String> entry : propertyMap.entrySet()) {
        propMap.put(entry.getKey().getName(), entry.getValue());
      }

      // The soft properties may not be a subset of the existing set
      for (SoftPermutation soft : result.getSoftPermutations()) {
        // Make a copy we can add add more properties to
        TreeMap<String, String> softMap = new TreeMap<String, String>(propMap);
        // Make sure this SelectionInformation contains the soft properties
        for (Map.Entry<SelectionProperty, String> entry : soft.getPropertyMap().entrySet()) {
          softMap.put(entry.getKey().getName(), entry.getValue());
        }
        emitted.add(new SelectionInformation(strongName, soft.getId(), softMap));
      }
    }

    return emitted;
  }

}
TOP

Related Classes of com.google.gwt.core.ext.linker.impl.SelectionScriptLinker

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.