forked from phoedos/pmd
Implement ApexQualifiableNode for ASTUserEnum.
Include enum in qualified name. Properly handle case where enum is root node. Fixes null deref in ApexQualifiedName.toString() for built-in ASTMethods in enum types. Reference: https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_enum.htm Add tests of qualified and unqualified enums. Change-Id: I691795d40a66f3d3335ab72ad43de7055a6aee31
This commit is contained in:
@@ -8,7 +8,9 @@ import net.sourceforge.pmd.annotation.InternalApi;
|
||||
|
||||
import apex.jorje.semantic.ast.compilation.UserEnum;
|
||||
|
||||
public class ASTUserEnum extends ApexRootNode<UserEnum> {
|
||||
public class ASTUserEnum extends ApexRootNode<UserEnum> implements ApexQualifiableNode {
|
||||
|
||||
private ApexQualifiedName qname;
|
||||
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
@@ -30,4 +32,20 @@ public class ASTUserEnum extends ApexRootNode<UserEnum> {
|
||||
public ASTModifierNode getModifiers() {
|
||||
return getFirstChildOfType(ASTModifierNode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApexQualifiedName getQualifiedName() {
|
||||
if (qname == null) {
|
||||
|
||||
ASTUserClass parent = this.getFirstParentOfType(ASTUserClass.class);
|
||||
|
||||
if (parent != null) {
|
||||
qname = ApexQualifiedName.ofNestedEnum(parent.getQualifiedName(), this);
|
||||
} else {
|
||||
qname = ApexQualifiedName.ofOuterEnum(this);
|
||||
}
|
||||
}
|
||||
|
||||
return qname;
|
||||
}
|
||||
}
|
||||
|
@@ -148,6 +148,20 @@ public final class ApexQualifiedName implements QualifiedName {
|
||||
}
|
||||
|
||||
|
||||
static ApexQualifiedName ofOuterEnum(ASTUserEnum astUserEnum) {
|
||||
String ns = astUserEnum.getNamespace();
|
||||
String[] classes = {astUserEnum.getImage()};
|
||||
return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, classes, null);
|
||||
}
|
||||
|
||||
|
||||
static ApexQualifiedName ofNestedEnum(ApexQualifiedName parent, ASTUserEnum astUserEnum) {
|
||||
String[] classes = Arrays.copyOf(parent.classes, parent.classes.length + 1);
|
||||
classes[classes.length - 1] = astUserEnum.getImage();
|
||||
return new ApexQualifiedName(parent.nameSpace, classes, null);
|
||||
}
|
||||
|
||||
|
||||
private static String getOperationString(ASTMethod node) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(node.getImage()).append('(');
|
||||
@@ -171,18 +185,28 @@ public final class ApexQualifiedName implements QualifiedName {
|
||||
|
||||
|
||||
static ApexQualifiedName ofMethod(ASTMethod node) {
|
||||
ASTUserClassOrInterface<?> parent = node.getFirstParentOfType(ASTUserClassOrInterface.class);
|
||||
if (parent == null) {
|
||||
ASTUserTrigger trigger = node.getFirstParentOfType(ASTUserTrigger.class);
|
||||
String ns = trigger.getNamespace();
|
||||
String targetObj = trigger.getTargetName();
|
||||
|
||||
return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, new String[]{"trigger", targetObj}, trigger.getImage()); // uses a reserved word as a class name to prevent clashes
|
||||
|
||||
} else {
|
||||
ApexQualifiedName baseName = parent.getQualifiedName();
|
||||
// Check first, as enum must be innermost potential parent
|
||||
ASTUserEnum enumParent = node.getFirstParentOfType(ASTUserEnum.class);
|
||||
if (enumParent != null) {
|
||||
ApexQualifiedName baseName = enumParent.getQualifiedName();
|
||||
|
||||
return new ApexQualifiedName(baseName.nameSpace, baseName.classes, getOperationString(node));
|
||||
}
|
||||
|
||||
ASTUserClassOrInterface<?> classParent = node.getFirstParentOfType(ASTUserClassOrInterface.class);
|
||||
if (classParent != null) {
|
||||
ApexQualifiedName baseName = classParent.getQualifiedName();
|
||||
|
||||
return new ApexQualifiedName(baseName.nameSpace, baseName.classes, getOperationString(node));
|
||||
}
|
||||
|
||||
ASTUserTrigger triggerParent = node.getFirstParentOfType(ASTUserTrigger.class);
|
||||
if (triggerParent != null) {
|
||||
String ns = triggerParent.getNamespace();
|
||||
String targetObj = triggerParent.getTargetName();
|
||||
|
||||
return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, new String[]{"trigger", targetObj}, triggerParent.getImage()); // uses a reserved word as a class name to prevent clashes
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -97,4 +98,32 @@ public class ApexQualifiedNameTest extends ApexParserTestBase {
|
||||
assertEquals("c__trigger.Account#myAccountTrigger", m.getQualifiedName().toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedEnum() {
|
||||
ApexNode<Compilation> root = parse("public enum primaryColor { RED, YELLOW, BLUE }");
|
||||
|
||||
ApexQualifiedName enumQName = ASTUserEnum.class.cast(root).getQualifiedName();
|
||||
List<ASTMethod> methods = root.findDescendantsOfType(ASTMethod.class);
|
||||
|
||||
assertEquals("c__primaryColor", enumQName.toString());
|
||||
for (ASTMethod m : methods) {
|
||||
assertTrue(m.getQualifiedName().toString().startsWith("c__primaryColor#"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQualifiedEnum() {
|
||||
ApexNode<Compilation> root = parse("public class Outer { public enum Inner { OK } }");
|
||||
|
||||
ASTUserEnum enumNode = root.getFirstDescendantOfType(ASTUserEnum.class);
|
||||
ApexQualifiedName enumQName = enumNode.getQualifiedName();
|
||||
List<ASTMethod> methods = enumNode.findDescendantsOfType(ASTMethod.class);
|
||||
|
||||
assertEquals("c__Outer.Inner", enumQName.toString());
|
||||
for (ASTMethod m : methods) {
|
||||
assertTrue(m.getQualifiedName().toString().startsWith("c__Outer.Inner#"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user