Skip to content

Method: toString()

1: package de.fhdw.wtf.walker.tasks;
2:
3: import java.util.Collections;
4: import java.util.Iterator;
5: import java.util.LinkedList;
6: import java.util.List;
7:
8: import de.fhdw.wtf.common.ast.Attribute;
9: import de.fhdw.wtf.common.ast.ConstructorOrOperation;
10: import de.fhdw.wtf.common.ast.Group;
11: import de.fhdw.wtf.common.ast.Model;
12: import de.fhdw.wtf.common.ast.type.ClassType;
13: import de.fhdw.wtf.common.ast.type.Type;
14: import de.fhdw.wtf.common.exception.walker.TaskException;
15: import de.fhdw.wtf.common.task.TaskExecutor;
16: import de.fhdw.wtf.walker.walker.SimpleWalkerTask;
17:
18: /**
19: * Analyzes the inheritance trees of the given model. Enriches the model with useful information regarding necessary
20: * constructor calls.
21: */
22: public final class AnalyzeInheritanceTreesTask extends SimpleWalkerTask {
23:         
24:         /**
25:          * Used to identify the type Anything.
26:          */
27:         private static final String ANYTHING = "Anything";
28:         
29:         /**
30:          * Returns a new instance of {@link AnalyzeInheritanceTreesTask}.
31:          *
32:          * @param model
33:          * The model to perform this task on.
34:          * @param taskmanager
35:          * The executor which performs this task.
36:          * @return a new instance of {@link AnalyzeInheritanceTreesTask}
37:          */
38:         public static AnalyzeInheritanceTreesTask create(final Model model, final TaskExecutor taskmanager) {
39:                 return new AnalyzeInheritanceTreesTask(model, taskmanager);
40:         }
41:         
42:         /**
43:          * Instantiates a new instance of {@link AnalyzeInheritanceTreesTask}.
44:          *
45:          * @param model
46:          * The model to perform this task on.
47:          * @param taskmanager
48:          * The executor which performs this task.
49:          */
50:         private AnalyzeInheritanceTreesTask(final Model model, final TaskExecutor taskmanager) {
51:                 super(model, taskmanager);
52:         }
53:         
54:         /**
55:          * Helper class which represents one path along the inheritance-hierarchy of a root class.
56:          */
57:         private class InheritancePath {
58:                 /**
59:                  * Types ordered along this InheritancePath.
60:                  */
61:                 private final List<Type> pathElements;
62:                 
63:                 /**
64:                  * Constructs an empty InheritancePath.
65:                  */
66:                 private InheritancePath() {
67:                         this.pathElements = new LinkedList<>();
68:                 }
69:                 
70:                 /**
71:                  * Constructs an InheritancePath with first as its root.
72:                  *
73:                  * @param first
74:                  * the first element of this path.
75:                  */
76:                 InheritancePath(final Type first) {
77:                         this();
78:                         this.addElement(first);
79:                 }
80:                 
81:                 /**
82:                  * Constructs an InheritancePath with firsts as its path elements.
83:                  *
84:                  * @param firsts
85:                  * the path elements for this.
86:                  */
87:                 InheritancePath(final List<Type> firsts) {
88:                         this();
89:                         this.addElements(firsts);
90:                 }
91:                 
92:                 /**
93:                  * Constructs an InheritancePath which is identical to firsts.
94:                  *
95:                  * @param firsts
96:                  * the path to copy.
97:                  */
98:                 InheritancePath(final InheritancePath firsts) {
99:                         this(firsts.pathElements);
100:                 }
101:                 
102:                 /**
103:                  * Adds element to the end of this path.
104:                  *
105:                  * @param element
106:                  * the new end of this path.
107:                  */
108:                 void addElement(final Type element) {
109:                         this.pathElements.add(element);
110:                 }
111:                 
112:                 /**
113:                  * Adds elements to this path so that these elements remain in the order provided in elements.
114:                  *
115:                  * @param elements
116:                  * the elements added to this.
117:                  */
118:                 void addElements(final List<Type> elements) {
119:                         this.pathElements.addAll(elements);
120:                 }
121:                 
122:                 /**
123:                  * Returns true if element is also an element of this. False otherwise.
124:                  *
125:                  * @param element
126:                  * to check.
127:                  * @return true if element is part of this. False otherwise.
128:                  */
129:                 boolean contains(final Type element) {
130:                         return this.pathElements.contains(element);
131:                 }
132:                 
133:                 /**
134:                  * Returns all elements that this and other have in common. The common elements will be provided the order
135:                  * provided by this.
136:                  *
137:                  * @param other
138:                  * the path to intersect with.
139:                  * @return common elements in the order of their existence in this path.
140:                  */
141:                 InheritancePath intersect(final InheritancePath other) {
142:                         final InheritancePath result = new InheritancePath();
143:                         final Iterator<Type> myElements = this.pathElements.iterator();
144:                         while (myElements.hasNext()) {
145:                                 final Type myElement = myElements.next();
146:                                 if (other.contains(myElement)) {
147:                                         result.addElement(myElement);
148:                                 }
149:                         }
150:                         return result;
151:                 }
152:                 
153:                 /**
154:                  * Makes one step in the inheritance-tree. Alternative paths will be returned.
155:                  *
156:                  * @return alternative paths if any exist. An empty llist otherwise.
157:                  */
158:                 List<InheritancePath> step() {
159:                         final List<InheritancePath> alternativePaths = new LinkedList<>();
160:                         final Type lastElement = this.pathElements.get(this.pathElements.size() - 1);
161:                         final Iterator<Type> followers = lastElement.getSuperTypes().iterator();
162:                         if (followers.hasNext()) {
163:                                 final Type firstFollower = followers.next();
164:                                 while (followers.hasNext()) {
165:                                         final Type follower = followers.next();
166:                                         if (!follower.toString().equals(ANYTHING)) {
167:                                                 final InheritancePath alternativePath = new InheritancePath(this);
168:                                                 alternativePath.addElement(follower);
169:                                                 alternativePaths.add(alternativePath);
170:                                         }
171:                                 }
172:                                 if (!firstFollower.toString().equals(ANYTHING)) {
173:                                         this.addElement(firstFollower);
174:                                 }
175:                         }
176:                         return alternativePaths;
177:                 }
178:                 
179:                 /**
180:                  * Calculates all inheritance paths reachable from the end of this path and returns them.
181:                  *
182:                  * @return a list with all paths reachable from the last element of this.
183:                  */
184:                 List<InheritancePath> run() {
185:                         final List<InheritancePath> result = new LinkedList<>();
186:                         final List<InheritancePath> preResult = new LinkedList<>();
187:                         int oldSize = this.pathElements.size();
188:                         List<InheritancePath> alternativePaths = this.step();
189:                         while (this.pathElements.size() != oldSize || !alternativePaths.isEmpty()) {
190:                                 preResult.addAll(alternativePaths);
191:                                 oldSize = this.pathElements.size();
192:                                 alternativePaths = this.step();
193:                         }
194:                         final Iterator<InheritancePath> paths = preResult.iterator();
195:                         result.add(this);
196:                         while (paths.hasNext()) {
197:                                 result.addAll(paths.next().run());
198:                         }
199:                         return result;
200:                 }
201:                 
202:                 @Override
203:                 public String toString() {
204:                         return this.pathElements.toString();
205:                 }
206:                 
207:                 @Override
208:                 public boolean equals(final Object o) {
209:                         if (o instanceof InheritancePath) {
210:                                 final InheritancePath oAsInheritancePath = (InheritancePath) o;
211:                                 return this.pathElements.equals(oAsInheritancePath.pathElements);
212:                         }
213:                         return false;
214:                 }
215:                 
216:                 @Override
217:                 public int hashCode() {
218:                         return this.pathElements.hashCode();
219:                 }
220:                 
221:                 /**
222:                  *
223:                  * @return true if this path is empty.
224:                  */
225:                 boolean isEmpty() {
226:                         return this.pathElements.isEmpty();
227:                 }
228:                 
229:                 /**
230:                  * removes top from this path if top is the top of this path.
231:                  *
232:                  * @param top
233:                  * the top to remove.
234:                  *
235:                  */
236:                 void removeTop(final Type top) {
237:                         final int highestIndex = this.pathElements.size() - 1;
238:                         if (highestIndex >= 0 && this.pathElements.get(highestIndex).equals(top)) {
239:                                 this.pathElements.remove(highestIndex);
240:                         }
241:                 }
242:                 
243:         }
244:         
245:         @Override
246:         public void handleClass(final ClassType c) throws TaskException {
247:                 final List<InheritancePath> allPaths = new LinkedList<>();
248:                 final Iterator<Type> superTypes = c.getSuperTypes().iterator();
249:                 while (superTypes.hasNext()) {
250:                         final Type currentSuperType = superTypes.next();
251:                         if (!currentSuperType.toString().equals(ANYTHING)) {
252:                                 allPaths.addAll(new InheritancePath(currentSuperType).run());
253:                         }
254:                 }
255:                 final List<Type> definitiveResponsibilities = new LinkedList<>();
256:                 final List<InheritancePath> allIntersections =
257:                                 this.gatherAllRelevantIntersections(allPaths, definitiveResponsibilities);
258:                 this.mapCalls(c, allIntersections, definitiveResponsibilities);
259:         }
260:         
261:         /**
262:          * First step to get all call responsibilities. Fills definitiveResponsibilities with the value found in the first
263:          * intersection of none open ended paths where the intersection is of length 1.
264:          *
265:          * @param allPaths
266:          * all inheritance paths.
267:          * @param definitiveResponsibilities
268:          * a collection with all definitive call responsibilities.
269:          * @return a list with resulting intersection.
270:          */
271:         private List<InheritancePath> gatherAllRelevantIntersections(final List<InheritancePath> allPaths,
272:                         final List<Type> definitiveResponsibilities) {
273:                 if (allPaths.size() <= 1) {
274:                         return allPaths;
275:                 }
276:                 final List<InheritancePath> allIntersections = new LinkedList<>();
277:                 final List<InheritancePath> copy = new LinkedList<>();
278:                 copy.addAll(allPaths);
279:                 Collections.reverse(copy);
280:                 boolean firstSingleOne = true;
281:                 boolean firstRound = true;
282:                 boolean firstAllLooseEnds = true;
283:                 final Iterator<InheritancePath> paths = allPaths.iterator();
284:                 while (paths.hasNext()) {
285:                         final InheritancePath currentPath = paths.next();
286:                         final Type pathEnd = currentPath.pathElements.get(currentPath.pathElements.size() - 1);
287:                         final List<InheritancePath> currentIntersections = new LinkedList<>();
288:                         boolean commonEnd = false;
289:                         final Iterator<InheritancePath> copyPaths = copy.iterator();
290:                         while (copyPaths.hasNext()) {
291:                                 final InheritancePath currentCopy = copyPaths.next();
292:                                 final InheritancePath intersection = currentPath.intersect(currentCopy);
293:                                 if (!intersection.isEmpty()) {
294:                                         currentIntersections.add(intersection);
295:                                 }
296:                                 if (!currentPath.equals(currentCopy)) {
297:                                         commonEnd =
298:                                                         commonEnd
299:                                                                         || pathEnd
300:                                                                                         .equals(currentCopy.pathElements.get(currentCopy.pathElements.size() - 1));
301:                                         firstAllLooseEnds = firstAllLooseEnds && firstRound && !commonEnd;
302:                                 }
303:                         }
304:                         if (commonEnd) {
305:                                 if (firstSingleOne) {
306:                                         firstSingleOne =
307:                                                         this.gatherSingleOnes(currentPath, currentIntersections, definitiveResponsibilities);
308:                                 }
309:                                 allIntersections.addAll(currentIntersections);
310:                         } else {
311:                                 if (firstAllLooseEnds && currentPath.pathElements.size() == 1) {
312:                                         final Type type = currentPath.pathElements.get(0);
313:                                         if (!definitiveResponsibilities.contains(type)) {
314:                                                 definitiveResponsibilities.add(type);
315:                                         }
316:                                 } else {
317:                                         allIntersections.addAll(currentIntersections);
318:                                 }
319:                         }
320:                         firstRound = firstAllLooseEnds;
321:                 }
322:                 
323:                 return allIntersections;
324:         }
325:         
326:         /**
327:          * Fills definitiveResponsibilities with the value found in the first intersection of none open ended paths where
328:          * the intersection is of length 1.
329:          *
330:          * @param currentPath
331:          * the current path.
332:          * @param currentIntersections
333:          * the current intersections.
334:          * @param definitiveResponsibilities
335:          * a list with definitive responsibilities.
336:          * @return true if definitiveResponsibilities has been changed.
337:          */
338:         private boolean gatherSingleOnes(final InheritancePath currentPath,
339:                         final List<InheritancePath> currentIntersections,
340:                         final List<Type> definitiveResponsibilities) {
341:                 final Iterator<InheritancePath> intersections = currentIntersections.iterator();
342:                 final Type first = currentPath.pathElements.get(0);
343:                 boolean firstOne = false;
344:                 while (intersections.hasNext()) {
345:                         final InheritancePath current = intersections.next();
346:                         if (current.pathElements.size() == 1) {
347:                                 final Type type = current.pathElements.get(0);
348:                                 firstOne = !type.equals(first) && !definitiveResponsibilities.contains(type);
349:                                 if (firstOne) {
350:                                         definitiveResponsibilities.add(type);
351:                                 }
352:                         }
353:                 }
354:                 return firstOne;
355:         }
356:         
357:         /**
358:          * Maps c to all types it has to instantiate during runtime. Uses the map "constructorCalldependencies" in Model for
359:          * this purpose. Maps to an empty list if c has no responsibilities in the regard of instantiating super types.
360:          *
361:          * @param c
362:          * the root of the inheritance tree to map types from.
363:          * @param allIntersections
364:          * The inheritance tree with c as root in form of all paths that have the direct super types of c as root
365:          * after running gatherAllRelevantIntersections.
366:          * @param definitiveResponsibilities
367:          * a list with definitive responsibilities.
368:          */
369:         private void mapCalls(final ClassType c,
370:                         final List<InheritancePath> allIntersections,
371:                         final List<Type> definitiveResponsibilities) {
372:                 List<InheritancePath> copyNewIntersections = new LinkedList<>();
373:                 copyNewIntersections.addAll(allIntersections);
374:                 while (!copyNewIntersections.isEmpty()) {
375:                         final List<InheritancePath> newIntersections = new LinkedList<>();
376:                         final Iterator<InheritancePath> intersections = copyNewIntersections.iterator();
377:                         while (intersections.hasNext()) {
378:                                 final InheritancePath path = intersections.next();
379:                                 if (path.isEmpty()) {
380:                                         intersections.remove();
381:                                 } else if (path.pathElements.size() == 1) {
382:                                         final Type type = path.pathElements.get(0);
383:                                         if (!definitiveResponsibilities.contains(type)) {
384:                                                 definitiveResponsibilities.add(type);
385:                                         }
386:                                         final List<InheritancePath> copyList = new LinkedList<>();
387:                                         copyList.addAll(copyNewIntersections);
388:                                         final Iterator<InheritancePath> copy = copyList.iterator();
389:                                         while (copy.hasNext()) {
390:                                                 copy.next().removeTop(type);
391:                                         }
392:                                         // intersections.remove();
393:                                 } else {
394:                                         final Type top = path.pathElements.get(path.pathElements.size() - 1);
395:                                         final List<InheritancePath> copyList = new LinkedList<>();
396:                                         copyList.addAll(copyNewIntersections);
397:                                         final Iterator<InheritancePath> copies = copyList.iterator();
398:                                         while (copies.hasNext()) {
399:                                                 final InheritancePath copy = copies.next();
400:                                                 final InheritancePath intersection = path.intersect(copy);
401:                                                 copy.removeTop(top);
402:                                                 if (!intersection.isEmpty()) {
403:                                                         if (intersection.pathElements.size() == 1) {
404:                                                                 final Type type = path.pathElements.get(0);
405:                                                                 if (!definitiveResponsibilities.contains(type)) {
406:                                                                         definitiveResponsibilities.add(type);
407:                                                                 }
408:                                                         } else {
409:                                                                 intersection.removeTop(top);
410:                                                                 newIntersections.add(intersection);
411:                                                         }
412:                                                 }
413:                                         }
414:                                 }
415:                         }
416:                         copyNewIntersections = new LinkedList<>();
417:                         copyNewIntersections.addAll(newIntersections);
418:                 }
419:                 this.getModel().putConstructorCallDependency(c, definitiveResponsibilities);
420:         }
421:         
422:         @Override
423:         public void handleGroup(final Group g) throws TaskException {
424:                 // Nothing to do.
425:         }
426:         
427:         @Override
428:         public void handleAttribute(final Attribute a, final ClassType owner) throws TaskException {
429:                 // Nothing to do.
430:         }
431:         
432:         @Override
433:         public void handleConstructorOrOperation(final ConstructorOrOperation coo, final ClassType owner)
434:                         throws TaskException {
435:                 // Nothing to do.
436:         }
437:         
438:         @Override
439:         public void finalizeTask() throws TaskException {
440:                 // Nothing to do.
441:         }
442:         
443:         @Override
444:         public void beginTask() throws TaskException {
445:                 // Nothing to do.
446:         }
447:         
448: }