jaroslav@716: /** jaroslav@716: * Back 2 Browser Bytecode Translator jaroslav@716: * Copyright (C) 2012 Jaroslav Tulach jaroslav@716: * jaroslav@716: * This program is free software: you can redistribute it and/or modify jaroslav@716: * it under the terms of the GNU General Public License as published by jaroslav@716: * the Free Software Foundation, version 2 of the License. jaroslav@716: * jaroslav@716: * This program is distributed in the hope that it will be useful, jaroslav@716: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@716: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@716: * GNU General Public License for more details. jaroslav@716: * jaroslav@716: * You should have received a copy of the GNU General Public License jaroslav@716: * along with this program. Look for COPYING file in the top folder. jaroslav@716: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@716: */ jaroslav@716: package org.apidesign.bck2brwsr.ide.editor; jaroslav@716: jaroslav@716: import com.sun.source.tree.AnnotationTree; jaroslav@716: import com.sun.source.tree.ExpressionTree; jaroslav@716: import com.sun.source.tree.LiteralTree; jaroslav@716: import com.sun.source.tree.MethodTree; jaroslav@716: import com.sun.source.tree.Tree.Kind; jaroslav@716: import com.sun.source.tree.VariableTree; jaroslav@716: import com.sun.source.util.TreePath; jaroslav@716: import java.util.ArrayList; jaroslav@716: import java.util.Arrays; jaroslav@716: import java.util.Collections; jaroslav@716: import java.util.List; jaroslav@716: import java.util.regex.Matcher; jaroslav@716: import java.util.regex.Pattern; jaroslav@716: import org.netbeans.api.java.lexer.JavaTokenId; jaroslav@716: import static org.netbeans.api.java.lexer.JavaTokenId.BLOCK_COMMENT; jaroslav@716: import static org.netbeans.api.java.lexer.JavaTokenId.JAVADOC_COMMENT; jaroslav@716: import static org.netbeans.api.java.lexer.JavaTokenId.LINE_COMMENT; jaroslav@716: import static org.netbeans.api.java.lexer.JavaTokenId.WHITESPACE; jaroslav@716: import org.netbeans.api.java.source.CompilationInfo; jaroslav@716: import org.netbeans.api.java.source.TreeMaker; jaroslav@716: import org.netbeans.api.lexer.Token; jaroslav@716: import org.netbeans.api.lexer.TokenSequence; jaroslav@716: import org.netbeans.spi.editor.hints.ErrorDescription; jaroslav@716: import org.netbeans.spi.editor.hints.Fix; jaroslav@716: import org.netbeans.spi.java.hints.ErrorDescriptionFactory; jaroslav@716: import org.netbeans.spi.java.hints.Hint; jaroslav@716: import org.netbeans.spi.java.hints.HintContext; jaroslav@716: import org.netbeans.spi.java.hints.JavaFix; jaroslav@716: import org.netbeans.spi.java.hints.TriggerTreeKind; jaroslav@716: import org.openide.util.NbBundle.Messages; jaroslav@716: jaroslav@716: @Hint(displayName = "#DN_JSNI2JavaScriptBody", description = "#DESC_JSNI2JavaScriptBody", category = "general") jaroslav@716: @Messages({ jaroslav@716: "DN_JSNI2JavaScriptBody=JSNI to @JavaScriptBody", jaroslav@716: "DESC_JSNI2JavaScriptBody=JSNI to @JavaScriptBody" jaroslav@716: }) jaroslav@716: public class JSNI2JavaScriptBody { jaroslav@716: jaroslav@716: private static final Pattern JSNIPattern = Pattern.compile("/\\*-\\{(.*)}-\\*/"); jaroslav@716: jaroslav@716: @TriggerTreeKind(Kind.METHOD) jaroslav@716: @Messages("ERR_JSNI2JavaScriptBody=Can convert JSNI to @JavaScriptBody") jaroslav@716: public static ErrorDescription computeWarning(final HintContext ctx) { jaroslav@716: Token token = findBlockToken(ctx.getInfo(), ctx.getPath(), ctx); jaroslav@716: jaroslav@716: if (token == null) { jaroslav@716: return null; jaroslav@716: } jaroslav@716: jaroslav@716: Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath()).toEditorFix(); jaroslav@716: return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_JSNI2JavaScriptBody(), fix); jaroslav@716: } jaroslav@716: jaroslav@716: private static Token findBlockToken(CompilationInfo info, TreePath path, HintContext ctx) { jaroslav@716: int end = (int) info.getTrees().getSourcePositions().getEndPosition(path.getCompilationUnit(), path.getLeaf()); jaroslav@716: TokenSequence ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language()); jaroslav@716: jaroslav@716: if (ts == null) return null; jaroslav@716: jaroslav@716: ts.move(end); jaroslav@716: jaroslav@716: if ((ctx != null && ctx.isCanceled()) || !ts.movePrevious() || ts.token().id() != JavaTokenId.SEMICOLON) return null; jaroslav@716: jaroslav@716: OUTER: while (ts.movePrevious()) { jaroslav@716: if (ctx != null && ctx.isCanceled()) return null; jaroslav@716: jaroslav@716: switch (ts.token().id()) { jaroslav@716: case WHITESPACE: break; jaroslav@716: case LINE_COMMENT: break; jaroslav@716: case JAVADOC_COMMENT: break; jaroslav@716: case BLOCK_COMMENT: jaroslav@716: if (JSNIPattern.matcher(ts.token().text()).matches()) { jaroslav@716: return ts.offsetToken(); jaroslav@716: } jaroslav@716: break; jaroslav@716: default: jaroslav@716: break OUTER; jaroslav@716: } jaroslav@716: } jaroslav@716: jaroslav@716: return null; jaroslav@716: } jaroslav@716: jaroslav@716: private static final class FixImpl extends JavaFix { jaroslav@716: jaroslav@716: public FixImpl(CompilationInfo info, TreePath tp) { jaroslav@716: super(info, tp); jaroslav@716: } jaroslav@716: jaroslav@716: @Override jaroslav@716: @Messages("FIX_JSNI2JavaScriptBody=Convert JSNI to @JavaScriptBody") jaroslav@716: protected String getText() { jaroslav@716: return Bundle.FIX_JSNI2JavaScriptBody(); jaroslav@716: } jaroslav@716: jaroslav@716: @Override jaroslav@716: protected void performRewrite(TransformationContext ctx) { jaroslav@716: Token jsniComment = findBlockToken(ctx.getWorkingCopy(), ctx.getPath(), null); jaroslav@716: jaroslav@716: if (jsniComment == null) { jaroslav@716: //XXX: warn? jaroslav@716: return ; jaroslav@716: } jaroslav@716: jaroslav@716: Matcher m = JSNIPattern.matcher(jsniComment.text()); jaroslav@716: jaroslav@716: if (!m.matches()) return ; //XXX: warn? jaroslav@716: jaroslav@716: TreeMaker make = ctx.getWorkingCopy().getTreeMaker(); jaroslav@716: MethodTree mt = (MethodTree) ctx.getPath().getLeaf(); jaroslav@716: List params = new ArrayList(); jaroslav@716: jaroslav@716: params.add(make.Literal("this")); jaroslav@716: jaroslav@716: for (VariableTree p : mt.getParameters()) { jaroslav@716: params.add(make.Literal(p.getName().toString())); jaroslav@716: } jaroslav@716: jaroslav@716: AnnotationTree jsBody = make.Annotation(make.QualIdent("org.apidesign.bck2brwsr.core.JavaScriptBody"), jaroslav@716: Arrays.asList( jaroslav@716: make.Assignment(make.Identifier("body"), make.Literal(m.group(1))), jaroslav@716: make.Assignment(make.Identifier("args"), make.NewArray(null, Collections.emptyList(), params)) jaroslav@716: ) jaroslav@716: ); jaroslav@716: jaroslav@716: jaroslav@716: ctx.getWorkingCopy().rewrite(mt.getModifiers(), make.addModifiersAnnotation(mt.getModifiers(), jsBody)); jaroslav@716: } jaroslav@716: } jaroslav@716: }