Skip to content

Method: handleOperation(Operation)

1: package de.fhdw.wtf.walker.tasks;
2:
3: import java.util.Collection;
4: import java.util.Iterator;
5: import java.util.LinkedList;
6:
7: import de.fhdw.wtf.common.ast.Attribute;
8: import de.fhdw.wtf.common.ast.Constructor;
9: import de.fhdw.wtf.common.ast.ConstructorByReferenceState;
10: import de.fhdw.wtf.common.ast.ConstructorByTypeAndSignatureState;
11: import de.fhdw.wtf.common.ast.ConstructorInvalidState;
12: import de.fhdw.wtf.common.ast.ConstructorOrOperation;
13: import de.fhdw.wtf.common.ast.ConstructorReference;
14: import de.fhdw.wtf.common.ast.Group;
15: import de.fhdw.wtf.common.ast.Model;
16: import de.fhdw.wtf.common.ast.Operation;
17: import de.fhdw.wtf.common.ast.Tuple;
18: import de.fhdw.wtf.common.ast.type.ClassType;
19: import de.fhdw.wtf.common.ast.visitor.ConstructorOrOperationExceptionVisitor;
20: import de.fhdw.wtf.common.ast.visitor.ConstructorReferenceStateVisitorReturn;
21: import de.fhdw.wtf.common.exception.referencer.ConstructorReferenceMismatchException;
22: import de.fhdw.wtf.common.exception.referencer.ConstructorReferencerExceptions;
23: import de.fhdw.wtf.common.exception.referencer.CyclicConstructorCallException;
24: import de.fhdw.wtf.common.exception.referencer.DuplicateConstructorException;
25: import de.fhdw.wtf.common.exception.walker.TaskException;
26: import de.fhdw.wtf.common.task.TaskExecutor;
27:
28: /**
29: * This referencer tries to find the real {@link Constructor} instances that are referenced by the
30: * {@link ConstructorReference} instances. The found {@link ConstructorReference} instances in the superConstructor
31: * collections of all defined {@link Constructor} instances are being replaced by matching {@link Constructor}
32: * instances. This task shall not be performed before the task {@link TypeReferencer}.
33: *
34: */
35: public class ConstructorReferencer extends Referencer {
36:         
37:         /**
38:          * The minimum of {@link Constructor} instances that are searched to replace a {@link ConstructorReference}.
39:          *
40:          */
41:         private static final int MINIMUM_OF_CONSTRUCTOR_INSTANCES_NEEDED_TO_MATCH_A_REFERENCE = 1;
42:         
43:         /**
44:          * The maximum of {@link Constructor} instances that are searched to replace a {@link ConstructorReference}. The
45:          * transgression of that value can be seen as exceptional behavior (Wrong wtf-model).
46:          */
47:         private static final int MAXIMUM_OF_CONSTRUCTOR_INSTANCES_ALLOWED_TO_MATCH_A_REFERENCE = 1;
48:         
49:         /**
50:          * COntains all {@link ClassType} instances that contain at least one {@link Constructor} instance (which should be
51:          * all {@link ClassType} instances).
52:          */
53:         private final Collection<ClassType> constructorOwners;
54:         /**
55:          * Contains tuples of {@link ConstructorReference} instances and the {@link Constructor} instances which contain
56:          * them.
57:          */
58:         private final Collection<Tuple<ConstructorReference, Constructor>> constructorReferences;
59:         /**
60:          * Contains all exception that might appear when this task is being performed. This field should be read to
61:          * determine whether an exception happened or not.
62:          */
63:         private final Collection<TaskException> exceptions;
64:         
65:         /**
66:          * Instantiates a new {@link ConstructorReferencer}.
67:          *
68:          * @param model
69:          * the model with the {@link ConstructorReference} instances to replace.
70:          * @param taskmanager
71:          * the manager which performs this task.
72:          */
73:         public ConstructorReferencer(final Model model, final TaskExecutor taskmanager) {
74:                 super(model, taskmanager);
75:                 this.constructorOwners = new LinkedList<>();
76:                 this.constructorReferences = new LinkedList<>();
77:                 this.exceptions = new LinkedList<>();
78:         }
79:         
80:         /**
81:          * Creates a {@link ConstructorReferencer}-Object.
82:          *
83:          * @param inputModel
84:          * Model
85:          * @param taskmanager
86:          * Taskmanager
87:          * @return The Referencer-Object
88:          */
89:         public static ConstructorReferencer create(final Model inputModel, final TaskExecutor taskmanager) {
90:                 return new ConstructorReferencer(inputModel, taskmanager);
91:         }
92:         
93:         @Override
94:         public void handleClass(final ClassType c) throws TaskException {
95:                 if (c.getConstructors().isEmpty()) {
96:                         this.exceptions.add(new TaskException("No constructor for class " + c.getTypeName()
97:                                         + " has bee defined!\nPlease define a constructor explicitely."));
98:                 }
99:                 this.constructorOwners.add(c);
100:         }
101:         
102:         @Override
103:         public void handleGroup(final Group g) throws TaskException {
104:                 // Nothing to do.
105:         }
106:         
107:         @Override
108:         public void handleAttribute(final Attribute a, final ClassType owner) throws TaskException {
109:                 // Nothing to do.
110:         }
111:         
112:         @Override
113:         public void handleConstructorOrOperation(final ConstructorOrOperation coo, final ClassType owner)
114:                         throws TaskException {
115:                 coo.accept(new ConstructorOrOperationExceptionVisitor<TaskException>() {
116:                         
117:                         @Override
118:                         public void handleOperation(final Operation operation) throws TaskException {
119:                                 // Nothing to do.
120:                                 
121:                         }
122:                         
123:                         @Override
124:                         public void handleConstructor(final Constructor constructor) throws TaskException {
125:                                 final Iterator<ConstructorReference> referencedConstructors =
126:                                                 constructor.getSuperConstructors().iterator();
127:                                 while (referencedConstructors.hasNext()) {
128:                                         final ConstructorReference referencedConstructor = referencedConstructors.next();
129:                                         ConstructorReferencer.this.constructorReferences
130:                                                         .add(new Tuple<>(referencedConstructor, constructor));
131:                                 }
132:                                 
133:                         }
134:                 });
135:                 
136:         }
137:         
138:         @Override
139:         public void finalizeTask() throws TaskException {
140:                 final Iterator<Tuple<ConstructorReference, Constructor>> references = this.constructorReferences.iterator();
141:                 while (references.hasNext()) {
142:                         final Tuple<ConstructorReference, Constructor> currentReferenceTuple = references.next();
143:                         final ConstructorReference currentReference = currentReferenceTuple.getFirst();
144:                         final Constructor callingConstructor = currentReferenceTuple.getSecond();
145:                         final Iterator<ClassType> constructorOwnersIterator = this.constructorOwners.iterator();
146:                         boolean matchFound = false;
147:                         while (!matchFound && constructorOwnersIterator.hasNext()) {
148:                                 final ClassType currentConstructorOwner = constructorOwnersIterator.next();
149:                                 matchFound =
150:                                                 this.replaceReferenceWithRealConstructorIfMatchFound(
151:                                                                 currentConstructorOwner,
152:                                                                 callingConstructor,
153:                                                                 currentReference);
154:                         }
155:                         if (!matchFound) {
156:                                 this.exceptions.add(ConstructorReferenceMismatchException.create(currentReference.getFirstToken()));
157:                         }
158:                 }
159:                 if (!this.exceptions.isEmpty()) {
160:                         throw ConstructorReferencerExceptions.create(this.exceptions);
161:                 }
162:         }
163:         
164:         /**
165:          * Tries to find exactly one match between reference, if it has only a type and the list of parameters, and one of
166:          * constructorOwners constructors and replaces reference in callingConstructor with the matched constructor. Returns
167:          * true if the above applies. Adds a DuplicateConstructorException to exceptions if more than one match is being
168:          * found.
169:          *
170:          * @param constructorOwner
171:          * a class with a list of constructors to match
172:          * @param callingConstructor
173:          * the constructor that references another constructor
174:          * @param reference
175:          * the reference to replace with a real constructor.
176:          * @return true if one or more matching constructors have been found (Regardless of the fact that this "causes" an
177:          * exception). Otherwise false.
178:          */
179:         private boolean replaceReferenceWithRealConstructorIfMatchFound(final ClassType constructorOwner,
180:                         final Constructor callingConstructor,
181:                         final ConstructorReference reference) {
182:                 
183:                 return reference.getState().accept(new ConstructorReferenceStateVisitorReturn<Boolean>() {
184:                         
185:                         @Override
186:                         public Boolean handle(final ConstructorByTypeAndSignatureState byName) {
187:                                 boolean matchFound = false;
188:                                 final Collection<Constructor> matchedConstuctors = constructorOwner.getMatchingConstructors(byName);
189:                                 matchFound = !matchedConstuctors.isEmpty();
190:                                 if (matchedConstuctors.size() == ConstructorReferencer.MINIMUM_OF_CONSTRUCTOR_INSTANCES_NEEDED_TO_MATCH_A_REFERENCE
191:                                                 && !ConstructorReferencer.this.constructorCycle(callingConstructor, matchedConstuctors)) {
192:                                         reference.setState(ConstructorByReferenceState.create(
193:                                                         matchedConstuctors.iterator().next(),
194:                                                         constructorOwner));
195:                                 } else if (matchedConstuctors.size() > ConstructorReferencer.MAXIMUM_OF_CONSTRUCTOR_INSTANCES_ALLOWED_TO_MATCH_A_REFERENCE) {
196:                                         ConstructorReferencer.this.exceptions.add(DuplicateConstructorException.create(
197:                                                         matchedConstuctors,
198:                                                         callingConstructor.getFirstToken()));
199:                                 }
200:                                 return matchFound;
201:                         }
202:                         
203:                         @Override
204:                         public Boolean handle(final ConstructorInvalidState invalid) {
205:                                 // nothing to do
206:                                 return false;
207:                         }
208:                         
209:                         @Override
210:                         public Boolean handle(final ConstructorByReferenceState byReference) {
211:                                 // nothing to do
212:                                 return false;
213:                         }
214:                 });
215:                 
216:         }
217:         
218:         /**
219:          * Returns true if callingConstructor is part of matchedConstructors. Adds a CyclicConstructorCallException to
220:          * exceptions and returns false otherwise.
221:          *
222:          * @param callingConstructor
223:          * the constructor search for within matchedConstructors.
224:          * @param matchedConstuctors
225:          * matchedConstructors.
226:          * @return a boolean as stated above.
227:          */
228:         private boolean constructorCycle(final Constructor callingConstructor,
229:                         final Collection<Constructor> matchedConstuctors) {
230:                 final boolean result = matchedConstuctors.contains(callingConstructor);
231:                 if (result) {
232:                         this.exceptions.add(CyclicConstructorCallException.create(
233:                                         callingConstructor,
234:                                         callingConstructor.getFirstToken()));
235:                 }
236:                 return result;
237:         }
238:         
239:         @Override
240:         public void beginTask() throws TaskException {
241:                 // Nothing to do.
242:         }
243:         
244: }