Package org.stjs.generator

Source Code of org.stjs.generator.GenerationContext

/**
*  Copyright 2011 Alexandru Craciun, Eyal Kaspi
*
*  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 org.stjs.generator;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.Map;

import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import org.stjs.generator.check.Checks;
import org.stjs.generator.javac.AnnotationHelper;
import org.stjs.generator.javac.TreeWrapper;
import org.stjs.generator.javascript.JavaScriptBuilder;
import org.stjs.generator.name.JavaScriptNameProvider;
import org.stjs.generator.visitor.TreePathHolder;

import com.google.common.collect.Maps;
import com.google.debugging.sourcemap.SourceMapGenerator;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TreeVisitor;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;

/**
* This class can resolve an identifier or a method in the given source context. There is one context create for each generation process.
* @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
*/
public class GenerationContext<JS> implements TreePathHolder {

  private static final Object NULL = new Object();

  private final File inputFile;

  private final GeneratorConfiguration configuration;

  private final JavaScriptNameProvider names;

  private Trees trees;

  private Elements elements;

  private Types types;

  private TreePath currentPath;

  private final Checks checks;

  private CompilationUnitTree compilationUnit;

  private final JavaScriptBuilder<JS> javaScriptBuilder;

  private SourceMapGenerator sourceMapGenerator;

  private final ClassLoader builtProjectClassLoader;

  private final Map<AnnotationCacheKey, Object> cacheAnnotations;
  private final Map<Tree, TreeWrapper<?, JS>> cacheWrappers = Maps.newIdentityHashMap();
  private final Map<Element, TreeWrapper<?, JS>> cacheWrappersByElement = Maps.newIdentityHashMap();

  public GenerationContext(File inputFile, GeneratorConfiguration configuration, JavaScriptNameProvider names, Trees trees,
      ClassLoader builtProjectClassLoader, Map<AnnotationCacheKey, Object> cacheAnnotations, JavaScriptBuilder<JS> javaScriptBuilder) {
    this.inputFile = inputFile;
    this.configuration = configuration;
    this.names = names;
    this.trees = trees;
    this.checks = new Checks();
    this.javaScriptBuilder = javaScriptBuilder;
    this.builtProjectClassLoader = builtProjectClassLoader;
    this.cacheAnnotations = cacheAnnotations;
  }

  public File getInputFile() {
    return inputFile;
  }

  public GeneratorConfiguration getConfiguration() {
    return configuration;
  }

  public JavaScriptNameProvider getNames() {
    return names;
  }

  public Trees getTrees() {
    return trees;
  }

  public void setTrees(Trees trees) {
    this.trees = trees;
  }

  @Override
  public TreePath getCurrentPath() {
    return currentPath;
  }

  @Override
  public void setCurrentPath(TreePath currentPath) {
    this.currentPath = currentPath;
  }

  public Elements getElements() {
    return elements;
  }

  public void setElements(Elements elements) {
    this.elements = elements;
  }

  public Types getTypes() {
    return types;
  }

  public void setTypes(Types types) {
    this.types = types;
  }

  public Checks getChecks() {
    return checks;
  }

  public CompilationUnitTree getCompilationUnit() {
    return compilationUnit;
  }

  public void setCompilationUnit(CompilationUnitTree compilationUnit) {
    this.compilationUnit = compilationUnit;
  }

  public JavascriptFileGenerationException addError(Tree tree, String message) {
    if (compilationUnit == null) {
      return new JavascriptFileGenerationException(new SourcePosition(inputFile, 0, 0), message);
    }
    long startPos = trees.getSourcePositions().getStartPosition(compilationUnit, tree);
    SourcePosition pos =
        startPos >= 0 ? new SourcePosition(inputFile, (int) compilationUnit.getLineMap().getLineNumber(startPos), (int) compilationUnit
            .getLineMap().getColumnNumber(startPos)) : new SourcePosition(inputFile, 0, 0);
    JavascriptFileGenerationException ex = new JavascriptFileGenerationException(pos, message);
    checks.addError(ex);
    return ex;
  }

  /**
   * add the position of the Java original node in the generated node.
   * @param tree
   * @param node
   * @return
   */
  public JS withPosition(Tree tree, JS node) {
    if (compilationUnit == null) {
      return node;
    }
    long startPos = trees.getSourcePositions().getStartPosition(compilationUnit, tree);
    int line = startPos >= 0 ? (int) compilationUnit.getLineMap().getLineNumber(startPos) : 0;
    int column = startPos >= 0 ? (int) compilationUnit.getLineMap().getColumnNumber(startPos) : 0;

    long endPos = trees.getSourcePositions().getEndPosition(compilationUnit, tree);
    int endLine = endPos >= 0 ? (int) compilationUnit.getLineMap().getLineNumber(endPos) : 0;
    int endColumn = endPos >= 0 ? (int) compilationUnit.getLineMap().getColumnNumber(endPos) : 0;

    return javaScriptBuilder.position(node, line, column, endLine, endColumn);
  }

  /**
   * @param tree
   * @return the starting line of the given tree node
   */
  public int getStartLine(Tree tree) {
    if (compilationUnit == null) {
      return -1;
    }
    long startPos = trees.getSourcePositions().getStartPosition(compilationUnit, tree);
    return (int) (startPos >= 0 ? compilationUnit.getLineMap().getLineNumber(startPos) : 0);
  }

  public JavaScriptBuilder<JS> js() {
    return javaScriptBuilder;
  }

  public void writeJavaScript(JS astRoot, Writer writer) {
    sourceMapGenerator = javaScriptBuilder.writeJavaScript(astRoot, inputFile, configuration.isGenerateSourceMap(), writer);
  }

  public void writeSourceMap(Writer sourceMapWriter) throws IOException {
    // you need to call first writeJavaScript
    if (sourceMapGenerator == null) {
      throw new IllegalStateException("Cannot call this method for writer that do not generate source maps");
    }
    sourceMapGenerator.appendTo(sourceMapWriter, inputFile.getName().replaceAll("\\.java$", ".js"));
  }

  public <T extends Tree> TreeWrapper<T, JS> getCurrentWrapper() {
    return wrap(currentPath);
  }

  @SuppressWarnings("unchecked")
  public <T extends Tree> TreeWrapper<T, JS> wrap(TreePath path) {
    TreeWrapper<T, JS> tw = (TreeWrapper<T, JS>) cacheWrappers.get(path.getLeaf());
    if (tw == null) {
      tw = new TreeWrapper<T, JS>(path, this);
      cacheWrappers.put(path.getLeaf(), tw);
    }
    return tw;
  }

  private TreePath getTreePath(Element enclosingElement) {
    TreePath path = trees.getPath(enclosingElement);
    if (path == null) {
      Tree tree = trees.getTree(enclosingElement);
      if (tree == null) {
        tree = new DummyTree();
      }
      // XXX: this is ugly, null is better here?
      path = new TreePath(new TreePath(compilationUnit), tree);
    }
    return path;
  }

  @SuppressWarnings("unchecked")
  public <T extends Tree> TreeWrapper<T, JS> wrap(Element enclosingElement) {
    TreeWrapper<T, JS> tw = (TreeWrapper<T, JS>) cacheWrappersByElement.get(enclosingElement);
    if (tw == null) {
      tw = new TreeWrapper<T, JS>(enclosingElement, getTreePath(enclosingElement), this);
      cacheWrappersByElement.put(enclosingElement, tw);
    }

    return tw;
  }

  public ClassLoader getBuiltProjectClassLoader() {
    return builtProjectClassLoader;
  }

  @SuppressWarnings("unchecked")
  public <T extends Annotation> T getAnnotation(Element element, Class<T> annotationType) {
    AnnotationCacheKey key = new AnnotationCacheKey(annotationType, element);
    Object ret = cacheAnnotations.get(key);
    if (ret != null) {
      return NULL.equals(ret) ? null : (T) ret;
    }
    ret = AnnotationHelper.getAnnotation(elements, element, annotationType);
    if (ret == null) {
      cacheAnnotations.put(key, NULL);
    } else {
      cacheAnnotations.put(key, ret);
    }
    return (T) ret;
  }

  public static class AnnotationCacheKey {
    private final Class<? extends Annotation> annotationType;
    private final Element element;

    public AnnotationCacheKey(Class<? extends Annotation> annotationType, Element element) {
      this.annotationType = annotationType;
      this.element = element;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + (annotationType == null ? 0 : annotationType.hashCode());
      result = prime * result + (element == null ? 0 : element.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      if (getClass() != obj.getClass()) {
        return false;
      }
      AnnotationCacheKey other = (AnnotationCacheKey) obj;
      if (annotationType == null) {
        if (other.annotationType != null) {
          return false;
        }
      } else if (!annotationType.equals(other.annotationType)) {
        return false;
      }
      if (element == null) {
        if (other.element != null) {
          return false;
        }
      } else if (!element.equals(other.element)) {
        return false;
      }
      return true;
    }

  }

  /**
   * this is a dummy try just to have the wrapper thing working for cases where only the element is available
   */
  public static class DummyTree implements Tree {
    @Override
    public <R, D> R accept(TreeVisitor<R, D> arg0, D arg1) {
      return null;
    }

    @Override
    public Kind getKind() {
      return Kind.ERRONEOUS;
    }

  }

}
TOP

Related Classes of org.stjs.generator.GenerationContext

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.