001package org.andromda.metafacades.uml14; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Iterator; 007import java.util.LinkedHashMap; 008import java.util.LinkedHashSet; 009import java.util.List; 010import java.util.Map; 011import org.andromda.metafacades.uml.EventFacade; 012import org.andromda.metafacades.uml.FrontEndActionState; 013import org.andromda.metafacades.uml.FrontEndActivityGraph; 014import org.andromda.metafacades.uml.FrontEndController; 015import org.andromda.metafacades.uml.FrontEndControllerOperation; 016import org.andromda.metafacades.uml.FrontEndEvent; 017import org.andromda.metafacades.uml.FrontEndFinalState; 018import org.andromda.metafacades.uml.FrontEndForward; 019import org.andromda.metafacades.uml.FrontEndParameter; 020import org.andromda.metafacades.uml.FrontEndUseCase; 021import org.andromda.metafacades.uml.FrontEndView; 022import org.andromda.metafacades.uml.ModelElementFacade; 023import org.andromda.metafacades.uml.OperationFacade; 024import org.andromda.metafacades.uml.ParameterFacade; 025import org.andromda.metafacades.uml.PseudostateFacade; 026import org.andromda.metafacades.uml.StateVertexFacade; 027import org.andromda.metafacades.uml.TransitionFacade; 028import org.andromda.metafacades.uml.UseCaseFacade; 029import org.apache.commons.collections.CollectionUtils; 030import org.apache.commons.collections.Predicate; 031import org.apache.commons.lang.StringUtils; 032 033 034/** 035 * MetafacadeLogic implementation for org.andromda.metafacades.uml.FrontEndAction. 036 * 037 * @see org.andromda.metafacades.uml.FrontEndAction 038 * @author Bob Fields 039 */ 040public class FrontEndActionLogicImpl 041 extends FrontEndActionLogic 042{ 043 private static final long serialVersionUID = -5553731254658712583L; 044 045 /** 046 * @param metaObject 047 * @param context 048 */ 049 public FrontEndActionLogicImpl( 050 Object metaObject, 051 String context) 052 { 053 super(metaObject, context); 054 } 055 056 /** 057 * Returns either a PseudostateFacade or FrontEndView 058 * @return input source 059 * @see org.andromda.metafacades.uml.FrontEndAction#getInput() 060 */ 061 protected Object handleGetInput() 062 { 063 Object input = null; 064 final ModelElementFacade source = this.getSource(); 065 if (source instanceof PseudostateFacade) 066 { 067 final PseudostateFacade pseudostate = (PseudostateFacade)source; 068 if (pseudostate.isInitialState()) 069 { 070 input = source; 071 } 072 } 073 else 074 { 075 if (source instanceof FrontEndView) 076 { 077 input = source; 078 } 079 } 080 return input; 081 } 082 083 /** 084 * @see org.andromda.metafacades.uml.FrontEndAction#getParameters() 085 */ 086 @Override 087 protected List handleGetParameters() 088 { 089 final EventFacade trigger = this.getTrigger(); 090 return trigger == null ? Collections.emptyList() : new ArrayList(trigger.getParameters()); 091 } 092 093 /** 094 * @see org.andromda.metafacades.uml.FrontEndAction#findParameter(String) 095 */ 096 @Override 097 protected ParameterFacade handleFindParameter(final String name) 098 { 099 return (ParameterFacade)CollectionUtils.find( 100 this.getParameters(), 101 new Predicate() 102 { 103 public boolean evaluate(Object object) 104 { 105 final ParameterFacade parameter = (ParameterFacade)object; 106 return StringUtils.trimToEmpty(parameter.getName()).equals(name); 107 } 108 }); 109 } 110 111 /** 112 * @see org.andromda.metafacades.uml.FrontEndAction#getDeferredOperations() 113 */ 114 @Override 115 protected List handleGetDeferredOperations() 116 { 117 final Collection deferredOperations = new LinkedHashSet(); 118 final FrontEndController controller = this.getController(); 119 if (controller != null) 120 { 121 final List actionStates = this.getActionStates(); 122 for (int ctr = 0; ctr < actionStates.size(); ctr++) 123 { 124 final FrontEndActionState actionState = (FrontEndActionState)actionStates.get(ctr); 125 deferredOperations.addAll(actionState.getControllerCalls()); 126 } 127 128 final List transitions = this.getDecisionTransitions(); 129 for (int ctr = 0; ctr < transitions.size(); ctr++) 130 { 131 final FrontEndForward forward = (FrontEndForward)transitions.get(ctr); 132 final FrontEndEvent trigger = forward.getDecisionTrigger(); 133 if (trigger != null) 134 { 135 deferredOperations.add(trigger.getControllerCall()); 136 } 137 } 138 } 139 return new ArrayList(deferredOperations); 140 } 141 142 /** 143 * @see org.andromda.metafacades.uml.FrontEndAction#getDecisionTransitions() 144 */ 145 @Override 146 protected List handleGetDecisionTransitions() 147 { 148 if (decisionTransitions == null) 149 { 150 initializeCollections(); 151 } 152 return new ArrayList(decisionTransitions); 153 } 154 155 /** 156 * @see org.andromda.metafacades.uml.FrontEndAction#getTargetViews() 157 */ 158 @Override 159 protected List<StateVertexFacade> handleGetTargetViews() 160 { 161 final Collection<StateVertexFacade> targetViews = new LinkedHashSet<StateVertexFacade>(); 162 final Collection<FrontEndForward> forwards = this.getActionForwards(); 163 for (final Iterator<FrontEndForward> iterator = forwards.iterator(); iterator.hasNext();) 164 { 165 final FrontEndForward forward = iterator.next(); 166 if (forward.isEnteringView()) 167 { 168 targetViews.add(forward.getTarget()); 169 } 170 } 171 return new ArrayList(targetViews); 172 } 173 174 /** 175 * All action states that make up this action, this includes all possible action states traversed 176 * after a decision point too. 177 */ 178 private Collection actionStates = null; 179 180 /** 181 * All transitions leading into either a page or final state that originated from a call to this action. 182 */ 183 private Map actionForwards = null; 184 185 /** 186 * All transitions leading into a decision point that originated from a call to this action. 187 */ 188 private Collection decisionTransitions = null; 189 190 /** 191 * All transitions that can be traversed when calling this action. 192 */ 193 private Collection transitions = null; 194 195 /** 196 * Initializes all action states, action forwards, decision transitions and transitions in one shot, so that they 197 * can be queried more effiencently later on. 198 */ 199 private void initializeCollections() 200 { 201 this.actionStates = new LinkedHashSet(); 202 this.actionForwards = new LinkedHashMap(); 203 this.decisionTransitions = new LinkedHashSet(); 204 this.transitions = new LinkedHashSet(); 205 this.collectTransitions( 206 (TransitionFacade)this.THIS(), 207 transitions); 208 } 209 210 /** 211 * Recursively collects all action states, action forwards, decision transitions and transitions. 212 * 213 * @param transition the current transition that is being processed 214 * @param processedTransitions the set of transitions already processed 215 */ 216 private void collectTransitions( 217 TransitionFacade transition, 218 Collection processedTransitions) 219 { 220 if (processedTransitions.contains(transition)) 221 { 222 return; 223 } 224 processedTransitions.add(transition); 225 final StateVertexFacade target = transition.getTarget(); 226 if (target instanceof FrontEndView || target instanceof FrontEndFinalState) 227 { 228 if (!this.actionForwards.containsKey(transition.getTarget())) 229 { 230 this.actionForwards.put( 231 transition.getTarget(), 232 transition); 233 } 234 } 235 else if (target instanceof PseudostateFacade && ((PseudostateFacade)target).isDecisionPoint()) 236 { 237 this.decisionTransitions.add(transition); 238 final Collection<TransitionFacade> outcomes = target.getOutgoings(); 239 for (final Iterator<TransitionFacade> iterator = outcomes.iterator(); iterator.hasNext();) 240 { 241 final TransitionFacade outcome = iterator.next(); 242 collectTransitions( 243 outcome, 244 processedTransitions); 245 } 246 } 247 else if (target instanceof FrontEndActionState) 248 { 249 this.actionStates.add(target); 250 final FrontEndForward forward = ((FrontEndActionState)target).getForward(); 251 if (forward != null) 252 { 253 collectTransitions( 254 forward, 255 processedTransitions); 256 } 257 } 258 else // all the rest is ignored but outgoing transitions are further processed 259 { 260 final Collection<TransitionFacade> outcomes = target.getOutgoings(); 261 for (final Iterator<TransitionFacade> iterator = outcomes.iterator(); iterator.hasNext();) 262 { 263 final TransitionFacade outcome = iterator.next(); 264 collectTransitions( 265 outcome, 266 processedTransitions); 267 } 268 } 269 } 270 271 /** 272 * @see org.andromda.metafacades.uml.FrontEndAction#getActionStates() 273 */ 274 @Override 275 protected List handleGetActionStates() 276 { 277 if (this.actionStates == null) 278 { 279 this.initializeCollections(); 280 } 281 return new ArrayList(this.actionStates); 282 } 283 284 /** 285 * @see org.andromda.metafacades.uml.FrontEndAction#getTransitions() 286 */ 287 @Override 288 protected List handleGetTransitions() 289 { 290 if (this.transitions == null) 291 { 292 this.initializeCollections(); 293 } 294 return new ArrayList(this.transitions); 295 } 296 297 /** 298 * @see org.andromda.metafacades.uml.FrontEndAction#getActionForwards() 299 */ 300 @Override 301 protected List handleGetActionForwards() 302 { 303 if (this.actionForwards == null) 304 { 305 this.initializeCollections(); 306 } 307 return new ArrayList(this.actionForwards.values()); 308 } 309 310 /** 311 * @see org.andromda.metafacades.uml.FrontEndAction#getController() 312 */ 313 @Override 314 protected FrontEndController handleGetController() 315 { 316 final FrontEndActivityGraph graph = this.getFrontEndActivityGraph(); 317 return graph == null ? null : graph.getController(); 318 } 319 320 /** 321 * Overridden because actions (transitions) are not directly contained in a UML namespace. 322 * 323 * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName() 324 */ 325 @Override 326 public String handleGetPackageName() 327 { 328 String packageName = null; 329 330 final UseCaseFacade useCase = this.getUseCase(); 331 if (useCase != null) 332 { 333 packageName = useCase.getPackageName(); 334 } 335 return packageName; 336 } 337 338 /** 339 * @see org.andromda.metafacades.uml.FrontEndAction#isUseCaseStart() 340 */ 341 @Override 342 protected boolean handleIsUseCaseStart() 343 { 344 final StateVertexFacade source = getSource(); 345 return source instanceof PseudostateFacade && ((PseudostateFacade)source).isInitialState(); 346 } 347 348 /** 349 * @see org.andromda.metafacades.uml.FrontEndAction#getFormFields() 350 */ 351 @Override 352 protected List<ParameterFacade> handleGetFormFields() 353 { 354 final Map<String, ParameterFacade> formFieldMap = new LinkedHashMap<String, ParameterFacade>(); 355 356 // - For an action that starts the use case, we need to detect all usecases forwarding to the one 357 // belonging to this action if there are any parameters in those transitions we need to have 358 // them included in this action's form 359 if (this.isUseCaseStart()) 360 { 361 final FrontEndUseCase useCase = this.getUseCase(); 362 if (useCase != null) 363 { 364 final Collection<FrontEndFinalState> finalStates = useCase.getReferencingFinalStates(); 365 for (final Iterator<FrontEndFinalState> finalStateIterator = finalStates.iterator(); finalStateIterator.hasNext();) 366 { 367 final Object finalStateObject = finalStateIterator.next(); 368 369 // we need to test for the type because a non struts-use-case final state might accidently 370 // link to this use-case (for example: the user temporarily wants to disable code generation 371 // for a specific use-case and is not removing the final-state to use-case link(s)) 372 if (finalStateObject instanceof FrontEndFinalState) 373 { 374 final FrontEndFinalState finalState = (FrontEndFinalState)finalStateObject; 375 final Collection<FrontEndParameter> parameters = finalState.getInterUseCaseParameters(); 376 for (final Iterator<FrontEndParameter> parameterIterator = parameters.iterator(); parameterIterator.hasNext();) 377 { 378 final ParameterFacade parameter = (ParameterFacade)parameterIterator.next(); 379 formFieldMap.put( 380 parameter.getName(), 381 parameter); 382 } 383 } 384 } 385 } 386 } 387 388 // if any action encountered by the execution of the complete action-graph path emits a forward 389 // containing one or more parameters they need to be included as a form field too 390 final Collection actionStates = this.getActionStates(); 391 for (final Iterator iterator = actionStates.iterator(); iterator.hasNext();) 392 { 393 final FrontEndActionState actionState = (FrontEndActionState)iterator.next(); 394 final FrontEndForward forward = actionState.getForward(); 395 if (forward != null) 396 { 397 final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters(); 398 for (final Iterator<FrontEndParameter> parameterIterator = forwardParameters.iterator(); parameterIterator.hasNext();) 399 { 400 final ParameterFacade forwardParameter = (ParameterFacade)parameterIterator.next(); 401 formFieldMap.put( 402 forwardParameter.getName(), 403 forwardParameter); 404 } 405 } 406 } 407 408 // add page variables for all pages/final-states targetted 409 // also add the fields of the target page's actions (for preloading) 410 final Collection forwards = this.getActionForwards(); 411 for (final Iterator iterator = forwards.iterator(); iterator.hasNext();) 412 { 413 final FrontEndForward forward = (FrontEndForward)iterator.next(); 414 final StateVertexFacade target = forward.getTarget(); 415 if (target instanceof FrontEndView) 416 { 417 final FrontEndView view = (FrontEndView)target; 418 final Collection<FrontEndParameter> viewVariables = view.getVariables(); 419 for (final Iterator<FrontEndParameter> pageVariableIterator = viewVariables.iterator(); pageVariableIterator.hasNext();) 420 { 421 final ParameterFacade facade = (ParameterFacade)pageVariableIterator.next(); 422 formFieldMap.put( 423 facade.getName(), 424 facade); 425 } 426 final Collection<FrontEndParameter> allActionParameters = view.getAllFormFields(); 427 for (final Iterator<FrontEndParameter> actionParameterIterator = allActionParameters.iterator(); 428 actionParameterIterator.hasNext();) 429 { 430 // - don't allow existing parameters that are tables be overwritten (since they take 431 // precedence 432 final Object parameter = actionParameterIterator.next(); 433 if (parameter instanceof FrontEndParameter) 434 { 435 FrontEndParameter variable = (FrontEndParameter)parameter; 436 final String name = variable.getName(); 437 final Object existingParameter = formFieldMap.get(name); 438 if (existingParameter instanceof FrontEndParameter) 439 { 440 final FrontEndParameter existingVariable = (FrontEndParameter)existingParameter; 441 if (existingVariable.isTable()) 442 { 443 variable = existingVariable; 444 } 445 } 446 formFieldMap.put( 447 name, 448 variable); 449 } 450 } 451 } 452 else if (target instanceof FrontEndFinalState) 453 { 454 // only add these if there is no parameter recorded yet with the same name 455 final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters(); 456 for (final Iterator<FrontEndParameter> parameterIterator = forwardParameters.iterator(); parameterIterator.hasNext();) 457 { 458 final FrontEndParameter facade = parameterIterator.next(); 459 if (!formFieldMap.containsKey(facade.getName())) 460 { 461 formFieldMap.put( 462 facade.getName(), 463 facade); 464 } 465 } 466 } 467 } 468 469 // we do the action parameters in the end because they are allowed to overwrite existing properties 470 final Collection<FrontEndParameter> actionParameters = this.getParameters(); 471 for (final Iterator<FrontEndParameter> parameterIterator = actionParameters.iterator(); parameterIterator.hasNext();) 472 { 473 final Object parameter = parameterIterator.next(); 474 if (parameter instanceof FrontEndParameter) 475 { 476 final FrontEndParameter variable = (FrontEndParameter)parameter; 477 formFieldMap.put( 478 variable.getName(), 479 variable); 480 } 481 } 482 483 // - if we don't have any fields defined on this action and there are no action forwards, 484 // take the parameters from the deferred operations (since we would want to stay on the same view) 485 if (formFieldMap.isEmpty() && this.getActionForwards().isEmpty()) 486 { 487 for (final Iterator<FrontEndControllerOperation> iterator = this.getDeferredOperations().iterator(); iterator.hasNext();) 488 { 489 final OperationFacade operation = (OperationFacade)iterator.next(); 490 for (final Iterator<ParameterFacade> parameterIterator = operation.getArguments().iterator(); parameterIterator.hasNext();) 491 { 492 final ParameterFacade parameter = parameterIterator.next(); 493 formFieldMap.put(parameter.getName(), parameter); 494 } 495 } 496 } 497 return new ArrayList<ParameterFacade>(formFieldMap.values()); 498 } 499}