blob: 38511d7215eceea98ff5e1aa99f523721ac26f3f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.editor.htmlui;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.netbeans.api.java.lexer.JavaTokenId;
import static org.netbeans.api.java.lexer.JavaTokenId.BLOCK_COMMENT;
import static org.netbeans.api.java.lexer.JavaTokenId.JAVADOC_COMMENT;
import static org.netbeans.api.java.lexer.JavaTokenId.LINE_COMMENT;
import static org.netbeans.api.java.lexer.JavaTokenId.WHITESPACE;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
import org.netbeans.spi.java.hints.Hint;
import org.netbeans.spi.java.hints.HintContext;
import org.netbeans.spi.java.hints.JavaFix;
import org.netbeans.spi.java.hints.TriggerTreeKind;
import org.openide.util.NbBundle.Messages;
@Hint(displayName = "#DN_JSNI2JavaScriptBody", description = "#DESC_JSNI2JavaScriptBody", category = "general")
@Messages({
"DN_JSNI2JavaScriptBody=JSNI to @JavaScriptBody",
"DESC_JSNI2JavaScriptBody=JSNI to @JavaScriptBody"
})
public class JSNI2JavaScriptBody {
@TriggerTreeKind(Kind.METHOD)
@Messages("ERR_JSNI2JavaScriptBody=Can convert JSNI to @JavaScriptBody")
public static ErrorDescription computeWarning(final HintContext ctx) {
Token<JavaTokenId> token = findBlockToken(ctx.getInfo(), ctx.getPath(), ctx);
if (token == null) {
return null;
}
Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath()).toEditorFix();
return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_JSNI2JavaScriptBody(), fix);
}
private static Token<JavaTokenId> findBlockToken(CompilationInfo info, TreePath path, HintContext ctx) {
int end = (int) info.getTrees().getSourcePositions().getEndPosition(path.getCompilationUnit(), path.getLeaf());
TokenSequence<JavaTokenId> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language());
if (ts == null) return null;
ts.move(end);
if ((ctx != null && ctx.isCanceled()) || !ts.movePrevious() || ts.token().id() != JavaTokenId.SEMICOLON) return null;
OUTER: while (ts.movePrevious()) {
if (ctx != null && ctx.isCanceled()) return null;
switch (ts.token().id()) {
case WHITESPACE: break;
case LINE_COMMENT: break;
case JAVADOC_COMMENT: break;
case BLOCK_COMMENT:
final CharSequence tok = ts.token().text();
final int l = tok.length();
if (l > 4
&& tok.subSequence(0, 4).toString().equals("/*-{") // NOI18N
&& tok.subSequence(l - 4, l).toString().equals("}-*/") // NOI18N
) {
return ts.offsetToken();
}
break;
default:
break OUTER;
}
}
return null;
}
private static final class FixImpl extends JavaFix {
public FixImpl(CompilationInfo info, TreePath tp) {
super(info, tp);
}
@Override
@Messages("FIX_JSNI2JavaScriptBody=Convert JSNI to @JavaScriptBody")
protected String getText() {
return Bundle.FIX_JSNI2JavaScriptBody();
}
@Override
protected void performRewrite(TransformationContext ctx) {
Token<JavaTokenId> jsniComment = findBlockToken(ctx.getWorkingCopy(), ctx.getPath(), null);
if (jsniComment == null) {
//XXX: warn?
return ;
}
TreeMaker make = ctx.getWorkingCopy().getTreeMaker();
MethodTree mt = (MethodTree) ctx.getPath().getLeaf();
List<LiteralTree> params = new ArrayList<LiteralTree>();
for (VariableTree p : mt.getParameters()) {
params.add(make.Literal(p.getName().toString()));
}
String body = jsniComment.text().toString().replace("\"", "\\\"");
body = body.replace("/*-{", "").replace("}-*/", "");
List<ExpressionTree> arr = new ArrayList<ExpressionTree>();
arr.add(make.Assignment(make.Identifier("args"), make.NewArray(null, Collections.<ExpressionTree>emptyList(), params)));
if (body.contains("@") && body.contains("::")) {
arr.add(make.Assignment(make.Identifier("javacall"), make.Literal(true)));
}
final String[] lines = body.split("\n");
StringBuilder jsB = new StringBuilder();
String sep = "\"";
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (i < lines.length - 1) {
line = line + "\\n";
}
jsB.append(sep).append(line).append("\"");
sep = " + \n\"";
}
arr.add(make.Assignment(make.Identifier("body"), make.Identifier(jsB.toString())));
AnnotationTree jsBody = make.Annotation(make.QualIdent("net.java.html.js.JavaScriptBody"), arr);
ctx.getWorkingCopy().rewrite(mt.getModifiers(), make.addModifiersAnnotation(mt.getModifiers(), jsBody));
}
}
}