Groundwork: qualifiednames

This commit is contained in:
oowekyala
2017-07-30 00:13:56 +02:00
parent a61fe03e86
commit 7fd85f77a3
13 changed files with 289 additions and 13 deletions

View File

@ -0,0 +1,22 @@
---
title: How to implement a metrics framework for an existing language
short_title: Implement a metrics framework
tags: [customizing]
summary: "PMD's Java module has an extensive framework for the calculation of metrics, which allows rule developers
to implement and use new code metrics very simply. Most of the functionality of this framework is abstracted in such
a way that any PMD supported language can implement such a framework without too much trouble. Here's how."
last_updated: July 3, 2016
sidebar: pmd_sidebar
permalink: pmd_devdocs_adding_new_cpd_language.html
folder: pmd/devdocs
---
## Basic steps
* Implement the interface `QualifiedName` in a class. This implementation must be tailored to the target language so
that it can indentify unambiguously any class and operation in the analysed project (see JavaQualifiedName).
* Determine the AST nodes that correspond to class and method declaration in your language. Both these types must
implement the interface `QualifiableNode`, which means they must provide a `getQualifiedName` method to find their
qualified name.
* Implement the interface `Signature<N>`, parameterized with the type of the method AST nodes. Method signatures
describe basic information about a method, which typically includes most of the modifiers they declare (eg
visibility, abstract or virtual, etc.). It's up to you to define a

View File

@ -4,9 +4,13 @@
package net.sourceforge.pmd.lang.apex.ast;
import net.sourceforge.pmd.lang.apex.metrics.signature.ApexOperationSignature;
import net.sourceforge.pmd.lang.ast.SignedNode;
import net.sourceforge.pmd.lang.metrics.Signature;
import apex.jorje.semantic.ast.member.Method;
public class ASTMethod extends AbstractApexNode<Method> {
public class ASTMethod extends AbstractApexNode<Method> implements ApexQualifiableNode, SignedNode<ASTMethod> {
public ASTMethod(Method method) {
super(method);
@ -40,4 +44,16 @@ public class ASTMethod extends AbstractApexNode<Method> {
return super.getEndColumn();
}
@Override
public ApexQualifiedName getQualifiedName() {
return ApexQualifiedName.ofMethod(this);
}
@Override
public ApexOperationSignature getSignature() {
return ApexOperationSignature.of(this);
}
}

View File

@ -9,16 +9,21 @@ import java.lang.reflect.Field;
import apex.jorje.data.ast.Identifier;
import apex.jorje.semantic.ast.compilation.UserClass;
public class ASTUserClass extends ApexRootNode<UserClass> {
public class ASTUserClass extends ApexRootNode<UserClass> implements ApexQualifiableNode {
private ApexQualifiedName qname;
public ASTUserClass(UserClass userClass) {
super(userClass);
}
public Object jjtAccept(ApexParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public String getImage() {
try {
@ -31,4 +36,21 @@ public class ASTUserClass extends ApexRootNode<UserClass> {
}
return super.getImage();
}
@Override
public ApexQualifiedName getQualifiedName() {
if (qname == null) {
ASTUserClass parent = this.getFirstParentOfType(ASTUserClass.class);
if (parent != null) {
qname = ApexQualifiedName.ofNestedClass(parent.getQualifiedName(), this);
} else {
qname = ApexQualifiedName.ofOuterClass(this);
}
}
return qname;
}
}

View File

@ -0,0 +1,16 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.ast;
import net.sourceforge.pmd.lang.ast.QualifiableNode;
/**
* @author Clément Fournier
*/
public interface ApexQualifiableNode extends QualifiableNode {
@Override
ApexQualifiedName getQualifiedName();
}

View File

@ -0,0 +1,84 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.ast;
import java.util.Arrays;
import net.sourceforge.pmd.lang.ast.QualifiedName;
/**
* Qualified name of an apex class or method.
*
* @author Clément Fournier
*/
public class ApexQualifiedName implements QualifiedName {
private final String nameSpace;
private final String[] classes;
private final String operation;
private ApexQualifiedName(String nameSpace, String[] classes, String operation) {
this.nameSpace = nameSpace;
this.operation = operation;
this.classes = classes;
}
@Override
public String getOperation() {
return operation;
}
@Override
public String[] getClasses() {
return Arrays.copyOf(classes, classes.length);
}
/**
* Gets the namespace prefix of this resource.
*
* @return The namespace prefix
*/
public String getNameSpace() {
return nameSpace;
}
@Override
public boolean isClass() {
return operation == null;
}
@Override
public boolean isOperation() {
return operation != null;
}
static ApexQualifiedName ofOuterClass(ASTUserClass astUserClass) {
String ns = astUserClass.node.getDefiningType().getNamespace().toString();
String[] classes = {astUserClass.getImage()};
return new ApexQualifiedName(ns, classes, null);
}
static ApexQualifiedName ofNestedClass(ApexQualifiedName parent, ASTUserClass astUserClass) {
String[] classes = Arrays.copyOf(parent.classes, parent.classes.length);
classes[classes.length - 1] = astUserClass.getImage();
return new ApexQualifiedName(parent.nameSpace, classes, null);
}
static ApexQualifiedName ofMethod(ASTMethod node) {
ApexQualifiedName parent = node.getFirstParentOfType(ASTUserClass.class).getQualifiedName();
return new ApexQualifiedName(parent.nameSpace, parent.classes, node.getImage());
}
}

View File

@ -0,0 +1,14 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
import net.sourceforge.pmd.lang.metrics.AbstractMetricMemoizer;
/**
* @author Clément Fournier
*/
public class ApexClassStats extends AbstractMetricMemoizer<ASTUserClass> {
}

View File

@ -0,0 +1,16 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.metrics.AbstractMetricMemoizer;
/**
* Apex operation stats.
*
* @author Clément Fournier
*/
public class ApexOperationStats extends AbstractMetricMemoizer<ASTMethod> {
}

View File

@ -0,0 +1,36 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import java.util.HashMap;
import java.util.Map;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
import net.sourceforge.pmd.lang.apex.ast.ApexQualifiedName;
import net.sourceforge.pmd.lang.ast.QualifiedName;
import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
import net.sourceforge.pmd.lang.metrics.ProjectMirror;
/**
* @author Clément Fournier
*/
public class ApexProjectMirror implements ProjectMirror<ASTUserClass, ASTMethod> {
private final Map<ApexQualifiedName, ApexOperationStats> operations = new HashMap<>();
private final Map<ApexQualifiedName, ApexClassStats> classes = new HashMap<>();
@Override
public MetricMemoizer<ASTMethod> getOperationStats(QualifiedName qname) {
return null;
}
@Override
public MetricMemoizer<ASTUserClass> getClassStats(QualifiedName qname) {
return null;
}
}

View File

@ -0,0 +1,29 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.signature;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.metrics.Signature;
/**
* @author Clément Fournier
*/
public class ApexOperationSignature implements Signature<ASTMethod> {
private String foo;
/**
* Builds the signature of this node.
*
* @param node The method node
*
* @return The signature of the node
*/
public static ApexOperationSignature of(ASTMethod node) {
return null;
}
}

View File

@ -0,0 +1,11 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.signature;
/**
* @author Clément Fournier
*/
public class ApexSignature {
}

View File

@ -33,4 +33,21 @@ public interface QualifiedName {
*/
String[] getClasses();
/**
* Returns true if the resource addressed by this qualified name is a class.
*
* @return true if the resource addressed by this qualified name is a class.
*/
boolean isClass();
/**
* Returns true if the resource addressed by this qualified name is an operation.
*
* @return true if the resource addressed by this qualified name is an operation.
*/
boolean isOperation();
}

View File

@ -180,20 +180,12 @@ public final class JavaQualifiedName implements QualifiedName {
return sb.toString();
}
/**
* Returns true if the resource addressed by this qualified name is a class.
*
* @return true if the resource addressed by this qualified name is a class.
*/
@Override
public boolean isClass() {
return classes[0] != null && operation == null;
}
/**
* Returns true if the resource addressed by this qualified name is an operation.
*
* @return true if the resource addressed by this qualified name is an operation.
*/
@Override
public boolean isOperation() {
return operation != null;
}

View File

@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.metrics.impl;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.metrics.impl.visitors.DefaultNpathVisitor;
import net.sourceforge.pmd.lang.metrics.api.MetricVersion;
import net.sourceforge.pmd.lang.metrics.MetricVersion;
/**
* NPath complexity is a measurement of the acyclic execution paths through a function. See Nejmeh, Communications of
@ -21,4 +21,5 @@ public class NpathMetric extends AbstractJavaOperationMetric {
public double computeFor(ASTMethodOrConstructorDeclaration node, MetricVersion version) {
return (Integer) node.jjtAccept(new DefaultNpathVisitor(), null);
}
}