org.netbeans.modules.javadoc.hints.GenerateJavadocAction Maven / Gradle / Ivy
/*
* 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.javadoc.hints;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import java.awt.event.ActionEvent;
import java.io.IOException;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.StyledDocument;
import javax.swing.text.TextAction;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.modules.editor.indent.api.Indent;
import org.netbeans.api.java.lexer.JavaTokenId;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.java.source.Task;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenId;
import org.netbeans.api.lexer.TokenSequence;
import org.openide.filesystems.FileObject;
import org.openide.text.NbDocument;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
/**
* The action supposed to fix an empty javadoc block.
* The present use case is to complete javadoc generated on /** + <ENTER>
.
* See org.netbeans.modules.editor.java.JavaKit.JavaInsertBreakAction
*
* Note: The text action is a temporary solution until the editor
* introduce some SPI to plug.
*
* @author Jan Pokorsky
*/
public final class GenerateJavadocAction extends TextAction {
GenerateJavadocAction() {
super("fix-javadoc-action"); // NOI18N
}
public static GenerateJavadocAction create() {
return new GenerateJavadocAction();
}
public void actionPerformed(ActionEvent e) {
final JTextComponent jtc = getTextComponent(e);
final Document doc = jtc.getDocument();
if (!(doc instanceof StyledDocument)) {
// unsupported document
return;
}
try {
final Descriptor desc = new Descriptor(doc.createPosition(jtc.getCaretPosition()));
RequestProcessor.getDefault().post(new Runnable() {
@Override
public void run() {
try {
if (prepareGenerating(doc, desc)) {
// add javadoc content
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
generate(doc, desc, jtc);
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
});
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
private boolean prepareGenerating(final Document doc, final Descriptor desc) throws IOException {
JavaSource js = JavaSource.forDocument(doc);
if (js == null) {
return false;
}
FileObject file = js.getFileObjects().iterator().next();
SourceVersion sv = JavadocUtilities.resolveSourceVersion(file);
final JavadocGenerator gen = new JavadocGenerator(sv);
gen.updateSettings(file);
js.runUserActionTask(new Task() {
public void run(CompilationController javac) throws Exception {
javac.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TokenHierarchy tokens = javac.getTokenHierarchy();
TokenSequence ts = tokens.tokenSequence();
ts.move(desc.caret.getOffset());
if (!ts.moveNext() || ts.token().id() != JavaTokenId.JAVADOC_COMMENT) {
return;
}
desc.caret = doc.createPosition(desc.caret.getOffset());
final int jdBeginOffset = ts.offset();
int offsetBehindJavadoc = ts.offset() + ts.token().length();
while (ts.moveNext()) {
TokenId tid = ts.token().id();
if (tid != JavaTokenId.WHITESPACE && tid != JavaTokenId.LINE_COMMENT && tid != JavaTokenId.BLOCK_COMMENT) {
offsetBehindJavadoc = ts.offset();
// it is magic for TreeUtilities.pathFor
++offsetBehindJavadoc;
break;
}
}
TreePath tp = javac.getTreeUtilities().pathFor(offsetBehindJavadoc);
Tree leaf = tp.getLeaf();
Kind kind = leaf.getKind();
SourcePositions positions = javac.getTrees().getSourcePositions();
while (!TreeUtilities.CLASS_TREE_KINDS.contains(kind) && kind != Kind.METHOD && kind != Kind.VARIABLE && kind != Kind.COMPILATION_UNIT) {
tp = tp.getParentPath();
if (tp == null) {
leaf = null;
kind = null;
break;
}
leaf = tp.getLeaf();
kind = leaf.getKind();
}
if (leaf == null || positions.getStartPosition(javac.getCompilationUnit(), leaf) < jdBeginOffset) {
// not a class member javadoc -> ignore
return;
}
if (kind != Kind.COMPILATION_UNIT && !JavadocUtilities.hasErrors(leaf) /*&& Access.PRIVATE.isAccessible(javac, tp, true)*/) {
Element el = javac.getTrees().getElement(tp);
if (el != null) {
String javadoc = gen.generateComment(el, javac);
if(javadoc == null) {
return;
}
desc.javadoc = javadoc;
}
}
}
}, true);
return desc.javadoc != null;
}
private void generate(final Document doc, final Descriptor desc, final JTextComponent jtc) throws BadLocationException {
final Indent ie = Indent.get(doc);
try {
ie.lock();
NbDocument.runAtomicAsUser((StyledDocument) doc, new Runnable() {
public void run() {
try {
int caretPos = jtc.getCaretPosition();
generateJavadoc(doc, desc, ie);
// move caret
jtc.setCaretPosition(caretPos);
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
});
} finally {
ie.unlock();
}
}
private void generateJavadoc(Document doc, Descriptor desc, Indent ie) throws BadLocationException {
// skip javadoc header /** and tail */ since they were already generated
String content = desc.javadoc;
if(content.endsWith("\n")) {
content = content.substring(0, content.length() - "\n".length());
}
if (content.length() == 0) {
return;
}
Position pos = desc.caret;
int startOffset = pos.getOffset();
doc.insertString(startOffset, content, null);
if (startOffset != pos.getOffset()) {
ie.reindent(startOffset + 1, pos.getOffset());
}
}
private static final class Descriptor {
String javadoc;
/** position inside javadoc where to write */
Position caret;
public Descriptor(Position caret) {
this.caret = caret;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy