Operation signatures

This commit is contained in:
oowekyala 2017-07-30 15:02:03 +02:00
parent 7fd85f77a3
commit e9fab949b7
4 changed files with 129 additions and 11 deletions

View File

@ -14,9 +14,34 @@ 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
* Determine the AST nodes that correspond to class and method declaration in your language. These types are
referred hereafter as `T` and `O`, respectively. Both these types must implement the interface `QualifiableNode`, which
means they must provide a `getQualifiedName` method to give access to their qualified name.
* Implement the interface `Signature<O>`, 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
visibility, abstract or virtual, etc.). It's up to you to define the right level of detail, depending on the accuracy
of the pattern matching required.
* Make type `O` implement `SignedNode<O>`. This makes the node capable of giving its signature.
* Create a class implementing `Memoizer<T>` and one `Memoizer<O>`. An abstract base class is available. Instances of
these classes each represent a class or operation, respectively. They are used to store the results of metrics that
are already computed.
* Create a class implementing `ProjectMirror<T, O>`. This class will store the memoizers for all the classes and
interfaces of the analysed project. This class must be able to fetch and return a memoizer given the qualified name
of the resource it represents. As it stores the memoizers, it's a good idea to implement some signature matching
utilities in this class. What's signature matching? (See write custom metrics -- TODO)
* Create a class extending `AbstractMetricsComputer<T, O>`. This object will be responsible for calculating metrics
given a memoizer, a node and info about the metric. Typically, this object is stateless so you might as well make it
a singleton.
* Create a class extending `AbstractMetricsFacade<T, O>`. This class needs a reference to your `ProjectMirror` and
your `MetricsComputer`. It backs the real end user façade, and handles user provided parameters before delegating to
your `MetricsComputer`.
* Create the static façade of your framework. This one has an instance of your `MetricsFaçade` object and delegates
static methods to that instance. It should be able to give back
* If you want to implement signature matching, create an `AbstractMetric` class, which gives access to a
`SignatureMatcher` to your metrics.
* Create classes `AbstractOperationMetric` and `AbstractClassMetric`. These must implement `Metric<T>` and
`Metric<O>`, respectively. They typically provide defaults for the `supports` method of each metric.
* Create enums `ClassMetricKey` and `OperationMetricKey`. These must implement `MetricKey<T>` and `MetricKey<O>`. The
enums list all available metric keys for your language.
* Create metrics by extending your base classes, reference them in your enums, and you can start using them with your
façade!

View File

@ -10,6 +10,7 @@ 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.apex.metrics.signature.ApexOperationSignature;
import net.sourceforge.pmd.lang.ast.QualifiedName;
import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
import net.sourceforge.pmd.lang.metrics.ProjectMirror;
@ -19,18 +20,39 @@ import net.sourceforge.pmd.lang.metrics.ProjectMirror;
*/
public class ApexProjectMirror implements ProjectMirror<ASTUserClass, ASTMethod> {
private final Map<ApexQualifiedName, ApexOperationStats> operations = new HashMap<>();
private final Map<ApexOperationSignature, Map<ApexQualifiedName, ApexOperationStats>> operations = new HashMap<>();
private final Map<ApexQualifiedName, ApexClassStats> classes = new HashMap<>();
void addOperation(ApexQualifiedName qname, ApexOperationSignature sig) {
if (!operations.containsKey(sig)) {
operations.put(sig, new HashMap<>());
}
operations.get(sig).put(qname, new ApexOperationStats());
}
void addClass(ApexQualifiedName qname) {
classes.put(qname, new ApexClassStats());
}
@Override
public MetricMemoizer<ASTMethod> getOperationStats(QualifiedName qname) {
for (Map<ApexQualifiedName, ApexOperationStats> map : operations.values()) {
ApexOperationStats stats = map.get(qname);
if (stats != null) {
return stats;
}
}
return null;
}
@Override
public MetricMemoizer<ASTUserClass> getClassStats(QualifiedName qname) {
return null;
return classes.get(qname);
}
}

View File

@ -4,15 +4,45 @@
package net.sourceforge.pmd.lang.apex.metrics.signature;
import java.util.HashMap;
import java.util.Map;
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> {
public final class ApexOperationSignature extends ApexSignature implements Signature<ASTMethod> {
private String foo;
private static final Map<Integer, ApexOperationSignature> POOL = new HashMap<>();
/**
* Create a signature using its visibility.
*
* @param visibility The visibility
*/
private ApexOperationSignature(Visibility visibility) {
super(visibility);
}
@Override
public int hashCode() {
return code(visibility);
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
private static int code(Visibility visibility) {
return visibility.hashCode();
}
/**
@ -23,7 +53,13 @@ public class ApexOperationSignature implements Signature<ASTMethod> {
* @return The signature of the node
*/
public static ApexOperationSignature of(ASTMethod node) {
return null;
Visibility visibility = Visibility.get(node);
int code = code(visibility);
if (!POOL.containsKey(code)) {
POOL.put(code, new ApexOperationSignature(visibility));
}
return POOL.get(code);
}
}

View File

@ -4,8 +4,43 @@
package net.sourceforge.pmd.lang.apex.metrics.signature;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode;
/**
* Base class for apex field or method signatures.
*
* @author Clément Fournier
*/
public class ApexSignature {
public abstract class ApexSignature {
/** Visibility of the field or method. */
public final Visibility visibility;
/** Create a signature using its visibility. */
protected ApexSignature(Visibility visibility) {
this.visibility = visibility;
}
/** Visibility of a field or method. */
public enum Visibility {
PRIVATE, PUBLIC, PROTECTED, GLOBAL;
public static Visibility get(ASTMethod method) {
ASTModifierNode modifierNode = method.getFirstChildOfType(ASTModifierNode.class);
if (modifierNode.isPublic()) {
return PUBLIC;
} else if (modifierNode.isPrivate()) {
return PRIVATE;
} else if (modifierNode.isProtected()) {
return PROTECTED;
} else {
return GLOBAL;
}
}
}
}