1 | package de.uka.ipd.sdq.pcmsolver.transformations; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.List; |
5 | |
6 | import org.eclipse.emf.common.util.BasicEList; |
7 | import org.eclipse.emf.common.util.EList; |
8 | |
9 | import de.uka.ipd.sdq.pcm.core.composition.AssemblyConnector; |
10 | import de.uka.ipd.sdq.pcm.core.composition.AssemblyContext; |
11 | import de.uka.ipd.sdq.pcm.core.composition.ComposedStructure; |
12 | import de.uka.ipd.sdq.pcm.core.composition.Connector; |
13 | import de.uka.ipd.sdq.pcm.core.composition.ProvidedDelegationConnector; |
14 | import de.uka.ipd.sdq.pcm.core.composition.RequiredDelegationConnector; |
15 | import de.uka.ipd.sdq.pcm.repository.BasicComponent; |
16 | import de.uka.ipd.sdq.pcm.repository.OperationInterface; |
17 | import de.uka.ipd.sdq.pcm.repository.OperationProvidedRole; |
18 | import de.uka.ipd.sdq.pcm.repository.OperationRequiredRole; |
19 | import de.uka.ipd.sdq.pcm.repository.RepositoryComponent; |
20 | import de.uka.ipd.sdq.pcm.seff.ExternalCallAction; |
21 | import de.uka.ipd.sdq.pcm.system.System; |
22 | import de.uka.ipd.sdq.pcm.usagemodel.EntryLevelSystemCall; |
23 | |
24 | /** |
25 | * This class provides auxiliary functionality factored out from the |
26 | * ContextWrapper for examining PCM instances. |
27 | * |
28 | * TODO: continue the outsourcing to separate context-independent tasks from the |
29 | * ContextWrapper class! |
30 | * |
31 | * @author brosch |
32 | * |
33 | */ |
34 | public class PCMInstanceHelper { |
35 | |
36 | /** |
37 | * Retrieves the list of nested handling AssemblyContexts for the given |
38 | * EntryLevelSystemCall. |
39 | * |
40 | * A handling AssemblyContext is one that includes the executing behavior |
41 | * triggered by the call. |
42 | * |
43 | * @param call |
44 | * the EntryLevelSystemCall |
45 | * @param system |
46 | * the involved System instance |
47 | * @return the list of nested handling AssemblyContexts |
48 | */ |
49 | public static List<AssemblyContext> getHandlingAssemblyContexts( |
50 | final EntryLevelSystemCall call, final System system) { |
51 | |
52 | // Find the top-level assembly context and |
53 | // providing role for the call: |
54 | AssemblyContext topLevelAssCtx = null; |
55 | OperationProvidedRole topLevelAssCtxProvidedRole = null; |
56 | for (Connector conn : system.getConnectors__ComposedStructure()) { |
57 | if (conn instanceof ProvidedDelegationConnector) { |
58 | ProvidedDelegationConnector pdc = (ProvidedDelegationConnector) conn; |
59 | if (pdc.getOuterProvidedRole_ProvidedDelegationConnector() |
60 | .getId().equals( |
61 | call.getProvidedRole_EntryLevelSystemCall() |
62 | .getId())) { |
63 | topLevelAssCtx = pdc |
64 | .getAssemblyContext_ProvidedDelegationConnector(); |
65 | topLevelAssCtxProvidedRole = pdc |
66 | .getInnerProvidedRole_ProvidedDelegationConnector(); |
67 | } |
68 | } |
69 | } |
70 | |
71 | if (topLevelAssCtx != null) { |
72 | return getHandlingAssemblyContexts(topLevelAssCtx, |
73 | topLevelAssCtxProvidedRole, |
74 | new BasicEList<AssemblyContext>()); |
75 | } |
76 | |
77 | // No matching ProvidedDelegationConnector found: |
78 | throw new IllegalArgumentException( |
79 | "No ProvidedDelegationConnector found in system \"" |
80 | + system.getEntityName() |
81 | + "\" that matches the EntryLevelSystemCall \"" |
82 | + call.getEntityName() + "\"."); |
83 | } |
84 | |
85 | /** |
86 | * Searches for the providing AssemblyContexts that handle the given |
87 | * ExternalCallAction. |
88 | * |
89 | * If the ExternalCallAction is a system external call, the method returns |
90 | * an empty list. If the providing side is a nested structure of |
91 | * AssemblyContexts, all providing AssemblyContexts are returned in a list, |
92 | * with the actual handling context as the last element. |
93 | * |
94 | * @param call |
95 | * the ExternallCallAction |
96 | * @param encapsulatingContexts |
97 | * the list of encapsulating AssemblyContexts of the |
98 | * ExternalCallAction (required for its unique identification) |
99 | * @return the list of nested handling AssemblyContexts |
100 | */ |
101 | public static List<AssemblyContext> getHandlingAssemblyContexts( |
102 | final ExternalCallAction call, |
103 | final List<AssemblyContext> encapsulatingContexts) { |
104 | |
105 | // Copy the received list of contexts: |
106 | List<AssemblyContext> contexts = new ArrayList<AssemblyContext>(); |
107 | contexts.addAll(encapsulatingContexts); |
108 | |
109 | // Search for an AssemblyConnector between the calling |
110 | // AssemblyContext and the handling AssemblyContext: |
111 | AssemblyConnector connector = getAssemblyConnectorForRequiredRole(call |
112 | .getRole_ExternalService(), (OperationInterface) call |
113 | .getCalledService_ExternalService().eContainer(), contexts); |
114 | if (connector == null) { |
115 | // If no AssemblyConnector is found, the call is a system external |
116 | // call and has no handling AssemblyContext: |
117 | return new ArrayList<AssemblyContext>(); |
118 | } |
119 | |
120 | // Retrieve the set of handling assembly contexts from: |
121 | return getHandlingAssemblyContexts(connector |
122 | .getProvidingAssemblyContext_AssemblyConnector(), connector |
123 | .getProvidedRole_AssemblyConnector(), contexts); |
124 | } |
125 | |
126 | /** |
127 | * Searches for an AssemblyConnector that connects the current |
128 | * AssemblyContext via its given requiredRole to its providing counterpart. |
129 | * |
130 | * The current AssemblyContext is the last element of the given list of |
131 | * nested contexts. The method traverses any RequiredDelegationConnectors |
132 | * that lie between the AssemblyContext and its AssemblyConnector. If the |
133 | * role is connected to the system boundary, the method returns NULL. During |
134 | * the method, the list of nestedContexts is adapted to the current search |
135 | * level. If a connector is found, the resulting list reflects the |
136 | * encapsulating contexts of the connector. |
137 | * |
138 | * @param requiredRole |
139 | * the RequiredRole to match |
140 | * @param requiredInterfaceId |
141 | * the Interface to match |
142 | * @return the AssemblyConnector, or NULL, if the required role leads to the |
143 | * system boundary |
144 | */ |
145 | private static AssemblyConnector getAssemblyConnectorForRequiredRole( |
146 | final OperationRequiredRole requiredRole, |
147 | final OperationInterface requiredInterface, |
148 | final List<AssemblyContext> nestedContexts) { |
149 | |
150 | // Navigate upwards the stack of parent AssemblyContexts |
151 | // (starting from the current AssemblyContext): |
152 | OperationRequiredRole currentRequiredRole = requiredRole; |
153 | AssemblyContext currentContext = null; |
154 | while (!nestedContexts.isEmpty()) { |
155 | |
156 | // Examine the innermost context of the list: |
157 | currentContext = nestedContexts.get(nestedContexts.size() - 1); |
158 | nestedContexts.remove(currentContext); |
159 | |
160 | // Check if the searched AssemblyConnector is directly |
161 | // connected to the currently examined context: |
162 | AssemblyConnector matchingAssConn = getAssemblyConnectorForRequiringAssemblyContext( |
163 | currentRequiredRole, requiredInterface, currentContext); |
164 | if (matchingAssConn != null) { |
165 | return matchingAssConn; |
166 | } |
167 | |
168 | // As no AssemblyConnector is directly connected, we |
169 | // have to look for a RequiredDelegationConnector |
170 | // instead and repeat the search for the next higher |
171 | // AssemblyContext and its corresponding |
172 | // OperationRequiredRole: |
173 | RequiredDelegationConnector matchingDeleConn = getDelegationConnectorForRequiringAssemblyContext( |
174 | currentRequiredRole, requiredInterface, currentContext); |
175 | if (matchingDeleConn == null) { |
176 | // Error handling: |
177 | throw new IllegalArgumentException( |
178 | "Neither an AssemblyConnector nor a RequiredDelegationConnector could be found " |
179 | + "connected to the OperationRequiredRole \"" |
180 | + currentRequiredRole.getEntityName() |
181 | + "\" for the OperationInterface \"" |
182 | + requiredInterface.getEntityName() |
183 | + "\" of the AssemblyContext \"" |
184 | + currentContext.getEntityName() + "\"."); |
185 | } |
186 | currentRequiredRole = matchingDeleConn |
187 | .getOuterRequiredRole_RequiredDelegationConnector(); |
188 | } |
189 | |
190 | // No AssemblyContext found: |
191 | return null; |
192 | } |
193 | |
194 | /** |
195 | * Searches for an AssemblyConnector that connects a given |
196 | * requiringAssemblyContext via its requiredRole to its providing |
197 | * counterpart. |
198 | * |
199 | * Notice that the requiredRole of the requiringAssemblyContext could also |
200 | * be associated to a RequiredDelegationConnector instead of an |
201 | * AssemblyConnector. In this case, NULL is returned. |
202 | * |
203 | * @param requiredRole |
204 | * the RequiredRole to match |
205 | * @param requiredInterface |
206 | * the Interface to match |
207 | * @param requiringContext |
208 | * the AssemblyContext to match |
209 | * @return the matching AssemblyConnector within the parent |
210 | * ComposedStructure |
211 | */ |
212 | private static AssemblyConnector getAssemblyConnectorForRequiringAssemblyContext( |
213 | final OperationRequiredRole requiredRole, |
214 | final OperationInterface requiredInterface, |
215 | final AssemblyContext requiringContext) { |
216 | |
217 | // Retrieve the list of connectors within the parent |
218 | // ComposedStructure: |
219 | EList<Connector> connList = requiringContext |
220 | .getParentStructure__AssemblyContext() |
221 | .getConnectors__ComposedStructure(); |
222 | |
223 | // Check for each AssemblyConnector in the list if it fulfills |
224 | // the requirements: |
225 | for (Connector conn : connList) { |
226 | if (conn instanceof AssemblyConnector) { |
227 | AssemblyConnector assConn = (AssemblyConnector) conn; |
228 | if (assConn.getRequiringAssemblyContext_AssemblyConnector() |
229 | .getId().equals(requiringContext.getId()) |
230 | && assConn.getRequiredRole_AssemblyConnector() |
231 | .getRequiredInterface__OperationRequiredRole() |
232 | .getId().equals(requiredInterface.getId()) |
233 | && assConn.getRequiredRole_AssemblyConnector().getId() |
234 | .equals(requiredRole.getId())) { |
235 | return assConn; |
236 | } |
237 | } |
238 | } |
239 | |
240 | // No AssmblyConnector found: |
241 | return null; |
242 | } |
243 | |
244 | /** |
245 | * Searches for a RequiredDelegationConnector that connects a given |
246 | * requiring AssemblyContext via its requiredRole to an encapsulating |
247 | * ComposedStructure. |
248 | * |
249 | * Notice that the requiredRole of the requiring AssemblyContext could also |
250 | * be associated to an AssemblyConnector instead of a |
251 | * RequiredDelegationConnector. In this case, NULL is returned. |
252 | * |
253 | * @param requiredRole |
254 | * the RequiredRole to match |
255 | * @param requiredInterface |
256 | * the Interface to match |
257 | * @param requiringContext |
258 | * the AssemblyContext to match |
259 | * @return the matching RequiredDelegationConnector within the parent |
260 | * ComposedStructure |
261 | */ |
262 | private static RequiredDelegationConnector getDelegationConnectorForRequiringAssemblyContext( |
263 | OperationRequiredRole requiredRole, |
264 | OperationInterface requiredInterface, |
265 | AssemblyContext requiringContext) { |
266 | |
267 | // Retrieve the list of connectors within the parent |
268 | // ComposedStructure: |
269 | EList<Connector> connList = requiringContext |
270 | .getParentStructure__AssemblyContext() |
271 | .getConnectors__ComposedStructure(); |
272 | |
273 | // Check for each RequiredDelegationConnector in the list if it fulfills |
274 | // the requirements: |
275 | for (Connector conn : connList) { |
276 | if (conn instanceof RequiredDelegationConnector) { |
277 | RequiredDelegationConnector dc = (RequiredDelegationConnector) conn; |
278 | if (dc.getAssemblyContext_RequiredDelegationConnector().getId() |
279 | .equals(requiringContext.getId()) |
280 | && dc |
281 | .getInnerRequiredRole_RequiredDelegationConnector() |
282 | .getRequiredInterface__OperationRequiredRole() |
283 | .getId().equals(requiredInterface.getId()) |
284 | && dc |
285 | .getInnerRequiredRole_RequiredDelegationConnector() |
286 | .getId().equals(requiredRole.getId())) { |
287 | return dc; |
288 | } |
289 | } |
290 | } |
291 | |
292 | // No RequiredDelegationConnector found: |
293 | return null; |
294 | } |
295 | |
296 | /** |
297 | * Extends a given list of AssemblyContexts with a given top-level |
298 | * AssemblyContext and all nested ones, according to a given top-level |
299 | * OperationProvidedRole. |
300 | * |
301 | * @param topLevelContexts |
302 | * the top-level AssemblyContext |
303 | * @param topLevelProvidedRole |
304 | * the top-level OperationProvidedRole, which is assumed to |
305 | * belong to the component encapsulated by the top-level |
306 | * AssemblyContext. |
307 | * @param existingContexts |
308 | * the existing list of AssemblyContexts |
309 | * @return the extended AssemblyContext list |
310 | */ |
311 | private static List<AssemblyContext> getHandlingAssemblyContexts( |
312 | final AssemblyContext topLevelContexts, |
313 | final OperationProvidedRole topLevelProvidedRole, |
314 | final List<AssemblyContext> existingContexts) { |
315 | |
316 | // In any case, the top-level AssemblyContext itself |
317 | // will be added to the list: |
318 | existingContexts.add(topLevelContexts); |
319 | |
320 | // Retrieve the encapsulated top-level component: |
321 | RepositoryComponent rc = topLevelContexts |
322 | .getEncapsulatedComponent__AssemblyContext(); |
323 | |
324 | // Check for the type of the top-level component: |
325 | if (rc instanceof BasicComponent) { |
326 | |
327 | // Case 1: We already have a basic component. Hence, |
328 | // there are no more nested AssemblyContexts to add: |
329 | return existingContexts; |
330 | |
331 | } else if (rc instanceof ComposedStructure) { |
332 | |
333 | // Case 2: We have a composed structure. Hence, a |
334 | // ProvidedDelegationConnector will lead us to a |
335 | // nested AssemblyContext: |
336 | ComposedStructure cs = (ComposedStructure) rc; |
337 | for (Connector conn : cs.getConnectors__ComposedStructure()) { |
338 | if (conn instanceof ProvidedDelegationConnector) { |
339 | ProvidedDelegationConnector pdc = (ProvidedDelegationConnector) conn; |
340 | if (pdc.getOuterProvidedRole_ProvidedDelegationConnector() |
341 | .getId().equals(topLevelProvidedRole.getId())) { |
342 | |
343 | // We have found a ProvidedDelegationConnector that |
344 | // delegates from a nested AssemblyContext to the |
345 | // top-level ProvidedRole. Now, we recursively |
346 | // continue with the nested AssemblyContext: |
347 | AssemblyContext nestedAssCtx = pdc |
348 | .getAssemblyContext_ProvidedDelegationConnector(); |
349 | OperationProvidedRole nestedProvidedRole = pdc |
350 | .getInnerProvidedRole_ProvidedDelegationConnector(); |
351 | return getHandlingAssemblyContexts(nestedAssCtx, |
352 | nestedProvidedRole, existingContexts); |
353 | } |
354 | } |
355 | } |
356 | |
357 | // Error handling in case no matching ProvidedDelegationConnector |
358 | // has been found: |
359 | throw new IllegalArgumentException( |
360 | "No ProvidedDelegationConnector found in ComposedStructure \"" |
361 | + cs.getEntityName() |
362 | + "\" that matches the OperationProvidedRole \"" |
363 | + topLevelProvidedRole.getEntityName() + "\"."); |
364 | } |
365 | |
366 | // Error handling in case an unknown component type is encountered: |
367 | throw new IllegalArgumentException("Unknown component type \"" |
368 | + rc.eClass().getName() |
369 | + "\" encapsulated by AssemblyContext \"" |
370 | + topLevelContexts.getEntityName() + "\""); |
371 | } |
372 | } |