11package es .upv .mist .slicing .nodes ;
22
3+ import com .github .javaparser .resolution .types .ResolvedType ;
34import es .upv .mist .slicing .nodes .oo .MemberNode ;
5+ import es .upv .mist .slicing .nodes .oo .PolyMemberNode ;
46import es .upv .mist .slicing .utils .Utils ;
57
68import java .util .*;
79import java .util .regex .Matcher ;
810import java .util .regex .Pattern ;
11+ import java .util .stream .Collectors ;
12+ import java .util .stream .Stream ;
13+
14+ import static es .upv .mist .slicing .graphs .cfg .CFGBuilder .VARIABLE_NAME_OUTPUT ;
15+ import static es .upv .mist .slicing .graphs .exceptionsensitive .ESCFG .ACTIVE_EXCEPTION_VARIABLE ;
916
1017/**
1118 * A tree data structure that mimics the tree found in an object's fields.
@@ -22,7 +29,7 @@ public class ObjectTree implements Cloneable {
2229 public static final String ROOT_NAME = "-root-" ;
2330
2431 /** Regex pattern to split the root from the fields of a field access expression. */
25- private static final Pattern FIELD_SPLIT = Pattern .compile ("^(?<root>(([_0-9A-Za-z]+\\ .)*this)|([_0-9A-Za-z]+)|(-root- ))(\\ .(?<fields>.+))?$" );
32+ private static final Pattern FIELD_SPLIT = Pattern .compile ("^(?<root>(([_0-9A-Za-z]+\\ .)*this)|([_0-9A-Za-z]+)|(" + ROOT_NAME + ")|(" + VARIABLE_NAME_OUTPUT + ")|(" + ACTIVE_EXCEPTION_VARIABLE + " ))(\\ .(?<fields>.+))?$" );
2633
2734 /** Direct children of this tree node, mapped by field name. */
2835 private final Map <String , ObjectTree > childrenMap = new HashMap <>();
@@ -36,12 +43,22 @@ public ObjectTree() {
3643
3744 /** Create a root of a new object tree with the given name. */
3845 public ObjectTree (String memberName ) {
39- memberNode = new MemberNode (memberName , null );
46+ this ( new MemberNode (memberName , null ) );
4047 }
4148
4249 /** Create a child tree node for the given field, whose node is linked to the given parent. */
4350 private ObjectTree (String memberName , ObjectTree parent ) {
44- this .memberNode = new MemberNode (memberName , parent .memberNode );
51+ this (new MemberNode (memberName , parent .memberNode ));
52+ }
53+
54+ /** Create a child tree node for the given type, whose node is linked to the given parent. */
55+ private ObjectTree (ResolvedType resolvedType , ObjectTree parent ) {
56+ this (new PolyMemberNode (resolvedType , parent .memberNode ));
57+ }
58+
59+ /** Create a child tree with the given member node. */
60+ private ObjectTree (MemberNode memberNode ) {
61+ this .memberNode = memberNode ;
4562 }
4663
4764 /** The name of the variable or field represented by this tree. It doesn't include ancestors. */
@@ -58,6 +75,44 @@ public boolean hasChildren() {
5875 return !childrenMap .isEmpty ();
5976 }
6077
78+ /** Whether the field passed as argument has children. */
79+ public boolean hasChildren (String memberWithRoot ) {
80+ String member = removeRoot (memberWithRoot );
81+ if (member .isEmpty ())
82+ return hasChildren ();
83+ return hasChildrenInternal (member );
84+ }
85+
86+ protected boolean hasChildrenInternal (String members ) {
87+ if (members .contains ("." )) {
88+ int firstDot = members .indexOf ('.' );
89+ String first = members .substring (0 , firstDot );
90+ String rest = members .substring (firstDot + 1 );
91+ childrenMap .computeIfAbsent (first , f -> new ObjectTree (f , this ));
92+ return childrenMap .get (first ).hasChildrenInternal (rest );
93+ } else {
94+ return childrenMap .get (members ).hasChildren ();
95+ }
96+ }
97+
98+ /** Whether this object tree immediately contains polymorphic nodes. */
99+ public boolean hasPoly () {
100+ return childrenMap .values ().stream ().anyMatch (ot -> ot .getMemberNode () instanceof PolyMemberNode );
101+ }
102+
103+ /** A set of entry pairs, containing the field name and its corresponding tree. It is unmodifiable. */
104+ public Set <Map .Entry <String , ObjectTree >> entrySet () {
105+ return Collections .unmodifiableSet (childrenMap .entrySet ());
106+ }
107+
108+ /** Insert a polymorphic node for the given type. The type node will be
109+ * generated immediately beneath this tree node. */
110+ public ObjectTree addType (ResolvedType rt ) {
111+ assert !rt .describe ().isBlank ();
112+ assert !(memberNode instanceof PolyMemberNode );
113+ return childrenMap .computeIfAbsent (rt .describe (), n -> new ObjectTree (rt , this ));
114+ }
115+
61116 /**
62117 * Insert a field with the given name. This method should only be called on a root object tree.
63118 * This method may be used to add multiple levels simultaneously, calling this method with
@@ -66,22 +121,30 @@ public boolean hasChildren() {
66121 * @param fieldName The field to be added, should include the root variable name. For example,
67122 * to add the field "x" to a variable "a", this argument should be "a.x".
68123 */
69- public void addField (String fieldName ) {
124+ public ObjectTree addField (String fieldName ) {
70125 String members = removeRoot (fieldName );
71- addNonRootField (members );
126+ return addNonRootField (members );
127+ }
128+
129+ /** Insert a field in the current level of object tree. The field should be a variable name,
130+ * and not contain dots or be blank. */
131+ public ObjectTree addImmediateField (String fieldName ) {
132+ if (fieldName .contains ("." ) || fieldName .isBlank ())
133+ throw new IllegalArgumentException ("field name must not include dots or be blank!" );
134+ return childrenMap .computeIfAbsent (fieldName , f -> new ObjectTree (f , this ));
72135 }
73136
74137 /** Similar to {@link #addField(String)}, but may be called at any level
75138 * and the argument must not contain the root variable. */
76- private void addNonRootField (String members ) {
139+ private ObjectTree addNonRootField (String members ) {
77140 if (members .contains ("." )) {
78141 int firstDot = members .indexOf ('.' );
79142 String first = members .substring (0 , firstDot );
80143 String rest = members .substring (firstDot + 1 );
81144 childrenMap .computeIfAbsent (first , f -> new ObjectTree (f , this ));
82- childrenMap .get (first ).addNonRootField (rest );
145+ return childrenMap .get (first ).addNonRootField (rest );
83146 } else {
84- childrenMap .computeIfAbsent (members , f -> new ObjectTree (f , this ));
147+ return childrenMap .computeIfAbsent (members , f -> new ObjectTree (f , this ));
85148 }
86149 }
87150
@@ -96,25 +159,39 @@ public void addAll(ObjectTree tree) {
96159 }
97160
98161 /**
99- * Copies a subtree from source into another subtree in target.
100- *
162+ * Copies a subtree from source into another subtree in target. The tree may be
163+ * pasted multiple times, if there are polymorphic nodes that are not explicitly marked
164+ * in the prefix arguments.
101165 * @param source The source of the nodes.
102166 * @param target The tree where nodes will be added
103167 * @param sourcePrefix The prefix to be consumed before copying nodes. Without root.
104168 * @param targetPrefix The prefix to be consumed before copying nodes. Without root.
105169 */
106170 public static void copyTargetTreeToSource (ObjectTree source , ObjectTree target , String sourcePrefix , String targetPrefix ) {
107- ObjectTree a = source .findObjectTreeOfMember (sourcePrefix );
108- ObjectTree b = target .findObjectTreeOfMember (targetPrefix );
109- a .addAll (b );
171+ Collection <ObjectTree > a = source .findObjectTreeOfPolyMember (sourcePrefix );
172+ Collection <ObjectTree > b = target .findObjectTreeOfPolyMember (targetPrefix );
173+ for (ObjectTree sourceTree : a )
174+ for (ObjectTree targetTree : b )
175+ sourceTree .addAll (targetTree );
110176 }
111177
112- /**
113- * Locate an object tree that represents a field of this object.
114- * @param member The field, without a root prefix.
115- */
116- ObjectTree findObjectTreeOfMember (String member ) {
117- ObjectTree result = this ;
178+ /** Obtains the set of nodes in this object tree that have no children. */
179+ public Collection <MemberNode > leaves () {
180+ return streamLeaves ().collect (Collectors .toSet ());
181+ }
182+
183+ /** @see #leaves() */
184+ protected Stream <MemberNode > streamLeaves () {
185+ if (childrenMap .isEmpty ())
186+ return Stream .of (memberNode );
187+ return childrenMap .values ().stream ()
188+ .flatMap (ObjectTree ::streamLeaves );
189+ }
190+
191+ /** Similar to {@link #getNodesForPoly(String)}, but returns object trees
192+ * instead of member nodes. */
193+ Collection <ObjectTree > findObjectTreeOfPolyMember (String member ) {
194+ Collection <ObjectTree > result = List .of (this );
118195 while (!member .isEmpty ()) {
119196 int firstDot = member .indexOf ('.' );
120197 String first , rest ;
@@ -125,7 +202,22 @@ ObjectTree findObjectTreeOfMember(String member) {
125202 first = member ;
126203 rest = "" ;
127204 }
128- result = result .childrenMap .get (first );
205+ result = result .stream ().flatMap (res -> {
206+ ObjectTree ot = res .childrenMap .get (first );
207+ if (ot == null && res .childrenMap .size () > 0 ) {
208+ Collection <ObjectTree > collection = new LinkedList <>();
209+ for (ObjectTree child : childrenMap .values ()) {
210+ if (!(child .getMemberNode () instanceof PolyMemberNode ) || !child .childrenMap .containsKey (first ))
211+ throw new IllegalArgumentException ("Could not locate member in object tree" );
212+ collection .add (child .childrenMap .get (first ));
213+ }
214+ return collection .stream ();
215+ } else if (ot == null ) {
216+ throw new IllegalArgumentException ("Could not locate member in object tree" );
217+ } else {
218+ return Stream .of (ot );
219+ }
220+ }).collect (Collectors .toList ());
129221 member = rest ;
130222 }
131223 return result ;
@@ -134,19 +226,34 @@ ObjectTree findObjectTreeOfMember(String member) {
134226 /** Whether this object tree contains the given member. The argument should contain the root variable name. */
135227 public boolean hasMember (String member ) {
136228 String field = removeRoot (member );
137- return hasNonRootMember (field );
229+ return hasNonRootMember (field , false );
230+ }
231+
232+ /** Whether this object tree contains the given member. The argument may omit typing
233+ * information (i.e., 'a.x' will find 'a.A.x', where A is a polymorphic node). */
234+ public boolean hasPolyMember (String member ) {
235+ String field = removeRoot (member );
236+ return hasNonRootMember (field , true );
138237 }
139238
140239 /** Similar to hasMember, but valid at any level of the tree and the argument should not contain
141240 * the root variable's name.
142241 * @see #hasMember(String) */
143- private boolean hasNonRootMember (String members ) {
242+ private boolean hasNonRootMember (String members , boolean polymorphic ) {
144243 if (members .contains ("." )) {
145244 int firstDot = members .indexOf ('.' );
146245 String first = members .substring (0 , firstDot );
147246 String rest = members .substring (firstDot + 1 );
148- return childrenMap .containsKey (first ) && childrenMap .get (first ).hasNonRootMember (rest );
247+ if (polymorphic && !childrenMap .containsKey (first ) && !childrenMap .isEmpty ())
248+ return childrenMap .values ().stream ()
249+ .filter (ot -> ot .getMemberNode () instanceof PolyMemberNode )
250+ .anyMatch (ot -> ot .hasNonRootMember (members , true ));
251+ return childrenMap .containsKey (first ) && childrenMap .get (first ).hasNonRootMember (rest , polymorphic );
149252 } else {
253+ if (polymorphic && !childrenMap .containsKey (members ) && !childrenMap .isEmpty ())
254+ return childrenMap .values ().stream ()
255+ .filter (ot -> ot .getMemberNode () instanceof PolyMemberNode )
256+ .anyMatch (ot -> ot .hasNonRootMember (members , true ));
150257 return childrenMap .containsKey (members );
151258 }
152259 }
@@ -174,6 +281,16 @@ MemberNode getNodeForNonRoot(String members) {
174281 }
175282 }
176283
284+ /** Similar to {@link #getNodeFor(String)}, but if the argument does not contain
285+ * types, it will obtain all member nodes that represent a given field (in multiple
286+ * types). For example, the argument 'a.x' may produce 'a.A.x' and 'a.B.x'; whereas
287+ * the argument 'a.A.x' will only produce one node. */
288+ public Collection <MemberNode > getNodesForPoly (String memberWithRoot ) {
289+ return findObjectTreeOfPolyMember (removeRoot (memberWithRoot )).stream ()
290+ .map (ObjectTree ::getMemberNode )
291+ .collect (Collectors .toList ());
292+ }
293+
177294 /** @return An iterable through the names (with full prefixes) of all members of this tree,
178295 * excluding the root. */
179296 public Iterable <String > nameIterable () {
@@ -192,14 +309,16 @@ public String next() {
192309 MemberNode node = element .memberNode ;
193310 if (node == null )
194311 return ROOT_NAME ;
312+ else if (node instanceof PolyMemberNode )
313+ return next ();
195314 else
196315 builder .append (node .getLabel ());
197- while (node .getParent () instanceof MemberNode && node . getParent (). getLabel (). matches ( "^(USE|DEF|DEC) \\ {" ) ) {
316+ while (node .getParent () instanceof MemberNode ) {
198317 node = (MemberNode ) node .getParent ();
199318 builder .insert (0 , '.' );
200319 builder .insert (0 , node .getLabel ());
201320 }
202- return builder .insert ( 0 , ROOT_NAME + "." ). toString ();
321+ return builder .toString ();
203322 }
204323 };
205324 }
@@ -248,11 +367,6 @@ public ObjectTree next() {
248367 };
249368 }
250369
251- /** @see #treeIterator() */
252- Iterable <ObjectTree > treeIterable () {
253- return this ::treeIterator ;
254- }
255-
256370 @ SuppressWarnings ("MethodDoesntCallSuperMethod" )
257371 @ Override
258372 public Object clone () {
@@ -263,7 +377,7 @@ public Object clone() {
263377 }
264378
265379 private ObjectTree clone (ObjectTree parent ) {
266- ObjectTree clone = new ObjectTree (getMemberName (), parent );
380+ ObjectTree clone = new ObjectTree (getMemberNode (). copyToParent ( parent . getMemberNode ()) );
267381 for (Map .Entry <String , ObjectTree > entry : childrenMap .entrySet ())
268382 clone .childrenMap .put (entry .getKey (), entry .getValue ().clone (clone ));
269383 return clone ;
0 commit comments