1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.application;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.EnumSet;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36 import java.util.regex.Pattern;
37 import javax.el.MethodExpression;
38 import javax.faces.FacesException;
39 import javax.faces.application.ConfigurableNavigationHandler;
40 import javax.faces.application.FacesMessage;
41 import javax.faces.application.NavigationCase;
42 import javax.faces.application.ProjectStage;
43 import javax.faces.application.ViewHandler;
44 import javax.faces.component.UIComponent;
45 import javax.faces.component.UIViewAction;
46 import javax.faces.component.UIViewRoot;
47 import javax.faces.component.visit.VisitCallback;
48 import javax.faces.component.visit.VisitContext;
49 import javax.faces.component.visit.VisitHint;
50 import javax.faces.component.visit.VisitResult;
51 import javax.faces.context.ExternalContext;
52 import javax.faces.context.FacesContext;
53 import javax.faces.context.PartialViewContext;
54 import javax.faces.flow.Flow;
55 import javax.faces.flow.FlowCallNode;
56 import javax.faces.flow.FlowHandler;
57 import javax.faces.flow.FlowNode;
58 import javax.faces.flow.MethodCallNode;
59 import javax.faces.flow.Parameter;
60 import javax.faces.flow.ReturnNode;
61 import javax.faces.flow.SwitchCase;
62 import javax.faces.flow.SwitchNode;
63 import javax.faces.flow.ViewNode;
64 import javax.faces.view.ViewDeclarationLanguage;
65 import javax.faces.view.ViewMetadata;
66
67 import org.apache.myfaces.config.RuntimeConfig;
68 import org.apache.myfaces.config.element.NavigationRule;
69 import org.apache.myfaces.flow.FlowHandlerImpl;
70 import org.apache.myfaces.shared.application.NavigationUtils;
71 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
72 import org.apache.myfaces.shared.util.ClassUtils;
73 import org.apache.myfaces.shared.util.HashMapUtils;
74 import org.apache.myfaces.shared.util.StringUtils;
75 import org.apache.myfaces.util.FilenameUtils;
76 import org.apache.myfaces.view.facelets.ViewPoolProcessor;
77 import org.apache.myfaces.view.facelets.tag.jsf.PreDisposeViewEvent;
78
79
80
81
82
83
84 public class NavigationHandlerImpl
85 extends ConfigurableNavigationHandler
86 {
87
88 private static final Logger log = Logger.getLogger(NavigationHandlerImpl.class.getName());
89
90 private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
91
92 private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet(
93 EnumSet.of(VisitHint.SKIP_ITERATION));
94
95 private static final String OUTCOME_NAVIGATION_SB = "oam.navigation.OUTCOME_NAVIGATION_SB";
96
97 private static final Pattern AMP_PATTERN = Pattern.compile("&(amp;)?");
98
99 private static final String ASTERISK = "*";
100
101 private Map<String, Set<NavigationCase>> _navigationCases = null;
102
103 private List<_WildcardPattern> _wildcardPatterns = new ArrayList<_WildcardPattern>();
104 private Boolean _developmentStage;
105
106 private Map<String, _FlowNavigationStructure> _flowNavigationStructureMap =
107 new ConcurrentHashMap<String, _FlowNavigationStructure>();
108
109 private NavigationHandlerSupport navigationHandlerSupport;
110
111 public NavigationHandlerImpl()
112 {
113 if (log.isLoggable(Level.FINEST))
114 {
115 log.finest("New NavigationHandler instance created");
116 }
117 }
118
119 @Override
120 public void handleNavigation(FacesContext facesContext, String fromAction, String outcome)
121 {
122 handleNavigation(facesContext, fromAction, outcome, null);
123 }
124
125 @Override
126 public void handleNavigation(FacesContext facesContext, String fromAction,
127 String outcome, String toFlowDocumentId)
128 {
129
130 NavigationContext navigationContext = new NavigationContext();
131 NavigationCase navigationCase = null;
132 try
133 {
134 navigationCase = getNavigationCommand(facesContext, navigationContext, fromAction, outcome,
135 toFlowDocumentId);
136 }
137 finally
138 {
139 navigationContext.finish(facesContext);
140 }
141
142 if (navigationCase != null)
143 {
144 if (log.isLoggable(Level.FINEST))
145 {
146 log.finest("handleNavigation fromAction=" + fromAction + " outcome=" + outcome +
147 " toViewId =" + navigationCase.getToViewId(facesContext) +
148 " redirect=" + navigationCase.isRedirect());
149 }
150 boolean isViewActionProcessingBroadcastAndRequiresRedirect = false;
151 if (UIViewAction.isProcessingBroadcast(facesContext))
152 {
153
154
155
156
157 facesContext.getExternalContext().getFlash().setKeepMessages(true);
158 String fromViewId = (facesContext.getViewRoot() == null) ? null :
159 facesContext.getViewRoot().getViewId();
160 String toViewId = navigationCase.getToViewId(facesContext);
161
162
163
164 if (fromViewId == null && toViewId != null)
165 {
166 isViewActionProcessingBroadcastAndRequiresRedirect = true;
167 }
168 else if (fromViewId != null && !fromViewId.equals(toViewId))
169 {
170 isViewActionProcessingBroadcastAndRequiresRedirect = true;
171 }
172 }
173 if (navigationCase.isRedirect() || isViewActionProcessingBroadcastAndRequiresRedirect)
174 {
175
176
177
178
179
180
181 ExternalContext externalContext = facesContext.getExternalContext();
182 ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
183 String toViewId = navigationCase.getToViewId(facesContext);
184
185 String redirectPath = viewHandler.getRedirectURL(
186 facesContext, toViewId,
187 NavigationUtils.getEvaluatedNavigationParameters(facesContext,
188 navigationCase.getParameters()) ,
189 navigationCase.isIncludeViewParams());
190
191
192
193
194
195 applyFlowTransition(facesContext, navigationContext);
196
197
198 UIViewRoot viewRoot = facesContext.getViewRoot();
199 if (viewRoot != null && !toViewId.equals(viewRoot.getViewId()))
200 {
201
202 Map<String, Object> viewMap = viewRoot.getViewMap(false);
203 if (viewMap != null)
204 {
205 viewMap.clear();
206 }
207 }
208
209
210
211
212
213 PartialViewContext partialViewContext = facesContext.getPartialViewContext();
214 String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
215 if ( partialViewContext.isPartialRequest() &&
216 !partialViewContext.isRenderAll() &&
217 toViewId != null &&
218 !toViewId.equals(viewId))
219 {
220 partialViewContext.setRenderAll(true);
221 }
222
223
224 ViewPoolProcessor processor = ViewPoolProcessor.getInstance(facesContext);
225 if (processor != null &&
226 processor.isViewPoolEnabledForThisView(facesContext, facesContext.getViewRoot()))
227 {
228 processor.disposeView(facesContext, facesContext.getViewRoot());
229 }
230
231
232 externalContext.getFlash().setRedirect(true);
233 try
234 {
235 externalContext.redirect(redirectPath);
236 facesContext.responseComplete();
237 }
238 catch (IOException e)
239 {
240 throw new FacesException(e.getMessage(), e);
241 }
242 }
243 else
244 {
245 ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
246
247 String newViewId = navigationCase.getToViewId(facesContext);
248
249
250
251
252
253 PartialViewContext partialViewContext = facesContext.getPartialViewContext();
254 String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
255 if ( partialViewContext.isPartialRequest() &&
256 !partialViewContext.isRenderAll() &&
257 newViewId != null &&
258 !newViewId.equals(viewId))
259 {
260 partialViewContext.setRenderAll(true);
261 }
262
263 if (facesContext.getViewRoot() != null &&
264 facesContext.getViewRoot().getAttributes().containsKey("oam.CALL_PRE_DISPOSE_VIEW"))
265 {
266 try
267 {
268 facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
269
270 VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, VISIT_HINTS);
271 facesContext.getViewRoot().visitTree(visitContext,
272 new PreDisposeViewCallback());
273 }
274 finally
275 {
276 facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
277 }
278 }
279
280 applyFlowTransition(facesContext, navigationContext);
281
282
283 ViewPoolProcessor processor = ViewPoolProcessor.getInstance(facesContext);
284 if (processor != null &&
285 processor.isViewPoolEnabledForThisView(facesContext, facesContext.getViewRoot()))
286 {
287 processor.disposeView(facesContext, facesContext.getViewRoot());
288 }
289
290
291 UIViewRoot viewRoot = null;
292
293 String derivedViewId = viewHandler.deriveViewId(facesContext, newViewId);
294
295 if (derivedViewId != null)
296 {
297 ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(facesContext, derivedViewId);
298
299 if (vdl != null)
300 {
301 ViewMetadata metadata = vdl.getViewMetadata(facesContext, newViewId);
302
303 if (metadata != null)
304 {
305 viewRoot = metadata.createMetadataView(facesContext);
306 }
307 }
308 }
309
310
311
312
313
314 if (viewRoot == null)
315 {
316 viewRoot = viewHandler.createView(facesContext, newViewId);
317 }
318
319 facesContext.setViewRoot(viewRoot);
320 facesContext.renderResponse();
321 }
322 }
323 else
324 {
325
326 if (log.isLoggable(Level.FINEST))
327 {
328 log.finest("handleNavigation fromAction=" + fromAction + " outcome=" + outcome +
329 " no matching navigation-case found, staying on current ViewRoot");
330 }
331 }
332 }
333
334 private void applyFlowTransition(FacesContext facesContext, NavigationContext navigationContext)
335 {
336
337
338 if (navigationContext != null &&
339 navigationContext.getSourceFlows() != null ||
340 (navigationContext.getTargetFlows() != null &&
341 !navigationContext.getTargetFlows().isEmpty()))
342 {
343 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
344 for (int i = 0; i < navigationContext.getTargetFlows().size(); i++)
345 {
346 Flow sourceFlow = navigationContext.getSourceFlows().get(i);
347 Flow targetFlow = navigationContext.getTargetFlows().get(i);
348
349 flowHandler.transition(facesContext, sourceFlow, targetFlow,
350 navigationContext.getFlowCallNodes().get(i),
351 navigationContext.getNavigationCase().getToViewId(facesContext));
352 sourceFlow = targetFlow;
353 }
354 }
355 }
356
357
358
359
360 protected NavigationHandlerSupport getNavigationHandlerSupport()
361 {
362 if (navigationHandlerSupport == null)
363 {
364 navigationHandlerSupport = new DefaultNavigationHandlerSupport();
365 }
366 return navigationHandlerSupport;
367 }
368
369 public void setNavigationHandlerSupport(NavigationHandlerSupport navigationHandlerSupport)
370 {
371 this.navigationHandlerSupport = navigationHandlerSupport;
372 }
373
374 private static class PreDisposeViewCallback implements VisitCallback
375 {
376
377 public VisitResult visit(VisitContext context, UIComponent target)
378 {
379 context.getFacesContext().getApplication().publishEvent(context.getFacesContext(),
380 PreDisposeViewEvent.class, target);
381
382 return VisitResult.ACCEPT;
383 }
384 }
385
386
387
388
389 public NavigationCase getNavigationCase(FacesContext facesContext, String fromAction, String outcome)
390 {
391 NavigationContext navigationContext = new NavigationContext();
392 try
393 {
394 return getNavigationCommand(facesContext, navigationContext, fromAction, outcome, null);
395 }
396 finally
397 {
398 navigationContext.finish(facesContext);
399 }
400 }
401
402 public NavigationCase getNavigationCommandFromGlobalNavigationCases(
403 FacesContext facesContext, String viewId, NavigationContext navigationContext,
404 String fromAction, String outcome)
405 {
406 Map<String, Set<NavigationCase>> casesMap = getNavigationCases();
407 NavigationCase navigationCase = null;
408
409 Set<? extends NavigationCase> casesSet;
410 if (viewId != null)
411 {
412 casesSet = casesMap.get(viewId);
413 if (casesSet != null)
414 {
415
416 navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
417 }
418 }
419
420 if (navigationCase == null)
421 {
422
423
424 List<_WildcardPattern> wildcardPatterns = getSortedWildcardPatterns();
425
426 for (int i = 0; i < wildcardPatterns.size(); i++)
427 {
428 _WildcardPattern wildcardPattern = wildcardPatterns.get(i);
429 if (wildcardPattern.match(viewId))
430 {
431 casesSet = casesMap.get(wildcardPattern.getPattern());
432 if (casesSet != null)
433 {
434 navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
435 if (navigationCase != null)
436 {
437 break;
438 }
439 }
440 }
441 }
442 }
443 return navigationCase;
444 }
445
446 private Flow calculateTargetFlow(FacesContext facesContext, String outcome,
447 FlowHandler flowHandler, List<Flow> activeFlows, String toFlowDocumentId)
448 {
449 Flow targetFlow = null;
450 if (toFlowDocumentId != null)
451 {
452 targetFlow = flowHandler.getFlow(facesContext, toFlowDocumentId, outcome);
453 }
454 if (targetFlow == null && !activeFlows.isEmpty())
455 {
456 for (Flow currentFlow : activeFlows)
457 {
458 targetFlow = flowHandler.getFlow(facesContext, currentFlow.getDefiningDocumentId(), outcome);
459 if (targetFlow != null)
460 {
461 break;
462 }
463 }
464 }
465 if (targetFlow == null)
466 {
467 targetFlow = flowHandler.getFlow(facesContext, "", outcome);
468 }
469 return targetFlow;
470 }
471
472 public NavigationCase getNavigationCommand(
473 FacesContext facesContext, NavigationContext navigationContext, String fromAction, String outcome,
474 String toFlowDocumentId)
475 {
476 String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
477 NavigationCase navigationCase = getNavigationCommandFromGlobalNavigationCases(
478 facesContext, viewId, navigationContext, fromAction, outcome);
479 if (outcome != null && navigationCase == null)
480 {
481 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
482 List<Flow> activeFlows = FlowHandlerImpl.getActiveFlows(facesContext, flowHandler);
483
484
485 Flow targetFlow = calculateTargetFlow(facesContext, outcome, flowHandler, activeFlows, toFlowDocumentId);
486 Flow currentFlow = navigationContext.getCurrentFlow(facesContext);
487 FlowCallNode targetFlowCallNode = null;
488 boolean startFlow = false;
489 String startFlowDocumentId = null;
490 String startFlowId = null;
491 boolean checkFlowNode = false;
492 String outcomeToGo = outcome;
493 String actionToGo = fromAction;
494
495 if (currentFlow != null)
496 {
497
498
499
500 if (targetFlow != null)
501 {
502 if (flowHandler.isActive(facesContext, targetFlow.getDefiningDocumentId(), targetFlow.getId()))
503 {
504
505
506 FlowNode flowNode = targetFlow.getNode(outcome);
507 if (flowNode != null)
508 {
509 checkFlowNode = true;
510 }
511 else
512 {
513 startFlow = true;
514 }
515 }
516 else
517 {
518 startFlow = true;
519 }
520 }
521 else
522 {
523
524 checkFlowNode = true;
525 }
526 }
527 else
528 {
529 if (targetFlow != null)
530 {
531
532 startFlow = true;
533 }
534 }
535 if (!startFlow)
536 {
537 for (Flow activeFlow : activeFlows)
538 {
539 FlowNode node = activeFlow.getNode(outcome);
540 if (node != null)
541 {
542 currentFlow = activeFlow;
543 break;
544 }
545 _FlowNavigationStructure flowNavigationStructure = _flowNavigationStructureMap.get(
546 activeFlow.getId());
547 navigationCase = getNavigationCaseFromFlowStructure(facesContext,
548 flowNavigationStructure, fromAction, outcome, viewId);
549 if (navigationCase != null)
550 {
551 currentFlow = activeFlow;
552 break;
553 }
554 }
555 }
556
557
558 if (startFlow || (checkFlowNode && currentFlow != null))
559 {
560 boolean complete = false;
561 boolean checkNavCase = true;
562
563 while (!complete && (startFlow || checkFlowNode))
564 {
565 if (startFlow)
566 {
567 if (flowHandler.isActive(facesContext, targetFlow.getDefiningDocumentId(), targetFlow.getId())
568 && targetFlowCallNode == null)
569 {
570
571 Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
572
573 while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
574 targetFlow.getDefiningDocumentId()) &&
575 baseReturnFlow.getId().equals(targetFlow.getId())) )
576 {
577 navigationContext.popFlow(facesContext);
578 baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
579 }
580 navigationContext.popFlow(facesContext);
581 currentFlow = navigationContext.getCurrentFlow(facesContext);
582 navigationContext.addTargetFlow(baseReturnFlow, currentFlow, null);
583 }
584 if (startFlowId == null)
585 {
586 startFlowDocumentId = targetFlow.getDefiningDocumentId();
587 startFlowId = targetFlowCallNode == null ? targetFlow.getId() : targetFlowCallNode.getId();
588 }
589 navigationContext.addTargetFlow(currentFlow, targetFlow, targetFlowCallNode);
590 targetFlowCallNode = null;
591
592
593 navigationContext.pushFlow(facesContext, targetFlow);
594 currentFlow = targetFlow;
595
596
597 outcomeToGo = resolveStartNodeOutcome(targetFlow);
598 checkFlowNode = true;
599 startFlow = false;
600 }
601 if (checkFlowNode)
602 {
603 FlowNode flowNode = currentFlow.getNode(outcomeToGo);
604 if (flowNode != null)
605 {
606 checkNavCase = true;
607 if (!complete && flowNode instanceof SwitchNode)
608 {
609 outcomeToGo = calculateSwitchOutcome(facesContext, (SwitchNode) flowNode);
610
611
612 actionToGo = currentFlow.getId();
613 flowNode = currentFlow.getNode(outcomeToGo);
614 continue;
615 }
616 if (!complete && flowNode instanceof FlowCallNode)
617 {
618
619 FlowCallNode flowCallNode = (FlowCallNode) flowNode;
620 targetFlow = calculateFlowCallTargetFlow(facesContext,
621 flowHandler, flowCallNode, currentFlow);
622 if (targetFlow != null)
623 {
624 targetFlowCallNode = flowCallNode;
625 startFlow = true;
626 continue;
627 }
628 else
629 {
630
631
632 complete = true;
633 }
634 }
635 if (!complete && flowNode instanceof MethodCallNode)
636 {
637 MethodCallNode methodCallNode = (MethodCallNode) flowNode;
638 String vdlViewIdentifier = calculateVdlViewIdentifier(facesContext, methodCallNode);
639
640 if (vdlViewIdentifier != null)
641 {
642 outcomeToGo = vdlViewIdentifier;
643 actionToGo = currentFlow.getId();
644 continue;
645 }
646 else
647 {
648 complete = true;
649 }
650 }
651 if (!complete && flowNode instanceof ReturnNode)
652 {
653 ReturnNode returnNode = (ReturnNode) flowNode;
654 String fromOutcome = returnNode.getFromOutcome(facesContext);
655 actionToGo = currentFlow.getId();
656 Flow sourceFlow = currentFlow;
657 Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
658
659 while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
660 currentFlow.getDefiningDocumentId()) &&
661 baseReturnFlow.getId().equals(currentFlow.getId())) )
662 {
663 navigationContext.popFlow(facesContext);
664 baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
665 }
666 navigationContext.popFlow(facesContext);
667 currentFlow = navigationContext.getCurrentFlow(facesContext);
668 navigationContext.addTargetFlow(sourceFlow, currentFlow, null);
669 outcomeToGo = fromOutcome;
670 String lastDisplayedViewId = navigationContext.getLastDisplayedViewId(facesContext,
671 currentFlow);
672
673
674
675
676
677
678 navigationCase = getNavigationCommand(facesContext,
679 navigationContext, actionToGo, outcomeToGo, FlowHandler.NULL_FLOW);
680 if (navigationCase != null)
681 {
682 navigationCase = new FlowNavigationCase(navigationCase,
683 flowNode.getId(), FlowHandler.NULL_FLOW);
684 complete = true;
685 }
686 else
687 {
688
689 if (lastDisplayedViewId != null)
690 {
691 navigationCase = createNavigationCase(
692 viewId, flowNode.getId(), lastDisplayedViewId, FlowHandler.NULL_FLOW);
693 complete = true;
694 }
695 }
696 if (currentFlow == null)
697 {
698 complete = true;
699 }
700 continue;
701 }
702 if (!complete && flowNode instanceof ViewNode)
703 {
704 ViewNode viewNode = (ViewNode) flowNode;
705 navigationCase = createNavigationCase(viewId, flowNode.getId(),
706 viewNode.getVdlDocumentId());
707 complete = true;
708 }
709 else
710 {
711
712 complete = true;
713 }
714 }
715 else if (checkNavCase)
716 {
717
718 _FlowNavigationStructure flowNavigationStructure = _flowNavigationStructureMap.get(
719 currentFlow.getId());
720 navigationCase = getNavigationCaseFromFlowStructure(facesContext,
721 flowNavigationStructure, actionToGo, outcomeToGo, viewId);
722
723
724
725
726 if (navigationCase != null)
727 {
728 outcomeToGo = navigationCase.getToViewId(facesContext);
729 checkNavCase = false;
730 }
731 else
732 {
733
734 complete = true;
735 }
736 }
737 else
738 {
739 complete = true;
740 }
741 }
742 }
743
744 if (outcomeToGo != null && navigationCase == null)
745 {
746 navigationCase = getOutcomeNavigationCase (facesContext, actionToGo, outcomeToGo);
747 }
748 }
749 if (startFlowId != null)
750 {
751 navigationCase = new FlowNavigationCase(navigationCase, startFlowId, startFlowDocumentId);
752 }
753 }
754 if (outcome != null && navigationCase == null)
755 {
756
757
758 navigationCase = getOutcomeNavigationCase (facesContext, fromAction, outcome);
759 }
760 if (outcome != null && navigationCase == null && !facesContext.isProjectStage(ProjectStage.Production))
761 {
762 final FacesMessage facesMessage = new FacesMessage("No navigation case match for viewId " + viewId +
763 ", action " + fromAction + " and outcome " + outcome);
764 facesMessage.setSeverity(FacesMessage.SEVERITY_WARN);
765 facesContext.addMessage(null, facesMessage);
766 }
767 if (navigationCase != null)
768 {
769 navigationContext.setNavigationCase(navigationCase);
770 }
771 return navigationContext.getNavigationCase();
772
773 }
774
775 private String resolveStartNodeOutcome(Flow targetFlow)
776 {
777 String outcomeToGo;
778 if (targetFlow.getStartNodeId() == null)
779 {
780
781
782
783 outcomeToGo = "/" + targetFlow.getId()+ "/" +
784 targetFlow.getId() + ".xhtml";
785 }
786 else
787 {
788 outcomeToGo = targetFlow.getStartNodeId();
789 }
790 return outcomeToGo;
791 }
792
793 private String calculateSwitchOutcome(FacesContext facesContext, SwitchNode switchNode)
794 {
795 String outcomeToGo = null;
796 boolean resolved = false;
797
798
799
800 for (SwitchCase switchCase : switchNode.getCases())
801 {
802 Boolean isConditionTrue = switchCase.getCondition(facesContext);
803 if (Boolean.TRUE.equals(isConditionTrue))
804 {
805 outcomeToGo = switchCase.getFromOutcome();
806 resolved = true;
807 break;
808 }
809 }
810 if (!resolved)
811 {
812 outcomeToGo = switchNode.getDefaultOutcome(facesContext);
813 }
814 return outcomeToGo;
815 }
816
817 private Flow calculateFlowCallTargetFlow(FacesContext facesContext, FlowHandler flowHandler,
818 FlowCallNode flowCallNode, Flow currentFlow)
819 {
820 Flow targetFlow = null;
821
822
823
824
825
826
827
828
829 String calledFlowDocumentId = flowCallNode.getCalledFlowDocumentId(facesContext);
830 if (calledFlowDocumentId == null)
831 {
832 calledFlowDocumentId = currentFlow.getDefiningDocumentId();
833 }
834 targetFlow = flowHandler.getFlow(facesContext,
835 calledFlowDocumentId,
836 flowCallNode.getCalledFlowId(facesContext));
837 if (targetFlow == null && !"".equals(calledFlowDocumentId))
838 {
839 targetFlow = flowHandler.getFlow(facesContext, "",
840 flowCallNode.getCalledFlowId(facesContext));
841 }
842 return targetFlow;
843 }
844
845 private String calculateVdlViewIdentifier(FacesContext facesContext, MethodCallNode methodCallNode)
846 {
847 String vdlViewIdentifier = null;
848 MethodExpression method = methodCallNode.getMethodExpression();
849 if (method != null)
850 {
851 Object value = invokeMethodCallNode(facesContext, methodCallNode);
852 if (value != null)
853 {
854 vdlViewIdentifier = value.toString();
855 }
856 else if (methodCallNode.getOutcome() != null)
857 {
858 vdlViewIdentifier = (String) methodCallNode.getOutcome().getValue(
859 facesContext.getELContext());
860 }
861 }
862 return vdlViewIdentifier;
863 }
864
865 private Object invokeMethodCallNode(FacesContext facesContext, MethodCallNode methodCallNode)
866 {
867 MethodExpression method = methodCallNode.getMethodExpression();
868 Object value = null;
869
870 if (methodCallNode.getParameters() != null &&
871 !methodCallNode.getParameters().isEmpty())
872 {
873 Object[] parameters = new Object[methodCallNode.getParameters().size()];
874 Class[] clazzes = new Class[methodCallNode.getParameters().size()];
875 for (int i = 0; i < methodCallNode.getParameters().size(); i++)
876 {
877 Parameter param = methodCallNode.getParameters().get(i);
878 parameters[i] = param.getValue().getValue(facesContext.getELContext());
879 clazzes[i] = param.getName() != null ?
880 ClassUtils.simpleJavaTypeToClass(param.getName()) :
881 (parameters[i] == null ? String.class : parameters[i].getClass());
882 }
883
884
885
886 method = facesContext.getApplication().getExpressionFactory().createMethodExpression(
887 facesContext.getELContext(), method.getExpressionString(), null, clazzes);
888 value = method.invoke(facesContext.getELContext(), parameters);
889 }
890 else
891 {
892 value = method.invoke(facesContext.getELContext(), null);
893 }
894 return value;
895 }
896
897 private NavigationCase getNavigationCaseFromFlowStructure(FacesContext facesContext,
898 _FlowNavigationStructure flowNavigationStructure, String fromAction, String outcome, String viewId)
899 {
900 Set<NavigationCase> casesSet = null;
901 NavigationCase navigationCase = null;
902
903 if (viewId != null)
904 {
905 casesSet = flowNavigationStructure.getNavigationCases().get(viewId);
906 if (casesSet != null)
907 {
908
909 navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
910 }
911 }
912 if (navigationCase == null)
913 {
914 List<_WildcardPattern> wildcardPatterns = flowNavigationStructure.getWildcardKeys();
915 for (int i = 0; i < wildcardPatterns.size(); i++)
916 {
917 _WildcardPattern wildcardPattern = wildcardPatterns.get(i);
918 if (wildcardPattern.match(viewId))
919 {
920 casesSet = flowNavigationStructure.getNavigationCases().get(wildcardPattern.getPattern());
921 if (casesSet != null)
922 {
923 navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
924 if (navigationCase != null)
925 {
926 break;
927 }
928 }
929 }
930 }
931 }
932 return navigationCase;
933 }
934
935
936
937
938
939
940
941 private NavigationCase createNavigationCase(String fromViewId, String outcome, String toViewId)
942 {
943 return new NavigationCase(fromViewId, null, outcome, null, toViewId, null, false, false);
944 }
945
946 private NavigationCase createNavigationCase(String fromViewId, String outcome,
947 String toViewId, String toFlowDocumentId)
948 {
949 return new NavigationCase(fromViewId, null, outcome, null, toViewId,
950 toFlowDocumentId, null, false, false);
951 }
952
953
954
955
956
957
958
959 private NavigationCase getOutcomeNavigationCase (FacesContext facesContext, String fromAction, String outcome)
960 {
961 String implicitViewId = null;
962 boolean includeViewParams = false;
963 int index;
964 boolean isRedirect = false;
965 String queryString = null;
966 NavigationCase result = null;
967 String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
968
969 StringBuilder viewIdToTest = SharedStringBuilder.get(facesContext, OUTCOME_NAVIGATION_SB);
970 viewIdToTest.append(outcome);
971
972
973 index = viewIdToTest.indexOf ("?");
974 if (index != -1)
975 {
976 queryString = viewIdToTest.substring (index + 1);
977
978 viewIdToTest.setLength(index);
979
980
981 if (queryString.indexOf ("faces-redirect=true") != -1)
982 {
983 isRedirect = true;
984 }
985
986
987
988 if (queryString.indexOf("includeViewParams=true") != -1
989 || queryString.indexOf("faces-include-view-params=true") != -1)
990 {
991 includeViewParams = true;
992 }
993 }
994
995
996 index = viewIdToTest.indexOf (".");
997 if (index == -1)
998 {
999 if (viewId != null)
1000 {
1001 index = viewId.lastIndexOf('.');
1002
1003 if (index != -1)
1004 {
1005
1006 viewIdToTest.append(viewId.substring (index));
1007 }
1008 }
1009 else
1010 {
1011
1012
1013
1014
1015
1016
1017
1018
1019 String tempViewId = getNavigationHandlerSupport().calculateViewId(facesContext);
1020 if (tempViewId != null)
1021 {
1022 index = tempViewId.lastIndexOf('.');
1023 if(index != -1)
1024 {
1025 viewIdToTest.append(tempViewId.substring (index));
1026 }
1027 }
1028 }
1029 if (log.isLoggable(Level.FINEST))
1030 {
1031 log.finest("getOutcomeNavigationCase -> viewIdToTest: " + viewIdToTest);
1032 }
1033 }
1034
1035
1036
1037
1038
1039 boolean startWithSlash = false;
1040 if (viewIdToTest.length() > 0)
1041 {
1042 startWithSlash = (viewIdToTest.charAt(0) == '/');
1043 }
1044 if (!startWithSlash)
1045 {
1046 index = -1;
1047 if( viewId != null )
1048 {
1049 index = viewId.lastIndexOf('/');
1050 }
1051
1052 if (index == -1)
1053 {
1054
1055 viewIdToTest.insert(0,"/");
1056 }
1057
1058 else
1059 {
1060
1061 viewIdToTest.insert(0, viewId, 0, index + 1);
1062 }
1063 }
1064
1065
1066 String viewIdToTestString = null;
1067 boolean applyNormalization = false;
1068
1069 for (int i = 0; i < viewIdToTest.length()-1; i++)
1070 {
1071 if (viewIdToTest.charAt(i) == '.' &&
1072 viewIdToTest.charAt(i+1) == '/')
1073 {
1074 applyNormalization = true;
1075 break;
1076 }
1077 }
1078 if (applyNormalization)
1079 {
1080 viewIdToTestString = FilenameUtils.normalize(viewIdToTest.toString(), true);
1081 }
1082 else
1083 {
1084 viewIdToTestString = viewIdToTest.toString();
1085 }
1086
1087
1088 try
1089 {
1090 implicitViewId = facesContext.getApplication().getViewHandler().deriveViewId (
1091 facesContext, viewIdToTestString);
1092 }
1093
1094 catch (UnsupportedOperationException e)
1095 {
1096
1097
1098
1099
1100 }
1101
1102 if (implicitViewId != null)
1103 {
1104
1105
1106 Map<String, List<String>> params = null;
1107 if (queryString != null && !"".equals(queryString))
1108 {
1109
1110 String[] splitQueryParams = AMP_PATTERN.split(queryString);
1111 params = new HashMap<String, List<String>>(splitQueryParams.length,
1112 (splitQueryParams.length* 4 + 3) / 3);
1113 for (String queryParam : splitQueryParams)
1114 {
1115 String[] splitParam = StringUtils.splitShortString(queryParam, '=');
1116 if (splitParam.length == 2)
1117 {
1118
1119 if ("includeViewParams".equals(splitParam[0])
1120 || "faces-include-view-params".equals(splitParam[0])
1121 || "faces-redirect".equals(splitParam[0]))
1122 {
1123
1124 continue;
1125 }
1126 List<String> paramValues = params.get(splitParam[0]);
1127 if (paramValues == null)
1128 {
1129
1130 paramValues = new ArrayList<String>();
1131 params.put(splitParam[0], paramValues);
1132 }
1133 paramValues.add(splitParam[1]);
1134 }
1135 else
1136 {
1137
1138 throw new FacesException("Invalid parameter \"" +
1139 queryParam + "\" in outcome " + outcome);
1140 }
1141 }
1142 }
1143
1144
1145 result = new NavigationCase (viewId, fromAction, outcome, null,
1146 implicitViewId, params, isRedirect, includeViewParams);
1147 }
1148
1149 return result;
1150 }
1151
1152
1153
1154
1155 public String getViewId(FacesContext context, String fromAction, String outcome)
1156 {
1157 return this.getNavigationCase(context, fromAction, outcome).getToViewId(context);
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167 public String beforeNavigation(String viewId)
1168 {
1169 return null;
1170 }
1171
1172 private NavigationCase calcMatchingNavigationCase(FacesContext context,
1173 Set<? extends NavigationCase> casesList,
1174 String actionRef,
1175 String outcome)
1176 {
1177 NavigationCase noConditionCase = null;
1178 NavigationCase firstCase = null;
1179 NavigationCase firstCaseIf = null;
1180 NavigationCase secondCase = null;
1181 NavigationCase secondCaseIf = null;
1182 NavigationCase thirdCase = null;
1183 NavigationCase thirdCaseIf = null;
1184 NavigationCase fourthCase = null;
1185 NavigationCase fourthCaseIf = null;
1186
1187 for (NavigationCase caze : casesList)
1188 {
1189 String cazeOutcome = caze.getFromOutcome();
1190 String cazeActionRef = caze.getFromAction();
1191 Boolean cazeIf = caze.getCondition(context);
1192 boolean ifMatches = (cazeIf == null ? false : cazeIf.booleanValue());
1193
1194
1195
1196 if(outcome == null && (cazeOutcome != null || cazeIf == null) && actionRef == null)
1197 {
1198
1199 continue;
1200 }
1201
1202
1203 if (cazeOutcome == null && cazeActionRef == null &&
1204 cazeIf == null && noConditionCase == null && outcome != null)
1205 {
1206 noConditionCase = caze;
1207 }
1208
1209 if (cazeActionRef != null)
1210 {
1211 if (cazeOutcome != null)
1212 {
1213 if ((actionRef != null) && (outcome != null) && cazeActionRef.equals (actionRef) &&
1214 cazeOutcome.equals (outcome))
1215 {
1216
1217
1218
1219 if (cazeIf != null)
1220 {
1221 if (ifMatches)
1222 {
1223 firstCaseIf = caze;
1224
1225 }
1226
1227 continue;
1228 }
1229 else
1230 {
1231 firstCase = caze;
1232
1233 }
1234 }
1235 }
1236 else
1237 {
1238 if ((actionRef != null) && cazeActionRef.equals (actionRef))
1239 {
1240
1241
1242
1243 if (cazeIf != null)
1244 {
1245 if (ifMatches)
1246 {
1247 thirdCaseIf = caze;
1248
1249 }
1250
1251 continue;
1252 }
1253 else
1254 {
1255 if (outcome != null)
1256 {
1257 thirdCase = caze;
1258
1259 }
1260
1261 continue;
1262 }
1263 }
1264 else
1265 {
1266
1267
1268
1269
1270 continue;
1271 }
1272 }
1273 }
1274 else
1275 {
1276 if (cazeOutcome != null && (outcome != null) && cazeOutcome.equals (outcome))
1277 {
1278
1279
1280
1281 if (cazeIf != null)
1282 {
1283 if (ifMatches)
1284 {
1285 secondCaseIf = caze;
1286
1287 }
1288
1289 continue;
1290 }
1291 else
1292 {
1293 secondCase = caze;
1294
1295 }
1296 }
1297 }
1298
1299
1300
1301 if (outcome != null && cazeIf != null)
1302 {
1303
1304 if (ifMatches)
1305 {
1306 fourthCaseIf = caze;
1307
1308 }
1309
1310 continue;
1311 }
1312
1313 if ((cazeIf != null) && ifMatches)
1314 {
1315 fourthCase = caze;
1316
1317 }
1318 }
1319
1320 if (firstCaseIf != null)
1321 {
1322 return firstCaseIf;
1323 }
1324 else if (firstCase != null)
1325 {
1326 return firstCase;
1327 }
1328 else if (secondCaseIf != null)
1329 {
1330 return secondCaseIf;
1331 }
1332 else if (secondCase != null)
1333 {
1334 return secondCase;
1335 }
1336 else if (thirdCaseIf != null)
1337 {
1338 return thirdCaseIf;
1339 }
1340 else if (thirdCase != null)
1341 {
1342 return thirdCase;
1343 }
1344 else if (fourthCaseIf != null)
1345 {
1346 return fourthCaseIf;
1347 }
1348 else if (fourthCase != null)
1349 {
1350 return fourthCase;
1351 }
1352
1353 return noConditionCase;
1354 }
1355
1356 private List<_WildcardPattern> getSortedWildcardPatterns()
1357 {
1358 return _wildcardPatterns;
1359 }
1360
1361 @Override
1362 public Map<String, Set<NavigationCase>> getNavigationCases()
1363 {
1364 if (_developmentStage == null)
1365 {
1366 _developmentStage = FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development);
1367 }
1368 if (!Boolean.TRUE.equals(_developmentStage))
1369 {
1370 if (_navigationCases == null)
1371 {
1372 FacesContext facesContext = FacesContext.getCurrentInstance();
1373 ExternalContext externalContext = facesContext.getExternalContext();
1374 RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
1375
1376 calculateNavigationCases(runtimeConfig);
1377 }
1378 return _navigationCases;
1379 }
1380 else
1381 {
1382 FacesContext facesContext = FacesContext.getCurrentInstance();
1383 ExternalContext externalContext = facesContext.getExternalContext();
1384 RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
1385
1386 if (_navigationCases == null || runtimeConfig.isNavigationRulesChanged())
1387 {
1388 calculateNavigationCases(runtimeConfig);
1389 }
1390 return _navigationCases;
1391 }
1392 }
1393
1394 @Override
1395 public void inspectFlow(FacesContext context, Flow flow)
1396 {
1397 Map<String, Set<NavigationCase>> rules = flow.getNavigationCases();
1398 int rulesSize = rules.size();
1399
1400 Map<String, Set<NavigationCase>> cases = new HashMap<String, Set<NavigationCase>>(
1401 HashMapUtils.calcCapacity(rulesSize));
1402
1403 List<_WildcardPattern> wildcardPatterns = new ArrayList<_WildcardPattern>();
1404
1405 for (Map.Entry<String, Set<NavigationCase>> entry : rules.entrySet())
1406 {
1407 String fromViewId = entry.getKey();
1408
1409
1410 if (fromViewId == null)
1411 {
1412 fromViewId = ASTERISK;
1413 }
1414 else
1415 {
1416 fromViewId = fromViewId.trim();
1417 }
1418
1419 Set<NavigationCase> set = cases.get(fromViewId);
1420 if (set == null)
1421 {
1422 set = new HashSet<NavigationCase>(entry.getValue());
1423 cases.put(fromViewId, set);
1424 if (fromViewId.endsWith(ASTERISK))
1425 {
1426 wildcardPatterns.add(new _WildcardPattern(fromViewId));
1427 }
1428 }
1429 else
1430 {
1431 set.addAll(entry.getValue());
1432 }
1433 }
1434
1435 Collections.sort(wildcardPatterns, new KeyComparator());
1436
1437 _flowNavigationStructureMap.put(
1438 flow.getId(),
1439 new _FlowNavigationStructure(flow.getDefiningDocumentId(), flow.getId(), cases, wildcardPatterns) );
1440 }
1441
1442 private synchronized void calculateNavigationCases(RuntimeConfig runtimeConfig)
1443 {
1444 if (_navigationCases == null || runtimeConfig.isNavigationRulesChanged())
1445 {
1446 Collection<? extends NavigationRule> rules = runtimeConfig.getNavigationRules();
1447 int rulesSize = rules.size();
1448
1449 Map<String, Set<NavigationCase>> cases = new HashMap<String, Set<NavigationCase>>(
1450 HashMapUtils.calcCapacity(rulesSize));
1451
1452 List<_WildcardPattern> wildcardPatterns = new ArrayList<_WildcardPattern>();
1453
1454 for (NavigationRule rule : rules)
1455 {
1456 String fromViewId = rule.getFromViewId();
1457
1458
1459 if (fromViewId == null)
1460 {
1461 fromViewId = ASTERISK;
1462 }
1463 else
1464 {
1465 fromViewId = fromViewId.trim();
1466 }
1467
1468 Set<NavigationCase> set = cases.get(fromViewId);
1469 if (set == null)
1470 {
1471 set = new HashSet<NavigationCase>(convertNavigationCasesToAPI(rule));
1472 cases.put(fromViewId, set);
1473 if (fromViewId.endsWith(ASTERISK))
1474 {
1475 wildcardPatterns.add(new _WildcardPattern(fromViewId));
1476 }
1477 }
1478 else
1479 {
1480 set.addAll(convertNavigationCasesToAPI(rule));
1481 }
1482 }
1483
1484 Collections.sort(wildcardPatterns, new KeyComparator());
1485
1486 synchronized (cases)
1487 {
1488
1489
1490
1491
1492 _navigationCases = cases;
1493 _wildcardPatterns = wildcardPatterns;
1494
1495 runtimeConfig.setNavigationRulesChanged(false);
1496 }
1497 }
1498 }
1499
1500 private static final class KeyComparator implements Comparator<_WildcardPattern>
1501 {
1502 public int compare(_WildcardPattern s1, _WildcardPattern s2)
1503 {
1504 return -s1.getPattern().compareTo(s2.getPattern());
1505 }
1506 }
1507
1508 private Set<NavigationCase> convertNavigationCasesToAPI(NavigationRule rule)
1509 {
1510 Collection<? extends org.apache.myfaces.config.element.NavigationCase> configCases = rule.getNavigationCases();
1511 Set<NavigationCase> apiCases = new HashSet<NavigationCase>(configCases.size());
1512
1513 for(org.apache.myfaces.config.element.NavigationCase configCase : configCases)
1514 {
1515 if(configCase.getRedirect() != null)
1516 {
1517 String includeViewParamsAttribute = configCase.getRedirect().getIncludeViewParams();
1518 boolean includeViewParams = false;
1519 if (includeViewParamsAttribute != null)
1520 {
1521 includeViewParams = Boolean.valueOf(includeViewParamsAttribute);
1522 }
1523 apiCases.add(new NavigationCase(rule.getFromViewId(),configCase.getFromAction(),
1524 configCase.getFromOutcome(),configCase.getIf(),configCase.getToViewId(),
1525 configCase.getRedirect().getViewParams(),true,includeViewParams));
1526 }
1527 else
1528 {
1529 apiCases.add(new NavigationCase(rule.getFromViewId(),configCase.getFromAction(),
1530 configCase.getFromOutcome(),configCase.getIf(),
1531 configCase.getToViewId(),null,false,false));
1532 }
1533 }
1534
1535 return apiCases;
1536 }
1537
1538
1539
1540
1541
1542
1543
1544
1545 protected static class NavigationContext
1546 {
1547 private NavigationCase navigationCase;
1548 private List<Flow> sourceFlows;
1549 private List<Flow> targetFlows;
1550 private List<FlowCallNode> targetFlowCallNodes;
1551 private List<Flow> currentFlows;
1552 private int returnCount = 0;
1553
1554 public NavigationContext()
1555 {
1556 }
1557
1558 public NavigationContext(NavigationCase navigationCase)
1559 {
1560 this.navigationCase = navigationCase;
1561 }
1562
1563 public NavigationCase getNavigationCase()
1564 {
1565 return navigationCase;
1566 }
1567
1568 public void setNavigationCase(NavigationCase navigationCase)
1569 {
1570 this.navigationCase = navigationCase;
1571 }
1572
1573 public List<Flow> getSourceFlows()
1574 {
1575 return sourceFlows;
1576 }
1577
1578 public List<Flow> getTargetFlows()
1579 {
1580 return targetFlows;
1581 }
1582
1583 public List<FlowCallNode> getFlowCallNodes()
1584 {
1585 return targetFlowCallNodes;
1586 }
1587
1588 public void addTargetFlow(Flow sourceFlow, Flow targetFlow, FlowCallNode flowCallNode)
1589 {
1590 if (targetFlows == null)
1591 {
1592 sourceFlows = new ArrayList<Flow>(4);
1593 targetFlows = new ArrayList<Flow>(4);
1594 targetFlowCallNodes = new ArrayList<FlowCallNode>(4);
1595 }
1596 this.sourceFlows.add(sourceFlow);
1597 this.targetFlows.add(targetFlow);
1598 this.targetFlowCallNodes.add(flowCallNode);
1599 }
1600
1601 public Flow getCurrentFlow(FacesContext facesContext)
1602 {
1603 if (currentFlows != null && !currentFlows.isEmpty())
1604 {
1605 return currentFlows.get(currentFlows.size()-1);
1606 }
1607 else
1608 {
1609 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
1610 return flowHandler.getCurrentFlow(facesContext);
1611 }
1612 }
1613
1614 public void finish(FacesContext facesContext)
1615 {
1616
1617 for (int i=0; i < returnCount; i++)
1618 {
1619 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
1620 flowHandler.popReturnMode(facesContext);
1621 }
1622 returnCount = 0;
1623 }
1624
1625 public void popFlow(FacesContext facesContext)
1626 {
1627 if (currentFlows != null && !currentFlows.isEmpty())
1628 {
1629 currentFlows.remove(currentFlows.size()-1);
1630 }
1631 else
1632 {
1633 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
1634 flowHandler.pushReturnMode(facesContext);
1635 returnCount++;
1636 }
1637 }
1638
1639 public void pushFlow(FacesContext facesContext, Flow flow)
1640 {
1641 if (currentFlows == null)
1642 {
1643 currentFlows = new ArrayList<Flow>();
1644 }
1645 currentFlows.add(flow);
1646 }
1647
1648 public String getLastDisplayedViewId(FacesContext facesContext, Flow flow)
1649 {
1650 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
1651 return flowHandler.getLastDisplayedViewId(facesContext);
1652 }
1653 }
1654 }