Skip to content

Method: {...}

1: package de.fhdw.wtf.walker.tasks;
2:
3: import java.util.Collection;
4: import java.util.Iterator;
5: import java.util.LinkedList;
6: import java.util.List;
7: import java.util.Map;
8:
9: import de.fhdw.wtf.common.ast.Attribute;
10: import de.fhdw.wtf.common.ast.Constructor;
11: import de.fhdw.wtf.common.ast.ConstructorByReferenceState;
12: import de.fhdw.wtf.common.ast.ConstructorByTypeAndSignatureState;
13: import de.fhdw.wtf.common.ast.ConstructorInvalidState;
14: import de.fhdw.wtf.common.ast.ConstructorOrOperation;
15: import de.fhdw.wtf.common.ast.ConstructorReference;
16: import de.fhdw.wtf.common.ast.ConstructorReferenceState;
17: import de.fhdw.wtf.common.ast.Group;
18: import de.fhdw.wtf.common.ast.Model;
19: import de.fhdw.wtf.common.ast.Operation;
20: import de.fhdw.wtf.common.ast.type.AtomicType;
21: import de.fhdw.wtf.common.ast.type.ClassType;
22: import de.fhdw.wtf.common.ast.type.CompositeType;
23: import de.fhdw.wtf.common.ast.type.ListType;
24: import de.fhdw.wtf.common.ast.type.MapType;
25: import de.fhdw.wtf.common.ast.type.ProductType;
26: import de.fhdw.wtf.common.ast.type.SumType;
27: import de.fhdw.wtf.common.ast.type.ThrownType;
28: import de.fhdw.wtf.common.ast.type.Type;
29: import de.fhdw.wtf.common.ast.type.TypeProxy;
30: import de.fhdw.wtf.common.ast.visitor.CompositeTypeVisitorReturn;
31: import de.fhdw.wtf.common.ast.visitor.ConstructorOrOperationExceptionVisitor;
32: import de.fhdw.wtf.common.ast.visitor.ConstructorReferenceStateVisitorReturnException;
33: import de.fhdw.wtf.common.ast.visitor.TypeVisitorReturn;
34: import de.fhdw.wtf.common.exception.walker.InvalidConstructorReferenceListException;
35: import de.fhdw.wtf.common.exception.walker.TaskException;
36: import de.fhdw.wtf.common.task.TaskExecutor;
37: import de.fhdw.wtf.walker.walker.SimpleWalkerTask;
38:
39: /**
40: * Checks if the list of super constructors in constructors apply to the rules for constructors. Should not run before
41: * {@link ConstructorReferencer}.
42: */
43: public final class ReferencedConstructorsCheck extends SimpleWalkerTask {
44:         
45:         /**
46:          * The instantiations classes must do.
47:          */
48:         private final Map<Type, List<Type>> necessaryCalls;
49:         
50:         /**
51:          * Returns a new instance of {@link ReferencedConstructorsCheck}.
52:          *
53:          * @param model
54:          * The model to check.
55:          * @param taskmanager
56:          * The executor which performs this task.
57:          * @return a new instance of {@link ReferencedConstructorsCheck}
58:          */
59:         public static ReferencedConstructorsCheck create(final Model model, final TaskExecutor taskmanager) {
60:                 return new ReferencedConstructorsCheck(model, taskmanager);
61:         }
62:         
63:         /**
64:          * Instantiates a new instance of {@link ReferencedConstructorsCheck}.
65:          *
66:          * @param model
67:          * The model to check.
68:          * @param taskmanager
69:          * The executor which performs this task.
70:          */
71:         private ReferencedConstructorsCheck(final Model model, final TaskExecutor taskmanager) {
72:                 super(model, taskmanager);
73:                 this.necessaryCalls = model.getConstructorCallDependencies();
74:         }
75:         
76:         @Override
77:         public void handleClass(final ClassType c) throws TaskException {
78:                 // Nothing to do.
79:         }
80:         
81:         @Override
82:         public void handleGroup(final Group g) throws TaskException {
83:                 // Nothing to do.
84:         }
85:         
86:         @Override
87:         public void handleAttribute(final Attribute a, final ClassType owner) throws TaskException {
88:                 // Nothing to do.
89:         }
90:         
91:         @Override
92:         public void handleConstructorOrOperation(final ConstructorOrOperation coo, final ClassType owner)
93:                         throws TaskException {
94:                 coo.accept(new ConstructorOrOperationExceptionVisitor<TaskException>() {
95:                         
96:                         @Override
97:                         public void handleConstructor(final Constructor constructor) throws TaskException {
98:                                 final Type caller = constructor.getReturnType();
99:                                 final List<Type> typesToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(caller);
100:                                 final Collection<Type> calledSuperTypes = new LinkedList<>();
101:                                 final Iterator<ConstructorReference> references = constructor.getSuperConstructors().iterator();
102:                                 while (references.hasNext()) {
103:                                         final ConstructorReference reference = references.next();
104:                                         calledSuperTypes.add(ReferencedConstructorsCheck.this.getReferencedType(reference.getState()));
105:                                 }
106:                                 if (!ReferencedConstructorsCheck.this.checkIfLegitimeSuperTypeList(
107:                                                 caller,
108:                                                 calledSuperTypes,
109:                                                 typesToCall)) {
110:                                         if (calledSuperTypes.size() > typesToCall.size()) {
111:                                                 throw InvalidConstructorReferenceListException.create(
112:                                                                 constructor,
113:                                                                 "Too many super type constructors are being referenced,"
114:                                                                                 + " only constructors of the following types have to be referenced: "
115:                                                                                 + typesToCall + "\nCalling constructor: ");
116:                                         }
117:                                         if (typesToCall.size() > calledSuperTypes.size()) {
118:                                                 throw InvalidConstructorReferenceListException.create(
119:                                                                 constructor,
120:                                                                 "Missing super constructor references!\n"
121:                                                                                 + "The calling constructor has to call constructors of the following types: "
122:                                                                                 + typesToCall + "\nCalling constructor: ");
123:                                         }
124:                                         
125:                                 }
126:                                 
127:                         }
128:                         
129:                         @Override
130:                         public void handleOperation(final Operation operation) throws TaskException {
131:                                 // Nothing to do.
132:                         }
133:                         
134:                 });
135:                 
136:         }
137:         
138:         /**
139:          * Checks if the two collections contain each over mutually.
140:          *
141:          * @param caller
142:          * the caller.
143:          * @param calledSuperTypes
144:          * one collection.
145:          * @param typesToCall
146:          * one collection.
147:          * @return true if they contain each other. Otherwise false.
148:          */
149:         protected boolean checkIfLegitimeSuperTypeList(final Type caller,
150:                         final Collection<Type> calledSuperTypes,
151:                         final Collection<Type> typesToCall) {
152:                 return this.checkOneWay(caller, calledSuperTypes, typesToCall)
153:                                 && this.checkOneWay(caller, typesToCall, calledSuperTypes)
154:                                 && calledSuperTypes.size() == typesToCall.size();
155:         }
156:         
157:         /**
158:          * Checks if all elements in left are contained in right.
159:          *
160:          * @param caller
161:          * the caller.
162:          *
163:          * @param left
164:          * a collection.
165:          * @param right
166:          * a collection.
167:          * @return true if right contains all elements of right.
168:          */
169:         private boolean checkOneWay(final Type caller, final Collection<Type> left, final Collection<Type> right) {
170:                 final Iterator<Type> leftIterator = left.iterator();
171:                 boolean legitimate = true;
172:                 while (legitimate && leftIterator.hasNext()) {
173:                         final Type currentCalled = leftIterator.next();
174:                         legitimate = currentCalled.accept(new TypeChecker(caller, currentCalled, right));
175:                 }
176:                 return legitimate;
177:         }
178:         
179:         /**
180:          * Checks if a type is part of a given list of types.
181:          */
182:         class TypeChecker implements TypeVisitorReturn<Boolean> {
183:                 
184:                 /**
185:                  * Calling type.
186:                  */
187:                 private final Type caller;
188:                 
189:                 /**
190:                  * Called type.
191:                  */
192:                 private final Type called;
193:                 /**
194:                  * types to call.
195:                  */
196:                 private final Collection<Type> typesToCall;
197:                 
198:                 /**
199:                  * Returns a new TypeChecker.
200:                  *
201:                  * @param caller
202:                  * the caller.
203:                  * @param called
204:                  * called.
205:                  *
206:                  * @param typesToCall
207:                  * the list to check against.
208:                  */
209:                 TypeChecker(final Type caller, final Type called, final Collection<Type> typesToCall) {
210:                         this.caller = caller;
211:                         this.called = called;
212:                         this.typesToCall = typesToCall;
213:                 }
214:                 
215:                 @Override
216:                 public Boolean handle(final AtomicType atomicType) {
217:                         return ReferencedConstructorsCheck.this.contains(this.caller, atomicType, this.typesToCall);
218:                 }
219:                 
220:                 @Override
221:                 public Boolean handle(final CompositeType compositeType) {
222:                         return compositeType.accept(new CompositeTypeChecker(this.caller, this.called, this.typesToCall));
223:                 }
224:                 
225:                 @Override
226:                 public Boolean handle(final TypeProxy typeProxy) {
227:                         return typeProxy.getTarget().accept(this);
228:                 }
229:                 
230:         }
231:         
232:         /**
233:          * Checks if a part of a sum type is part of a given list of types.
234:          */
235:         class CompositeTypeChecker implements CompositeTypeVisitorReturn<Boolean> {
236:                 
237:                 /**
238:                  * Calling type.
239:                  */
240:                 private final Type caller;
241:                 /**
242:                  * Called type.
243:                  */
244:                 private final Type called;
245:                 /**
246:                  * types to call.
247:                  */
248:                 private final Collection<Type> typesToCall;
249:                 
250:                 /**
251:                  * Returns a new CompositeTypeChecker.
252:                  *
253:                  * @param caller
254:                  * the caller.
255:                  * @param called
256:                  * called.
257:                  *
258:                  * @param typesToCall
259:                  * the list to check against.
260:                  */
261:                 CompositeTypeChecker(final Type caller, final Type called, final Collection<Type> typesToCall) {
262:                         this.caller = caller;
263:                         this.called = called;
264:                         this.typesToCall = typesToCall;
265:                 }
266:                 
267:                 @Override
268:                 public Boolean handle(final ListType list) {
269:                         return list.getOf().accept(new TypeChecker(this.caller, this.called, this.typesToCall));
270:                 }
271:                 
272:                 @Override
273:                 public Boolean handle(final MapType map) {
274:                         return map.getOf().accept(new TypeChecker(this.caller, this.called, this.typesToCall));
275:                 }
276:                 
277:                 @Override
278:                 public Boolean handle(final ProductType product) {
279:                         return this.typesToCall.contains(product);
280:                 }
281:                 
282:                 @Override
283:                 public Boolean handle(final SumType sum) {
284:                         final Collection<Type> elements = sum.getElements();
285:                         if (elements.contains(this.caller)) {
286:                                 final List<Type> newToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(this.caller);
287:                                 newToCall.remove(sum);
288:                                 ReferencedConstructorsCheck.this.getModel().putConstructorCallDependency(this.caller, newToCall);
289:                                 return true;
290:                         }
291:                         final Iterator<Type> parts = elements.iterator();
292:                         boolean found = false;
293:                         while (!found && parts.hasNext()) {
294:                                 found = parts.next().accept(new TypeChecker(this.caller, this.called, this.typesToCall));
295:                         }
296:                         
297:                         final Iterator<Type> superParts = this.typesToCall.iterator();
298:                         while (!found && superParts.hasNext()) {
299:                                 found = superParts.next().isSupertypeOf(sum);
300:                         }
301:                         if (found) {
302:                                 final List<Type> newToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(this.caller);
303:                                 final int index = newToCall.indexOf(sum);
304:                                 newToCall.remove(index);
305:                                 newToCall.add(index, this.called);
306:                                 ReferencedConstructorsCheck.this.getModel().putConstructorCallDependency(this.caller, newToCall);
307:                         }
308:                         return found;
309:                 }
310:                 
311:                 @Override
312:                 public Boolean handle(final ThrownType thrownType) {
313:                         return this.typesToCall.contains(thrownType);
314:                 }
315:                 
316:         }
317:         
318:         /**
319:          * returns true if typesTocall contains atomicType.
320:          *
321:          * @param caller
322:          * caller
323:          * @param atomicType
324:          * atomicType
325:          * @param typesToCall
326:          * typesToCall
327:          * @return true if typesTocall contains atomicType. false otherwise.
328:          */
329:         public boolean contains(final Type caller, final AtomicType atomicType, final Collection<Type> typesToCall) {
330:                 final Iterator<Type> types = typesToCall.iterator();
331:                 boolean found = false;
332:                 while (!found && types.hasNext()) {
333:                         found = types.next().accept(new TypeVisitorReturn<Boolean>() {
334:                                 
335:                                 @Override
336:                                 public Boolean handle(final AtomicType atomicType2) {
337:                                         return atomicType2.equals(atomicType);
338:                                 }
339:                                 
340:                                 @Override
341:                                 public Boolean handle(final CompositeType compositeType) {
342:                                         final Collection<Type> typesToCall2 = new LinkedList<>();
343:                                         typesToCall2.add(atomicType);
344:                                         return compositeType.accept(new CompositeTypeChecker(caller, atomicType, typesToCall2));
345:                                 }
346:                                 
347:                                 @Override
348:                                 public Boolean handle(final TypeProxy typeProxy) {
349:                                         return typeProxy.getTarget().equals(atomicType);
350:                                 }
351:                                 
352:                         });
353:                 }
354:                 return found;
355:         }
356:         
357:         /**
358:          * Returns the type referenced in state.
359:          *
360:          * @param state
361:          * the state to get the type from.
362:          * @return the referenced type.
363:          * @throws TaskException
364:          * if the state is not a byReference state.
365:          */
366:         protected Type getReferencedType(final ConstructorReferenceState state) throws TaskException {
367:                 return state.accept(new ConstructorReferenceStateVisitorReturnException<Type, TaskException>() {
368:                         
369:                         @Override
370:                         public Type handle(final ConstructorByTypeAndSignatureState byName) throws TaskException {
371:                                 throw new TaskException("ByName states of ConstructorReferenceStates are not allowed here!");
372:                         }
373:                         
374:                         @Override
375:                         public Type handle(final ConstructorInvalidState invalid) throws TaskException {
376:                                 throw new TaskException("Invalid states of ConstructorReferenceStates are not allowed here!");
377:                         }
378:                         
379:                         @Override
380:                         public Type handle(final ConstructorByReferenceState byReference) throws TaskException {
381:                                 return byReference.getType();
382:                         }
383:                         
384:                 });
385:         }
386:         
387:         @Override
388:         public void finalizeTask() throws TaskException {
389:                 // Nothing to do.
390:         }
391:         
392:         @Override
393:         public void beginTask() throws TaskException {
394:                 // Nothing to do.
395:         }
396:         
397: }