Skip to content

Method: handleConstructor(Constructor)

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 (this.called.isSupertypeOf(sum)) {
286:                                 final List<Type> newToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(this.caller);
287:                                 final int index = newToCall.indexOf(sum);
288:                                 newToCall.remove(sum);
289:                                 newToCall.add(index, this.called);
290:                                 ReferencedConstructorsCheck.this.getModel().putConstructorCallDependency(this.caller, newToCall);
291:                                 return true;
292:                         }
293:                         if (elements.contains(this.caller)) {
294:                                 final List<Type> newToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(this.caller);
295:                                 newToCall.remove(sum);
296:                                 ReferencedConstructorsCheck.this.getModel().putConstructorCallDependency(this.caller, newToCall);
297:                                 return true;
298:                         }
299:                         final Iterator<Type> parts = elements.iterator();
300:                         boolean found = false;
301:                         while (!found && parts.hasNext()) {
302:                                 found = parts.next().accept(new TypeChecker(this.caller, this.called, this.typesToCall));
303:                         }
304:                         
305:                         final Iterator<Type> superParts = this.typesToCall.iterator();
306:                         while (!found && superParts.hasNext()) {
307:                                 found = superParts.next().isSupertypeOf(sum);
308:                         }
309:                         if (found) {
310:                                 final List<Type> newToCall = ReferencedConstructorsCheck.this.necessaryCalls.get(this.caller);
311:                                 final int index = newToCall.indexOf(sum);
312:                                 newToCall.remove(index);
313:                                 if (!newToCall.contains(this.called)) {
314:                                         newToCall.add(index, this.called);
315:                                 }
316:                                 ReferencedConstructorsCheck.this.getModel().putConstructorCallDependency(this.caller, newToCall);
317:                         }
318:                         return found;
319:                 }
320:                 
321:                 @Override
322:                 public Boolean handle(final ThrownType thrownType) {
323:                         return this.typesToCall.contains(thrownType);
324:                 }
325:                 
326:         }
327:         
328:         /**
329:          * returns true if typesTocall contains type.
330:          *
331:          * @param caller
332:          * caller
333:          * @param type
334:          * a type
335:          * @param typesToCall
336:          * typesToCall
337:          * @return true if typesTocall contains atomicType. false otherwise.
338:          */
339:         public boolean contains(final Type caller, final Type type, final Collection<Type> typesToCall) {
340:                 final Iterator<Type> types = typesToCall.iterator();
341:                 boolean found = false;
342:                 while (!found && types.hasNext()) {
343:                         found = types.next().accept(new TypeVisitorReturn<Boolean>() {
344:                                 
345:                                 @Override
346:                                 public Boolean handle(final AtomicType atomicType2) {
347:                                         return atomicType2.equals(type);
348:                                 }
349:                                 
350:                                 @Override
351:                                 public Boolean handle(final CompositeType compositeType) {
352:                                         final Collection<Type> typesToCall2 = new LinkedList<>();
353:                                         typesToCall2.add(type);
354:                                         return compositeType.accept(new CompositeTypeChecker(caller, type, typesToCall2));
355:                                 }
356:                                 
357:                                 @Override
358:                                 public Boolean handle(final TypeProxy typeProxy) {
359:                                         return typeProxy.getTarget().equals(type);
360:                                 }
361:                                 
362:                         });
363:                 }
364:                 return found;
365:         }
366:         
367:         /**
368:          * Returns the type referenced in state.
369:          *
370:          * @param state
371:          * the state to get the type from.
372:          * @return the referenced type.
373:          * @throws TaskException
374:          * if the state is not a byReference state.
375:          */
376:         protected Type getReferencedType(final ConstructorReferenceState state) throws TaskException {
377:                 return state.accept(new ConstructorReferenceStateVisitorReturnException<Type, TaskException>() {
378:                         
379:                         @Override
380:                         public Type handle(final ConstructorByTypeAndSignatureState byName) throws TaskException {
381:                                 throw new TaskException("ByName states of ConstructorReferenceStates are not allowed here!");
382:                         }
383:                         
384:                         @Override
385:                         public Type handle(final ConstructorInvalidState invalid) throws TaskException {
386:                                 throw new TaskException("Invalid states of ConstructorReferenceStates are not allowed here!");
387:                         }
388:                         
389:                         @Override
390:                         public Type handle(final ConstructorByReferenceState byReference) throws TaskException {
391:                                 return byReference.getType();
392:                         }
393:                         
394:                 });
395:         }
396:         
397:         @Override
398:         public void finalizeTask() throws TaskException {
399:                 // Nothing to do.
400:         }
401:         
402:         @Override
403:         public void beginTask() throws TaskException {
404:                 // Nothing to do.
405:         }
406:         
407: }