LogicalPointcutStructure.java
/*******************************************************************************
* Copyright (c) 2023 Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*******************************************************************************/
package org.aspectj.weaver.patterns;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Test helper class reflecting nesting structure for AND ('&&'), OR ('||') and NOT ('!') pointcuts, enabling
* comparisons disregarding order of AND/OR pointcuts on the same nesting level. For this class, there is no difference
* between 'A && B && C', 'A && C && B', 'C && B '&& A' etc., i.e. the commutative law is respected.
*
* @author Alexander Kriegisch
*/
public class LogicalPointcutStructure {
private enum PointcutType { NOT, AND, OR, TEXT }
private final PointcutType type;
private final List<LogicalPointcutStructure> children = new ArrayList<>();
private final Set<LogicalPointcutStructure> childrenSet = new HashSet<>();
private final String text;
private LogicalPointcutStructure(PointcutType type, LogicalPointcutStructure... children) {
this(type, null, children);
}
private LogicalPointcutStructure(PointcutType type, String text) {
this(type, text, (LogicalPointcutStructure[]) null);
}
private LogicalPointcutStructure(PointcutType type, String text, LogicalPointcutStructure... children) {
if (type == null)
throw new IllegalArgumentException("pointcutType must be != null");
if (text == null && children == null)
throw new IllegalArgumentException("either text or children must be != null");
if (text != null && children != null)
throw new IllegalArgumentException("cannot have both text and children, one must be null");
if (text != null && type != PointcutType.TEXT)
throw new IllegalArgumentException("if text is given, type must match to be TEXT");
if (children != null && type == PointcutType.TEXT)
throw new IllegalArgumentException("if children are given, type must be != TEXT");
this.type = type;
this.text = text;
if (children != null) {
this.children.addAll(Arrays.asList(children));
this.childrenSet.addAll(this.children);
}
}
public PointcutType getType() {
return type;
}
public List<LogicalPointcutStructure> getChildren() {
return children;
}
public String getText() {
return text;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
LogicalPointcutStructure that = (LogicalPointcutStructure) o;
if (type != that.type)
return false;
if (!childrenSet.equals(that.childrenSet))
return false;
return Objects.equals(text, that.text);
}
@Override
public int hashCode() {
int result = type.hashCode();
result = 31 * result + childrenSet.hashCode();
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
@Override
public String toString() {
return type == PointcutType.TEXT
? "\"" + text + "\""
: type + "(" + children.toString().replaceFirst("^.(.*).$", "$1") + ")";
}
public static LogicalPointcutStructure NOT(LogicalPointcutStructure child) {
return new LogicalPointcutStructure(PointcutType.NOT, child);
}
public static LogicalPointcutStructure NOT(Object childObject) {
LogicalPointcutStructure child;
if (childObject instanceof LogicalPointcutStructure)
child = (LogicalPointcutStructure) childObject;
else if (childObject instanceof String)
child = TEXT((String) childObject);
else
throw new IllegalArgumentException("each child must be either LogicalPointcutStructure or String");
return new LogicalPointcutStructure(PointcutType.NOT, child);
}
public static LogicalPointcutStructure AND(LogicalPointcutStructure... children) {
return new LogicalPointcutStructure(PointcutType.AND, children);
}
public static LogicalPointcutStructure AND(Object... childObjects) {
LogicalPointcutStructure[] children = new LogicalPointcutStructure[childObjects.length];
LogicalPointcutStructure child;
Object childObject;
for (int i = 0; i < childObjects.length; i++) {
childObject = childObjects[i];
if (childObject instanceof LogicalPointcutStructure)
child = (LogicalPointcutStructure) childObject;
else if (childObject instanceof String)
child = TEXT((String) childObject);
else
throw new IllegalArgumentException("each child must be either LogicalPointcutStructure or String");
children[i] = child;
}
return new LogicalPointcutStructure(PointcutType.AND, children);
}
public static LogicalPointcutStructure OR(LogicalPointcutStructure... children) {
return new LogicalPointcutStructure(PointcutType.OR, children);
}
public static LogicalPointcutStructure OR(Object... childObjects) {
LogicalPointcutStructure[] children = new LogicalPointcutStructure[childObjects.length];
LogicalPointcutStructure child;
Object childObject;
for (int i = 0; i < childObjects.length; i++) {
childObject = childObjects[i];
if (childObject instanceof LogicalPointcutStructure)
child = (LogicalPointcutStructure) childObject;
else if (childObject instanceof String)
child = TEXT((String) childObject);
else
throw new IllegalArgumentException("each child must be either LogicalPointcutStructure or String");
children[i] = child;
}
return new LogicalPointcutStructure(PointcutType.OR, children);
}
public static LogicalPointcutStructure TEXT(String text) {
return new LogicalPointcutStructure(PointcutType.TEXT, text);
}
public static LogicalPointcutStructure fromPointcut(Pointcut pointcut) {
if (pointcut instanceof NotPointcut) {
NotPointcut notPointcut = (NotPointcut) pointcut;
return NOT(fromPointcut(notPointcut.getNegatedPointcut()));
}
else if (pointcut instanceof AndPointcut) {
List<LogicalPointcutStructure> children = new ArrayList<>();
AndPointcut andPointcut = (AndPointcut) pointcut;
children.add(fromPointcut(andPointcut.getRight()));
while (andPointcut.getLeft() instanceof AndPointcut) {
andPointcut = (AndPointcut) andPointcut.getLeft();
children.add(fromPointcut(andPointcut.getRight()));
}
children.add(fromPointcut(andPointcut.getLeft()));
return AND(children.toArray(new LogicalPointcutStructure[0]));
}
else if (pointcut instanceof OrPointcut) {
List<LogicalPointcutStructure> children = new ArrayList<>();
OrPointcut orPointcut = (OrPointcut) pointcut;
children.add(fromPointcut(orPointcut.getRight()));
while (orPointcut.getLeft() instanceof OrPointcut) {
orPointcut = (OrPointcut) orPointcut.getLeft();
children.add(fromPointcut(orPointcut.getRight()));
}
children.add(fromPointcut(orPointcut.getLeft()));
return OR(children.toArray(new LogicalPointcutStructure[0]));
}
else {
return TEXT(pointcut.toString());
}
}
public static void main(String[] args) {
// true
System.out.println(verifyToString(
OR("A", NOT(OR("B", "C"))),
"OR(\"A\", NOT(OR(\"B\", \"C\")))"
));
// true
System.out.println(verifyToString(
OR(AND("A", NOT(OR("B", "C")), "D"), NOT(OR("E", "F"))),
"OR(AND(\"A\", NOT(OR(\"B\", \"C\")), \"D\"), NOT(OR(\"E\", \"F\")))"
));
// true
System.out.println(verifyEquals(
OR("A", NOT(OR("B", "C"))),
OR(NOT(OR("C", "B")), "A")
));
// true
System.out.println(verifyEquals(
OR(AND("A", NOT(OR("B", "C")), "D"), NOT(OR("E", "F"))),
OR(NOT(OR("F", "E")), AND("A", NOT(OR("C", "B")), "D"))
));
// false
System.out.println(verifyEquals(
OR(AND("A", NOT(OR("B", "C")), "D"), NOT(OR("E", "F"))),
OR(NOT(OR("F", "E")), AND(NOT(OR("C", "B", "D")), "A"))
));
}
private static boolean verifyToString(LogicalPointcutStructure structure, String toStringExpected) {
System.out.println();
System.out.println("Expected: " + toStringExpected);
System.out.println("Actual: " + structure);
return toStringExpected.equals(structure.toString());
}
private static boolean verifyEquals(LogicalPointcutStructure structure1, LogicalPointcutStructure structure2) {
System.out.println();
System.out.println("Structure 1: " + structure1);
System.out.println("Structure 2: " + structure2);
return structure1.equals(structure2);
}
}