Merge branch 'pr-2363' #2363

[core] Add more bindings for the designer
This commit is contained in:
Andreas Dangel 2020-03-20 12:18:21 +01:00
commit 08f9a3956e
3 changed files with 227 additions and 0 deletions

View File

@ -4,7 +4,12 @@
package net.sourceforge.pmd.util.designerbindings;
import java.util.Collection;
import java.util.Collections;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
import net.sourceforge.pmd.lang.symboltable.ScopedNode;
/**
@ -25,6 +30,106 @@ public interface DesignerBindings {
RelatedNodesSelector getRelatedNodesSelector();
/**
* Returns a collection of "additional information" entries pertaining to
* the given node. An entry may look like {@code ("Type = List<String>", 0)},
* or show the result of an XPath function. The information is shown
* when the node is displayed.
*
* <p>Order of the collection is unimportant, it's sorted using
* {@link AdditionalInfo#getSortKey()}.
*/
Collection<AdditionalInfo> getAdditionalInfo(Node node);
/**
* An entry for the "additional info" panel.
*/
class AdditionalInfo {
private final String sortKey;
private final String display;
public AdditionalInfo(String sortKey, String display) {
this.sortKey = sortKey;
this.display = display;
}
public AdditionalInfo(String display) {
this(display, display);
}
/**
* Returns the string used to sort the additional info.
* For example, returning {@code "A"} ensures this is displayed
* first, provided there's no other entry with an {@code "A"}.
*/
public String getSortKey() {
return sortKey;
}
/**
* Returns the string displayed to the user.
*/
public String getDisplayString() {
return display;
}
}
/**
* Returns the "main" attribute of the given node.
* The string representation of this attribute ({@link Attribute#getStringValue()})
* will be displayed next to the node type in the treeview. For
* example, for a numeric literal, this could return the attribute
* {@code (@IntValue, 1)}, for a class declaration, it could return the name
* of the class (eg {@code (@SimpleName, String)}.
*
* <p>If there's no obvious "main" attribute, or if the node is not
* supported, returns null. If the returned attribute is non-null,
* but its string value is, the return value is ignored.
*
* <p>Note: the attribute doesn't need to originate from
* {@link Node#getXPathAttributesIterator()}, it can be constructed
* ad-hoc. The name of the attribute should be a valid name for the
* XPath attribute though.
*
* <p>This method is meant to break the designer's dependency on {@link Node#getImage()}.
*/
// @Nullable
Attribute getMainAttribute(Node node);
/**
* Returns true if the children of this node should be displayed in
* the treeview by default. Returning "true" is the safe default value.
*/
boolean isExpandedByDefaultInTree(Node node);
/**
* Returns a constant describing an icon that the node should bear
* in the treeview and other relevant places. Returns null if no icon
* is applicable.
*/
// @Nullable
TreeIconId getIcon(Node node);
/**
* See {@link #getIcon(Node)}.
*/
@Experimental
enum TreeIconId {
CLASS,
METHOD,
CONSTRUCTOR,
FIELD,
VARIABLE
}
/**
* A base implementation for {@link DesignerBindings}.
*/
@ -37,6 +142,30 @@ public interface DesignerBindings {
return null;
}
@Override
public Collection<AdditionalInfo> getAdditionalInfo(Node node) {
return Collections.emptyList();
}
@Override
public Attribute getMainAttribute(Node node) {
String image = node.getImage();
if (image != null) {
return new Attribute(node, "Image", image);
}
return null;
}
@Override
public boolean isExpandedByDefaultInTree(Node node) {
return true;
}
@Override
public TreeIconId getIcon(Node node) {
return null;
}
/** Returns the default instance. */
public static DefaultDesignerBindings getInstance() {
return INSTANCE;

View File

@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.ast.MethodLikeNode;
import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade;
import net.sourceforge.pmd.lang.java.dfa.JavaDFAGraphRule;
import net.sourceforge.pmd.lang.java.internal.JavaDesignerBindings;
import net.sourceforge.pmd.lang.java.multifile.MultifileVisitorFacade;
import net.sourceforge.pmd.lang.java.qname.QualifiedNameResolver;
import net.sourceforge.pmd.lang.java.rule.JavaRuleViolationFactory;
@ -34,6 +35,7 @@ import net.sourceforge.pmd.lang.java.xpath.TypeIsFunction;
import net.sourceforge.pmd.lang.java.xpath.TypeOfFunction;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
import net.sourceforge.pmd.util.designerbindings.DesignerBindings;
import net.sf.saxon.sxpath.IndependentContext;
@ -151,6 +153,10 @@ public abstract class AbstractJavaHandler extends AbstractLanguageVersionHandler
};
}
@Override
public DesignerBindings getDesignerBindings() {
return JavaDesignerBindings.INSTANCE;
}
@Override
public DFAGraphRule getDFAGraphRule() {

View File

@ -0,0 +1,92 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.internal;
import java.util.Collection;
import java.util.Collections;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTRecordConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorReducedAdapter;
import net.sourceforge.pmd.lang.java.ast.TypeNode;
import net.sourceforge.pmd.util.designerbindings.DesignerBindings.DefaultDesignerBindings;
public final class JavaDesignerBindings extends DefaultDesignerBindings {
public static final JavaDesignerBindings INSTANCE = new JavaDesignerBindings();
private JavaDesignerBindings() {
}
@Override
public Attribute getMainAttribute(Node node) {
if (node instanceof JavaNode) {
Attribute attr = (Attribute) ((JavaNode) node).jjtAccept(MainAttrVisitor.INSTANCE, null);
if (attr != null) {
return attr;
}
}
return super.getMainAttribute(node);
}
@Override
public TreeIconId getIcon(Node node) {
if (node instanceof ASTFieldDeclaration) {
return TreeIconId.FIELD;
} else if (node instanceof ASTAnyTypeDeclaration) {
return TreeIconId.CLASS;
} else if (node instanceof ASTMethodDeclaration) {
return TreeIconId.METHOD;
} else if (node instanceof ASTConstructorDeclaration
|| node instanceof ASTRecordConstructorDeclaration) {
return TreeIconId.CONSTRUCTOR;
} else if (node instanceof ASTVariableDeclaratorId) {
return TreeIconId.VARIABLE;
}
return super.getIcon(node);
}
@Override
public Collection<AdditionalInfo> getAdditionalInfo(Node node) {
if (node instanceof TypeNode) {
Class<?> type = ((TypeNode) node).getType();
if (type != null) {
return Collections.singletonList(new AdditionalInfo("Type: " + type));
}
}
return super.getAdditionalInfo(node);
}
private static final class MainAttrVisitor extends JavaParserVisitorReducedAdapter {
private static final JavaParserVisitor INSTANCE = new MainAttrVisitor();
@Override
public Object visit(JavaNode node, Object data) {
return null; // don't recurse
}
@Override
public Object visit(ASTAnyTypeDeclaration node, Object data) {
return new Attribute(node, "SimpleName", node.getSimpleName());
}
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
return new Attribute(node, "Name", node.getName());
}
}
}