| 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 | } |