View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.flow;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import javax.el.MethodExpression;
29  import javax.faces.application.NavigationCase;
30  import javax.faces.context.FacesContext;
31  import javax.faces.flow.Flow;
32  import javax.faces.flow.FlowCallNode;
33  import javax.faces.flow.FlowNode;
34  import javax.faces.flow.MethodCallNode;
35  import javax.faces.flow.Parameter;
36  import javax.faces.flow.ReturnNode;
37  import javax.faces.flow.SwitchNode;
38  import javax.faces.flow.ViewNode;
39  import javax.faces.lifecycle.ClientWindow;
40  
41  /**
42   *
43   * @since 2.2
44   * @author Leonardo Uribe
45   */
46  public class FlowImpl extends Flow implements Freezable
47  {
48      private MethodExpression _initializer;
49      private MethodExpression _finalizer;
50      private String _startNodeId;
51      private String _id;
52      private String _definingDocumentId;
53      
54      private Map<String, FlowNode> _flowNodeMap;
55      
56      // The idea is use a normal HashMap, since there will not be modifications
57      // after initialization ( all setters must call checkInitialized() )
58      private Map<String, Parameter> _inboundParametersMap;
59      private Map<String, FlowCallNode> _flowCallsMap;
60      private List<MethodCallNode> _methodCallsList;
61      private Map<String, ReturnNode> _returnsMap;
62      private Map<String, SwitchNode> _switchesMap;
63      private List<ViewNode> _viewsList;
64      
65      // Note this class should be thread safe and inmutable once
66      // the flow is initialized or placed into service by the runtime.
67      private Map<String, Parameter> _unmodifiableInboundParametersMap;
68      private Map<String, FlowCallNode> _unmodifiableFlowCallsMap;
69      private List<MethodCallNode> _unmodifiableMethodCallsList;
70      private Map<String, ReturnNode> _unmodifiableReturnsMap;
71      private Map<String, SwitchNode> _unmodifiableSwitchesMap;
72      private List<ViewNode> _unmodifiableViewsList;
73      
74      private Map<String, Set<NavigationCase>> _navigationCases;
75      private Map<String, Set<NavigationCase>> _unmodifiableNavigationCases;
76      
77      // No need to make it volatile, because FlowImpl instances are
78      // created and initialized only at application startup, by a single
79      // thread.
80      private boolean _initialized;
81      
82      public FlowImpl()
83      {
84          _flowNodeMap = new HashMap<String, FlowNode>();
85          _inboundParametersMap = new HashMap<String, Parameter>();
86          _flowCallsMap = new HashMap<String, FlowCallNode>();
87          _methodCallsList = new ArrayList<MethodCallNode>();
88          _returnsMap = new HashMap<String, ReturnNode>();
89          _switchesMap = new HashMap<String, SwitchNode>();
90          _viewsList = new ArrayList<ViewNode>();
91          _navigationCases = new HashMap<String, Set<NavigationCase>>();
92          
93          // Collections.unmodifiableMap(...) uses delegation pattern, so as long
94          // as we don't modify _inboundParametersMap in the wrong time, it
95          // will be thread safe and inmutable.
96          _unmodifiableInboundParametersMap = Collections.unmodifiableMap(_inboundParametersMap);
97          _unmodifiableFlowCallsMap = Collections.unmodifiableMap(_flowCallsMap);
98          _unmodifiableMethodCallsList = Collections.unmodifiableList(_methodCallsList);
99          _unmodifiableReturnsMap = Collections.unmodifiableMap(_returnsMap);
100         _unmodifiableSwitchesMap = Collections.unmodifiableMap(_switchesMap);
101         _unmodifiableViewsList = Collections.unmodifiableList(_viewsList);
102         
103         _unmodifiableNavigationCases = Collections.unmodifiableMap(_navigationCases);
104     }
105     
106     public void freeze()
107     {
108         
109         
110         _initialized = true;
111         
112         for (Map.Entry<String, Parameter> entry : _inboundParametersMap.entrySet())
113         {
114             if (entry.getValue() instanceof Freezable)
115             {
116                 ((Freezable)entry.getValue()).freeze();
117             }
118         }
119             
120         for (Map.Entry<String, FlowCallNode> entry : _flowCallsMap.entrySet())
121         {
122             if (entry.getValue() instanceof Freezable)
123             {
124                 ((Freezable)entry.getValue()).freeze();
125             }
126         }
127 
128         for (MethodCallNode value : _methodCallsList)
129         {
130             if (value instanceof Freezable)
131             {
132                 ((Freezable)value).freeze();
133             }
134         }
135 
136         for (Map.Entry<String, ReturnNode> entry : _returnsMap.entrySet())
137         {
138             if (entry.getValue() instanceof Freezable)
139             {
140                 ((Freezable)entry.getValue()).freeze();
141             }
142         }
143 
144         for (Map.Entry<String, SwitchNode> entry : _switchesMap.entrySet())
145         {
146             if (entry.getValue() instanceof Freezable)
147             {
148                 ((Freezable)entry.getValue()).freeze();
149             }
150         }
151         
152         for (ViewNode value : _viewsList)
153         {
154             if (value instanceof Freezable)
155             {
156                 ((Freezable)value).freeze();
157             }
158         }
159     }
160 
161     @Override
162     public String getClientWindowFlowId(ClientWindow curWindow)
163     {
164         String id = getId();
165         String documentId = getDefiningDocumentId();
166         // Faces Flow relies on ClientWindow feature, so it should be enabled,
167         // and the expected id cannot be null.
168         String windowId = curWindow.getId();
169         StringBuilder sb = new StringBuilder( id.length() + 1 + windowId.length() );
170         sb.append(windowId).append('_').append(documentId).append('_').append(id);
171         return sb.toString();
172     }
173 
174     @Override
175     public String getDefiningDocumentId()
176     {
177         return _definingDocumentId;
178     }
179     
180     public void setDefiningDocumentId(String definingDocumentId)
181     {
182         checkInitialized();
183         _definingDocumentId = definingDocumentId;
184     }
185 
186     @Override
187     public String getId()
188     {
189         return _id;
190     }
191     
192     public void setId(String id)
193     {
194         checkInitialized();
195         _id = id;
196     }
197 
198     @Override
199     public MethodExpression getInitializer()
200     {
201         return _initializer;
202     }
203     
204     public void setInitializer(MethodExpression initializer)
205     {
206         checkInitialized();
207         _initializer = initializer;
208     }
209 
210     @Override
211     public MethodExpression getFinalizer()
212     {
213         return _finalizer;
214     }
215     
216     public void setFinalizer(MethodExpression finalizer)
217     {
218         checkInitialized();
219         _finalizer = finalizer;
220     }
221 
222     @Override
223     public String getStartNodeId()
224     {
225         return _startNodeId;
226     }
227     
228     public void setStartNodeId(String startNodeId)
229     {
230         checkInitialized();
231         _startNodeId = startNodeId;
232     }
233     
234     @Override
235     public Map<String, Parameter> getInboundParameters()
236     {
237         return _unmodifiableInboundParametersMap;
238     }
239     
240     public void putInboundParameter(String key, Parameter value)
241     {
242         checkInitialized();
243         _inboundParametersMap.put(key, value);
244     }
245     
246     @Override
247     public Map<String, FlowCallNode> getFlowCalls()
248     {
249         return _unmodifiableFlowCallsMap;
250     }
251     
252     public void putFlowCall(String key, FlowCallNode value)
253     {
254         checkInitialized();
255         _flowCallsMap.put(key, value);
256         _flowNodeMap.put(value.getId(), value);
257     }
258 
259     @Override
260     public List<MethodCallNode> getMethodCalls()
261     {
262         return _unmodifiableMethodCallsList;
263     }
264 
265     public void addMethodCall(MethodCallNode value)
266     {
267         checkInitialized();
268         _methodCallsList.add(value);
269         _flowNodeMap.put(value.getId(), value);
270     }
271 
272     @Override
273     public Map<String, ReturnNode> getReturns()
274     {
275         return _unmodifiableReturnsMap;
276     }
277     
278     public void putReturn(String key, ReturnNode value)
279     {
280         checkInitialized();
281         _returnsMap.put(key, value);
282         _flowNodeMap.put(value.getId(), value);
283     }
284 
285     @Override
286     public Map<String, SwitchNode> getSwitches()
287     {
288         return _unmodifiableSwitchesMap;
289     }
290     
291     public void putSwitch(String key, SwitchNode value)
292     {
293         checkInitialized();
294         _switchesMap.put(key, value);
295         _flowNodeMap.put(value.getId(), value);
296     }
297 
298     @Override
299     public List<ViewNode> getViews()
300     {
301         return _unmodifiableViewsList;
302     }
303     
304     public void addView(ViewNode value)
305     {
306         checkInitialized();
307         _viewsList.add(value);
308         _flowNodeMap.put(value.getId(), value);
309     }
310 
311     @Override
312     public FlowCallNode getFlowCall(Flow targetFlow)
313     {
314         FacesContext facesContext = null;
315         for (Map.Entry<String, FlowCallNode> entry : _flowCallsMap.entrySet())
316         {
317             if (facesContext == null)
318             {
319                 facesContext = FacesContext.getCurrentInstance();
320             }
321             String calledDocumentId = entry.getValue().getCalledFlowDocumentId(facesContext);
322             String calledFlowId = entry.getValue().getCalledFlowId(facesContext);
323             if (targetFlow.getDefiningDocumentId().equals(calledDocumentId) &&
324                 targetFlow.getId().equals(calledFlowId) )
325             {
326                 return entry.getValue();
327             }
328         }
329         return null;
330     }
331     
332     @Override
333     public FlowNode getNode(String nodeId)
334     {
335         return _flowNodeMap.get(nodeId);
336     }
337     
338     public void addNavigationCases(String fromViewId, Set<NavigationCase> navigationCases)
339     {
340         checkInitialized();
341         Set<NavigationCase> navigationCaseSet = _navigationCases.get(fromViewId);
342         if (navigationCaseSet == null)
343         {
344             navigationCaseSet = new HashSet<NavigationCase>();
345             _navigationCases.put(fromViewId, navigationCaseSet);
346         }
347         navigationCaseSet.addAll(navigationCases);
348     }
349     
350     public void addNavigationCase(NavigationCase navigationCase)
351     {
352         checkInitialized();
353         Set<NavigationCase> navigationCaseSet = _navigationCases.get(navigationCase.getFromViewId());
354         if (navigationCaseSet == null)
355         {
356             navigationCaseSet = new HashSet<NavigationCase>();
357             _navigationCases.put(navigationCase.getFromViewId(), navigationCaseSet);
358         }
359         navigationCaseSet.add(navigationCase);
360     }
361     
362     public void removeNavigationCase(NavigationCase navigationCase)
363     {
364         checkInitialized();
365         Set<NavigationCase> navigationCaseSet = _navigationCases.get(navigationCase.getFromViewId());
366         if (navigationCaseSet == null)
367         {
368             return;
369         }
370         navigationCaseSet.remove(navigationCase);
371     }
372 
373     private void checkInitialized() throws IllegalStateException
374     {
375         if (_initialized)
376         {
377             throw new IllegalStateException("Flow is inmutable once initialized");
378         }
379     }
380 
381     @Override
382     public Map<String, Set<NavigationCase>> getNavigationCases()
383     {
384         return _unmodifiableNavigationCases;
385     }
386     
387 }