symfinder Demos

This site references all demos of the symfinder toolchain.


Cypher queries used in symfinder

This document references in the source code the Cypher queries used in symfinder to detect symmetry implementations.

Software construct Commonality / Unchange Variability / Change Symmetry transformation Code for identification
Class subtyping Superclass / Type Subclasses Substitution Queries for symmetry in subtyping
Method overloading Structure Signatures / Arity Substitution Queries for symmetry in overloading
Strategy pattern Strategy interface Algorithms Substitution Detecting strategy patterns
Factory pattern Abstract creator and product Concrete creators and products Factory Detecting factory patterns
Decorator pattern Components and decorator interfaces Concrete components and decorators Composition Detecting decorator patterns
Template pattern Template of a method Method steps Template Detecting template patterns
Density nature Code for identification
Density in subtyping Detecting zones in subtyping
Density in overloading Detecting zones in overloading

Queries for symmetry in subtyping

Two main queries are used to identify symmetry in subtyping. The first one identifies the potential vp-s and the second one their variants.

Identifying potential vp-s

The vp-s in subtyping correspond to interfaces, abstract classes, and concrete extended classes. Hence, these vp-s are identified by querying if a given node holds label INTERFACE, or CLASS and ABSTRACT, or corresponds to the unchanged part of a design pattern, or has class level variants, meaning that a concrete class is a vp only if it has subclasses.

// Identifying vp-s realized by subtyping
MATCH (c)
WHERE (NOT c:OUT_OF_SCOPE)
AND (c:INTERFACE OR (c:CLASS AND c:ABSTRACT)
OR (n:STRATEGY OR n:FACTORY OR n:DECORATOR OR n:TEMPLATE)
OR (EXISTS(c.classVariants) AND c.classVariants > 0))
SET c:VP

source

Identifying potential variants

The variants of a vp realized by subtyping correspond to subclasses. Hence, they are identified by querying if a given node is related to another node that holds label CLASS or INTERFACE, which is identified as a vp, and which relation holds label EXTENDS or IMPLEMENTS.

// Identifying variants of a vp realized by subtyping
MATCH (sc:VP)-[:EXTENDS|IMPLEMENTS]->(c)
WHERE c:CLASS OR c:INTERFACE
SET c:VARIANT

source

Queries for symmetry in overloading

Four queries are used to identify symmetry in overloading. They identify the total number of potential vp-s and variants realized by overloading in each class node.

Identifying potential vp-s

The vp-s realized by overloading correspond to overloaded methods and constructors. First, for each class node is counted the number of overloaded methods using the name of method nodes that are linked to that class node. Then, the total number of overloaded methods for that class node is stored in its methodVPs attribute.

// Identifying the number of vp-s realized by method overloading
MATCH (c:CLASS)-->(a:METHOD) MATCH (c:CLASS)-->(b:METHOD)
WHERE a.name = b.name AND ID(a) <> ID(b)
WITH COUNT(DISTINCT a.name) AS cnt, c
SET c.methodVPs = cnt

// When there is no vp, set the node attribute to zero
MATCH (c:CLASS)
WHERE NOT EXISTS(c.methodVPs)
SET c.methodVPs = 0

source

Secondly, the number of vp-s realized by constructor overloading is obtained in a similar way.

// Identifying the number of vp-s realized by constructor overloading. If there is more than one constructor, this means that it is overloaded, hence the value is 1.
MATCH (c:CLASS)-->(a:CONSTRUCTOR)
WITH COUNT(a.name) AS cnt, c
SET c.constructorVPs = CASE WHEN cnt > 1 THEN 1 ELSE 0 END

// When there is no vp, set the node attribute to zero
MATCH (c:CLASS)
WHERE NOT EXISTS(c.constructorVPs)
SET c.constructorVPs = 0

source

Then, a node class has vp-s realized by overloading if it has at least one overloaded method or constructor.

// Total number of identified vp-s realized by overloading
MATCH (c)
WHERE (NOT c:OUT_OF_SCOPE) AND (c.methodVPs > 0 OR c.constructorVPs > 0)
SET c:METHOD_LEVEL_VP

source

Identifying potential variants

The variants realized by overloading correspond to the overloaded methods and constructors. Hence, for each overloaded method in a class node is counted how many times it is overloaded and then the resulted number of all overloaded methods for that class node is stored in its methodVariants attribute.

// Identifying the number of variants realized by method overloading
MATCH (c:CLASS)-->(a:METHOD) MATCH (c:CLASS)-->(b:METHOD)
WHERE a.name = b.name AND ID(a) <> ID(b)
WITH count(DISTINCT a) AS cnt, c
SET c.methodVariants = cnt

// When there is no variant, set the node attribute to zero
MATCH (c:CLASS)
WHERE NOT EXISTS(c.methodVariants)
SET c.methodVariants = 0

source

Then, the number of variants realized by constructor overloading is obtained in a similar way.

// Identifying the number of variants realized by constructor overloading. If there is no overload constructor, that is, there is 0 or 1, then the attribute is set to 0.
MATCH (c:CLASS)-->(a:CONSTRUCTOR)
WITH COUNT(a.name) AS cnt, c
MATCH (c:CLASS) SET c.constructorVariants = CASE
WHERE NOT EXISTS(c.methodVPs) WHEN cnt > 1 THEN cnt ELSE 0 END

// When there is no variant, set the node attribute to zero
MATCH (c:CLASS)
WHERE NOT EXISTS(c.constructorVariants)
SET c.constructorVariants = 0

source

Detecting strategy patterns

A strategy pattern is defined as follows:

  • a class who possesses at least two variants and is used as a field in another class
  • a class whose name contains “Strategy”

After populating the database with the classes and methods, and the relationships between them (inheritance between classes, and methods defined in classes), the codebase is parsed an additional time to identify the classes used as fields and matching the aforementioned criterias.

if (fieldTypeBinding.getName().contains("Strategy") || neoGraph.getNbVariants(node) >= 2) {
    neoGraph.addLabelToNode(node, DesignPatternType.STRATEGY.toString());
}

source

Detecting decorator patterns

A decorator pattern is defined as follows:

  • a class which possesses at least one subclass and a field corresponding to a class having at least two subclasses
  • a class whose name contains “Decorator”

After populating the database with the classes and methods, and the relationships between them (inheritance between classes, and methods defined in classes), the codebase is parsed an additional time to identify the classes used as fields and matching the aforementioned criterias.

if (fieldTypeBinding.getName().contains("Decorator")) {
    neoGraph.addLabelToNode(node, DesignPatternType.DECORATOR.toString());
}
    checkAbstractDecorator(fieldDeclaringClassBinding, fieldTypeBinding);  // finds the structural pattern
}

source

Detecting template patterns

A template pattern is defined as follows:

  • an abstract class which possesses at least one subclass and contains a concrete method calling an abstract method of this same class
  • a class whose name contains “Template”

After populating the database with the classes and methods, and the relationships between them (inheritance between classes, and methods defined in classes), the codebase is parsed an additional time to identify, for every called method, if it corresponds to an abstract method defined in the same class.

if (neoGraph.getNbVariants(declaringClassNode) > 0 && (declaringClass.getName().contains("Template") || (declaringClass.equals(this.thisClassBinding) && Modifier.isAbstract(methodBinding.getModifiers())))) {
    neoGraph.addLabelToNode(declaringClassNode, DesignPatternType.TEMPLATE.toString());
}

source

Detecting factory patterns

A factory pattern is defined as follows:

  • a class who possesses a method which returns an object whose type is a subtype of the method return type
  • a class whose name contains “Factory”

After populating the database with the classes and methods, and the relationships between them (inheritance between classes, and methods defined in classes), the codebase is parsed an additional time to identify:

  • the classes whose name contains “Factory”;
if (qualifiedName.contains("Factory")) {
    neoGraph.addLabelToNode(neoGraph.getOrCreateNode(qualifiedName, type.resolveBinding().isInterface() ? EntityType.INTERFACE : EntityType.CLASS), DesignPatternType.FACTORY.toString());
}

source

  • the classes who possess a method which returns an object whose type is a subtype of the method return type.
if (neoGraph.relatedTo(methodReturnTypeNode, returnedObjectTypeNode) && neoGraph.getNbVariants(methodReturnTypeNode) >= 2) {
    neoGraph.addLabelToNode(parsedClassNode, DesignPatternType.FACTORY.toString());
}

source

Queries for zones of high density of symmetries

To identify classes being part of a zone of high density of symmetries, we query the database to identify the class level vp-s possessing a particularly high number of variants, being at the class or method level. Prior to the analysis, the user sets a threshold in symfinder’s configuration file, which will be used as a minimum number of variants to consider a vp and its variants as a zone of high density of symmetries.

Detecting zones in subtyping

All vp-s having at least $threshold (defined by the user) class level variants, and the variants, have their hotspot property set to TRUE.

// Identifying high density zones in subtyping
MATCH (vp:VP)-->(v:VARIANT)
WITH count(v) as cnt, [vp] + collect(v) AS collected
WHERE cnt >= $threshold
FOREACH (v1 IN collected | SET v1.hotspot = TRUE)

source

Detecting zones in overloading

All nodes having at least $threshold (same value as for the detection in subtyping) method level variants (i.e. method variants and constructor variants together), have their hotspot property set to TRUE.

// Identifying high density zones in overloading
MATCH (n)
WHERE n.methodVariants + n.constructorVariants >= $threshold
SET n.hotspot = TRUE

source

Finally, all nodes having their hotspot property set to TRUE are labeled as HOTSPOTs.

// Identifying all nodes in high density zones
MATCH (n)
WHERE n.hotspot = TRUE
SET n:HOTSPOT

source