|
22 | 22 | import com.salesforce.graph.symbols.apex.ApexStandardValue; |
23 | 23 | import com.salesforce.graph.symbols.apex.ApexValue; |
24 | 24 | import com.salesforce.graph.symbols.apex.system.SystemSchema; |
25 | | -import com.salesforce.graph.vertex.AbstractReferenceExpressionVertex; |
26 | | -import com.salesforce.graph.vertex.ArrayLoadExpressionVertex; |
27 | | -import com.salesforce.graph.vertex.AssignmentExpressionVertex; |
28 | | -import com.salesforce.graph.vertex.BaseSFVertex; |
29 | | -import com.salesforce.graph.vertex.BinaryExpressionVertex; |
30 | | -import com.salesforce.graph.vertex.ChainedVertex; |
31 | | -import com.salesforce.graph.vertex.DeclarationVertex; |
32 | | -import com.salesforce.graph.vertex.EmptyReferenceExpressionVertex; |
33 | | -import com.salesforce.graph.vertex.FieldVertex; |
34 | | -import com.salesforce.graph.vertex.InvocableVertex; |
35 | | -import com.salesforce.graph.vertex.InvocableWithParametersVertex; |
36 | | -import com.salesforce.graph.vertex.LiteralExpressionVertex; |
37 | | -import com.salesforce.graph.vertex.MethodCallExpressionVertex; |
38 | | -import com.salesforce.graph.vertex.MethodVertex; |
39 | | -import com.salesforce.graph.vertex.NewCollectionExpressionVertex; |
40 | | -import com.salesforce.graph.vertex.NewObjectExpressionVertex; |
41 | | -import com.salesforce.graph.vertex.ParameterVertex; |
42 | | -import com.salesforce.graph.vertex.PostfixExpressionVertex; |
43 | | -import com.salesforce.graph.vertex.PrefixExpressionVertex; |
44 | | -import com.salesforce.graph.vertex.ReferenceExpressionVertex; |
45 | | -import com.salesforce.graph.vertex.SFVertexFactory; |
46 | | -import com.salesforce.graph.vertex.SoqlExpressionVertex; |
47 | | -import com.salesforce.graph.vertex.SuperMethodCallExpressionVertex; |
48 | | -import com.salesforce.graph.vertex.TernaryExpressionVertex; |
49 | | -import com.salesforce.graph.vertex.ThisMethodCallExpressionVertex; |
50 | | -import com.salesforce.graph.vertex.Typeable; |
51 | | -import com.salesforce.graph.vertex.UserClassVertex; |
52 | | -import com.salesforce.graph.vertex.VariableExpressionVertex; |
| 25 | +import com.salesforce.graph.vertex.*; |
53 | 26 | import com.salesforce.graph.visitor.ApexPathWalker; |
54 | 27 | import com.salesforce.graph.visitor.DefaultNoOpPathVertexVisitor; |
55 | 28 | import com.salesforce.messaging.CliMessager; |
|
75 | 48 | public final class MethodUtil { |
76 | 49 | private static final Logger LOGGER = LogManager.getLogger(MethodUtil.class); |
77 | 50 |
|
| 51 | + /** |
| 52 | + * Seeks the declaration of a method matching {@code signature} on {@code definingType} (or |
| 53 | + * optionally on its parent classes/interfaces). |
| 54 | + * |
| 55 | + * @param g - The graph containing the codebase |
| 56 | + * @param definingType - The type on which we should begin our search |
| 57 | + * @param signature - The method signature we're looking for |
| 58 | + * @param includeInheritedMethods - |
| 59 | + * @return - An Optional containing the matching method, if it exists. |
| 60 | + */ |
| 61 | + public static Optional<MethodVertex> getMethodWithSignature( |
| 62 | + GraphTraversalSource g, |
| 63 | + String definingType, |
| 64 | + String signature, |
| 65 | + boolean includeInheritedMethods) { |
| 66 | + // First, we need to check the target type itself. |
| 67 | + // Start by getting all the methods it defines directly. |
| 68 | + List<MethodVertex> methodsOnType = |
| 69 | + SFVertexFactory.loadVertices( |
| 70 | + g, g.V().where(H.has(NodeType.METHOD, Schema.DEFINING_TYPE, definingType))); |
| 71 | + // Check each method's signature and return it if it matches. |
| 72 | + for (MethodVertex methodVertex : methodsOnType) { |
| 73 | + if (methodVertex.getSignature().equalsIgnoreCase(signature)) { |
| 74 | + return Optional.of(methodVertex); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // If the type itself has no match, but we're asked to check inherited versions, do so. |
| 79 | + if (includeInheritedMethods) { |
| 80 | + InheritableSFVertex definingTypeVertex = |
| 81 | + SFVertexFactory.loadSingleOrNull( |
| 82 | + g, |
| 83 | + g.V() |
| 84 | + .where( |
| 85 | + H.has( |
| 86 | + Arrays.asList( |
| 87 | + NodeType.USER_CLASS, |
| 88 | + NodeType.USER_INTERFACE), |
| 89 | + Schema.DEFINING_TYPE, |
| 90 | + definingType))); |
| 91 | + if (definingTypeVertex != null) { |
| 92 | + // There are two forms of inheritance we need to check. |
| 93 | + // First, if the type has a superclass, then the method could be defined on that |
| 94 | + // superclass. |
| 95 | + // If the class has a superclass, we need to check that. |
| 96 | + Optional<String> superClassOptional = definingTypeVertex.getSuperClassName(); |
| 97 | + if (superClassOptional.isPresent()) { |
| 98 | + // Recursive call on the superclass gets us the superclass's result. |
| 99 | + // If the result is present and non-private, then it's inherited by the target |
| 100 | + // class. |
| 101 | + Optional<MethodVertex> superclassResult = |
| 102 | + getMethodWithSignature(g, superClassOptional.get(), signature, true); |
| 103 | + if (superclassResult.isPresent() && !superclassResult.get().isPrivate()) { |
| 104 | + return superclassResult; |
| 105 | + } |
| 106 | + } |
| 107 | + // Second, if the defining type is an abstract class that implements interfaces, |
| 108 | + // then the method could be defined on one of those interfaces but implemented |
| 109 | + // on a concrete subclass. |
| 110 | + if (definingTypeVertex instanceof UserClassVertex) { |
| 111 | + UserClassVertex userClassVertex = (UserClassVertex) definingTypeVertex; |
| 112 | + if (userClassVertex.isAbstract()) { |
| 113 | + List<String> interfaceNames = userClassVertex.getInterfaceNames(); |
| 114 | + for (String interfaceName : interfaceNames) { |
| 115 | + // Recursive call on the interface gets us the interface's result. |
| 116 | + Optional<MethodVertex> interfaceResult = |
| 117 | + getMethodWithSignature(g, interfaceName, signature, true); |
| 118 | + if (interfaceResult.isPresent()) { |
| 119 | + return interfaceResult; |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + // If we still haven't found anything, we're done. Return an empty optional. |
| 127 | + return Optional.empty(); |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Find all {@link VariableDeclarationVertex} instances representing variables declared in the |
| 132 | + * scope of {@code method}. |
| 133 | + */ |
| 134 | + public static List<VariableDeclarationVertex> getVariableDeclarations( |
| 135 | + GraphTraversalSource g, MethodVertex method) { |
| 136 | + return SFVertexFactory.loadVertices( |
| 137 | + g, |
| 138 | + g.V() |
| 139 | + .hasId(method.getId()) |
| 140 | + .repeat(__.out(Schema.CHILD)) |
| 141 | + .until(__.hasLabel(NodeType.VARIABLE_DECLARATION))); |
| 142 | + } |
| 143 | + |
78 | 144 | public static List<MethodVertex> getTargetedMethods( |
79 | 145 | GraphTraversalSource g, List<RuleRunnerTarget> targets) { |
80 | 146 | // The targets passed into this method should exclusively be ones that target specific |
|
0 commit comments