tzezula@720: /* tzezula@720: * To change this template, choose Tools | Templates tzezula@720: * and open the template in the editor. tzezula@720: */ tzezula@720: package org.apidesign.bck2brwsr.ide.editor; tzezula@720: tzezula@720: import com.sun.source.tree.AnnotationTree; tzezula@720: import com.sun.source.tree.AssignmentTree; tzezula@720: import com.sun.source.tree.CompilationUnitTree; tzezula@720: import com.sun.source.tree.ExpressionTree; tzezula@720: import com.sun.source.tree.LiteralTree; tzezula@720: import com.sun.source.tree.MethodTree; tzezula@720: import com.sun.source.util.SourcePositions; tzezula@720: import com.sun.source.util.TreePath; tzezula@720: import com.sun.source.util.TreePathScanner; tzezula@720: import com.sun.source.util.Trees; tzezula@720: import java.io.IOException; tzezula@720: import java.util.ArrayList; tzezula@720: import java.util.Collection; tzezula@720: import java.util.Collections; tzezula@720: import java.util.List; tzezula@720: import java.util.concurrent.atomic.AtomicBoolean; tzezula@720: import javax.lang.model.element.TypeElement; tzezula@720: import javax.swing.text.Document; tzezula@720: import org.netbeans.api.editor.mimelookup.MimeRegistration; tzezula@720: import org.netbeans.api.java.source.CompilationInfo; tzezula@720: import org.netbeans.api.java.source.JavaParserResultTask; tzezula@720: import org.netbeans.api.java.source.JavaSource; tzezula@720: import org.netbeans.api.lexer.Language; tzezula@720: import org.netbeans.api.lexer.TokenHierarchy; tzezula@720: import org.netbeans.api.lexer.TokenSequence; tzezula@720: import org.netbeans.modules.parsing.api.Snapshot; tzezula@720: import org.netbeans.modules.parsing.spi.Parser; tzezula@720: import org.netbeans.modules.parsing.spi.Scheduler; tzezula@720: import org.netbeans.modules.parsing.spi.SchedulerEvent; tzezula@720: import org.netbeans.modules.parsing.spi.SchedulerTask; tzezula@720: import org.netbeans.modules.parsing.spi.TaskFactory; tzezula@720: import org.openide.util.Exceptions; tzezula@720: tzezula@720: /** tzezula@720: * tzezula@720: * @author Tomas Zezula tzezula@720: */ tzezula@720: public final class JSEmbeddingProvider extends JavaParserResultTask { tzezula@720: tzezula@720: private static final int PRIORITY = 1000; tzezula@720: private static final String JS_ANNOTATION = "org.apidesign.bck2brwsr.core.JavaScriptBody"; //NOI18N tzezula@720: private static final String BODY = "body"; //NOI18N tzezula@720: private static final String JAVA_MIME_TYPE = "text/x-java"; //NOI18N tzezula@720: private static final String JAVASCRIPT_MIME_TYPE = "text/javascript"; //NOI18N tzezula@720: private final AtomicBoolean canceled = new AtomicBoolean(); tzezula@720: tzezula@720: private JSEmbeddingProvider() { tzezula@720: super(JavaSource.Phase.ELEMENTS_RESOLVED); tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public int getPriority() { tzezula@720: return PRIORITY; tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public void cancel() { tzezula@720: canceled.set(true); tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public Class getSchedulerClass() { tzezula@720: return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public void run(Parser.Result t, SchedulerEvent se) { tzezula@720: canceled.set(false); tzezula@720: final CompilationInfo ci = CompilationInfo.get(t); tzezula@720: final CompilationUnitTree cu = ci.getCompilationUnit(); tzezula@720: final Trees trees = ci.getTrees(); tzezula@720: final SourcePositions sp = trees.getSourcePositions(); tzezula@720: final Finder f = new Finder(trees); tzezula@720: final List result = new ArrayList(); tzezula@720: f.scan(cu, result); tzezula@720: if (!result.isEmpty()) { tzezula@720: try { tzezula@720: final TokenHierarchy tk = TokenHierarchy.get(ci.getDocument()); tzezula@720: final Language java = Language.find(JAVA_MIME_TYPE); tzezula@720: final Language javaScript = Language.find(JAVASCRIPT_MIME_TYPE); tzezula@720: if (java != null && javaScript != null) { tzezula@720: final TokenSequence seq = tk.tokenSequence(java); tzezula@720: if (seq != null) { tzezula@720: for (LiteralTree lt : result) { tzezula@720: final int start = (int) sp.getStartPosition(cu, lt); tzezula@720: final int end = (int) sp.getEndPosition(cu, lt); tzezula@720: seq.move(start); tzezula@720: while (seq.moveNext() && seq.offset() < end) { tzezula@720: seq.createEmbedding(javaScript, 1, 1, true); tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: } catch (IOException ioe) { tzezula@720: Exceptions.printStackTrace(ioe); tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: tzezula@720: tzezula@720: tzezula@720: tzezula@720: private static final class Finder extends TreePathScanner> { tzezula@720: tzezula@720: private final Trees trees; tzezula@720: private CompilationUnitTree cu; tzezula@720: private boolean inEmbedding; tzezula@720: tzezula@720: Finder(final Trees trees) { tzezula@720: this.trees = trees; tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public Void visitCompilationUnit( tzezula@720: final CompilationUnitTree unit, tzezula@720: final List p) { tzezula@720: this.cu = unit; tzezula@720: return super.visitCompilationUnit(unit, p); tzezula@720: } tzezula@720: tzezula@720: tzezula@720: tzezula@720: @Override tzezula@720: public Void visitMethod( tzezula@720: final MethodTree m, tzezula@720: final List p) { tzezula@720: for (AnnotationTree a : m.getModifiers().getAnnotations()) { tzezula@720: final TypeElement ae = (TypeElement) trees.getElement(TreePath.getPath(cu, a.getAnnotationType())); tzezula@720: if (ae != null && JS_ANNOTATION.contentEquals(ae.getQualifiedName())) { tzezula@720: final List args = a.getArguments(); tzezula@720: for (ExpressionTree kvp : args) { tzezula@720: if (kvp instanceof AssignmentTree) { tzezula@720: final AssignmentTree assignemt = (AssignmentTree) kvp; tzezula@720: if (BODY.equals(assignemt.getVariable().toString())) { tzezula@720: inEmbedding = true; tzezula@720: try { tzezula@720: scan(assignemt.getExpression(), p); tzezula@720: } finally { tzezula@720: inEmbedding = false; tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: } tzezula@720: return null; tzezula@720: } tzezula@720: tzezula@720: @Override tzezula@720: public Void visitLiteral(LiteralTree node, List p) { tzezula@720: if (inEmbedding) { tzezula@720: p.add(node); tzezula@720: } tzezula@720: return super.visitLiteral(node, p); tzezula@720: } tzezula@720: tzezula@720: } tzezula@720: tzezula@720: @MimeRegistration( tzezula@720: service = TaskFactory.class, tzezula@720: mimeType = JAVA_MIME_TYPE) tzezula@720: public static final class Factory extends TaskFactory { tzezula@720: @Override tzezula@720: public Collection create(Snapshot snpsht) { tzezula@720: return Collections.singleton(new JSEmbeddingProvider()); tzezula@720: } tzezula@720: } tzezula@720: tzezula@720: }