0c0d5d8fe8edb635aa39ce371a51e822291fad79
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / vaadin / ui / navigation / NavigationManagerBean.java
1 package eu.etaxonomy.vaadin.ui.navigation;
2
3 import java.util.Arrays;
4 import java.util.List;
5 import java.util.Stack;
6
7 import org.apache.commons.lang3.StringUtils;
8 import org.apache.log4j.Logger;
9 import org.springframework.beans.factory.DisposableBean;
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.context.ApplicationContext;
12 import org.springframework.context.annotation.Lazy;
13 import org.vaadin.spring.events.EventBus;
14 import org.vaadin.spring.events.annotation.EventBusListenerMethod;
15
16 import com.vaadin.navigator.ViewChangeListener;
17 import com.vaadin.navigator.ViewDisplay;
18 import com.vaadin.spring.annotation.SpringView;
19 import com.vaadin.spring.annotation.UIScope;
20 import com.vaadin.spring.navigator.SpringNavigator;
21 import com.vaadin.spring.navigator.SpringViewProvider;
22 import com.vaadin.ui.Field;
23 import com.vaadin.ui.UI;
24 import com.vaadin.ui.Window;
25
26 import eu.etaxonomy.cdm.vaadin.event.EditorActionContext;
27 import eu.etaxonomy.vaadin.mvp.AbstractEditorPresenter;
28 import eu.etaxonomy.vaadin.mvp.AbstractPopupEditor;
29 import eu.etaxonomy.vaadin.mvp.ApplicationView;
30 import eu.etaxonomy.vaadin.ui.UIInitializedEvent;
31 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
32 import eu.etaxonomy.vaadin.ui.view.PopEditorOpenedEvent;
33 import eu.etaxonomy.vaadin.ui.view.PopupView;
34
35 @UIScope
36 public class NavigationManagerBean extends SpringNavigator implements NavigationManager, DisposableBean {
37
38 private static final long serialVersionUID = 6599898650948333853L;
39
40 private final static Logger logger = Logger.getLogger(NavigationManagerBean.class);
41
42 // injecting the viewDisplay as spring bean causes problems with older cdm vaadin code
43 // SingleComponentContainerViewDisplay for example can't be used
44 // the viewDisplay should be configurable per UI therefore it seems more elegant to
45 // let the UI pass the viewDisplay to the Navigator
46 // @Autowired
47 private ViewDisplay viewDisplay;
48
49 @Autowired
50 private SpringViewProvider viewProvider;
51
52 @Autowired
53 private List<ViewChangeListener> viewChangeListeners;
54
55 @Autowired
56 protected ApplicationContext applicationContext;
57
58 protected EventBus.UIEventBus uiEventBus;
59
60 /**
61 * if set the navigator will block all other views
62 * and exclusively navigates to the error view to indicate that the
63 * UI is disabled.
64 */
65 private String uiDisabledErrorViewName = null;
66
67 @Autowired
68 protected void setViewEventBus(EventBus.UIEventBus uiEventBus){
69 this.uiEventBus = uiEventBus;
70 uiEventBus.subscribe(this);
71 }
72
73 // /**
74 // * This reference will cause the scoped PermissionDebugUtils being initialized.
75 // * It is not used in this class but attaches itself to the vaadin session
76 // * from where it will be accessible via VaadinUserHelper.fromSession()
77 // *
78 // * <b>NOTE:</b> PermissionDebugUtils is only available if the spring profile "debug" is active,
79 // * See
80 // */
81 // @Autowired(required=false)
82 // private PermissionDebugUtils permissionDebugUtils;
83
84 private PopupViewRegistration popupViewRegistration;
85
86 private String defaultViewName = null;
87
88 /*
89 * Why UriFragmentManager must be initialized lazily:
90 *
91 * when the SpringVaadinServlet usually is being instantiated the ServletUIInitHandler(UIInitHandler).getBrowserDetailsUI(VaadinRequest, VaadinSession) method is called which will
92 * first cause the WebapplicationContext being created. Once this is done the initialization of the UI classes is completed. This means that the UI classes are not readily available
93 * via Page.getCurrent() which is used in the UriFragmentManager constructor. The NavigationManagerBean is initialized with the WebapplicationContext, that is when the current ui is
94 * not yet available, therefore the UriFragmentManager must be initialized lazily.
95 */
96 @Autowired
97 @Lazy
98 private UriFragmentManager uriFragmentManager;
99
100
101 // public void setUriFragmentManager(UriFragmentManager uriFragmentManager) {
102 // this.uriFragmentManager = uriFragmentManager;
103 // }
104
105
106 public NavigationManagerBean() {
107 popupViewRegistration = new PopupViewRegistration();
108 }
109
110 // private Collection<PopupView> popupViews = new HashSet<>();
111 // @Lazy
112 // @Autowired(required=false)
113 // private void popUpViews(Collection<PopupView> popupViews){
114 // this.popupViews = popupViews;
115 // // popupViews.forEach(view -> this.popupViews.put(view.getClass(), view));
116 // }
117
118 private <P extends PopupView> P findPopupView(Class<P> type){
119 P viewBean = applicationContext.getBean(type);
120 if(viewBean == null){
121 throw new NullPointerException("no popup-view bean of type " + type.getName() + " found");
122 }
123 return viewBean;
124 // return popupViews.stream().filter(p -> p.getClass().equals(type)).findFirst();
125 }
126
127 @EventBusListenerMethod
128 protected void onUIInitialized(UIInitializedEvent e) {
129 init(UI.getCurrent(), uriFragmentManager, viewDisplay);
130 addProvider(viewProvider);
131 viewChangeListeners.forEach(vcl -> addViewChangeListener(vcl));
132 }
133
134 public void navigateTo(String navigationState, boolean fireNavigationEvent) {
135 if(getUiDisabledErrorView() != null) {
136 super.navigateTo(getUiDisabledErrorView());
137 }
138 if(StringUtils.isEmpty(navigationState)){
139 navigationState = defaultViewName;
140 }
141 if (fireNavigationEvent) {
142 navigateTo(navigationState);
143 } else {
144 super.navigateTo(navigationState);
145 }
146 }
147
148 @Override
149 public void navigateTo(String navigationState) {
150 if(getUiDisabledErrorView() != null) {
151 super.navigateTo(getUiDisabledErrorView());
152 }
153 if(StringUtils.isEmpty(navigationState)){
154 navigationState = defaultViewName;
155 }
156 super.navigateTo(navigationState);
157 //eventBus.publishEvent(new NavigationEvent(navigationState));
158 }
159
160 @EventBusListenerMethod
161 protected void onNavigationEvent(NavigationEvent e) {
162 navigateTo(e.getViewName(), false);
163 }
164
165 @Override
166 public <T extends PopupView> T showInPopup(Class<T> popupType, ApplicationView parentView, Field<?> targetField) {
167
168 PopupView popupView = findPopupView(popupType);
169
170 if(AbstractPopupEditor.class.isAssignableFrom(popupView.getClass())){
171 if(parentView instanceof AbstractPopupEditor){
172 // retain the chain of EditorActionContexts when starting a new pupupEditor
173 Stack<EditorActionContext> parentEditorActionContext = ((AbstractPopupEditor)parentView).getEditorActionContext();
174 ((AbstractPopupEditor)popupView).setParentEditorActionContext(parentEditorActionContext, targetField);
175 }
176 }
177
178 Window window = new Window();
179 window.setCaption(popupView.getWindowCaption());
180 window.center();
181 window.setResizable(popupView.isResizable());
182 // due to issue #6673 (https://dev.e-taxonomy.eu/redmine/issues/6673) popup editors must be modal!
183 //window.setModal(popupView.isModal());
184 window.setModal(true);
185 window.setCaptionAsHtml(popupView.isWindowCaptionAsHtml());
186 window.setWidth(popupView.getWindowWidth(), popupView.getWindowWidthUnit());
187 window.setHeight(popupView.getWindowHeight(), popupView.getWindowHeightUnit());
188 window.setContent(popupView.asComponent());
189 // TODO need to disallow pressing the close [x] button:
190 // since window.addCloseListener(e -> popupView.cancel()); will
191 // cause sending cancel events even if save has been clicked
192 window.setClosable(popupView.isClosable());
193 UI.getCurrent().addWindow(window);
194 popupView.viewEntered();
195 popupView.focusFirst();
196 uiEventBus.publish(this, new PopEditorOpenedEvent(this, popupView));
197
198 popupViewRegistration.put(window, parentView, popupView, targetField);
199
200 return (T) popupView;
201 }
202
203 @Override
204 public Field<?> targetFieldOf(PopupView popupView){
205 return popupViewRegistration.get(popupView);
206 }
207
208 @EventBusListenerMethod
209 protected void onDoneWithTheEditor(DoneWithPopupEvent event) {
210
211 PopupView popup = event.getPopup();
212 if(DisposableBean.class.isAssignableFrom(popup.getClass())){
213 try {
214 ((DisposableBean)popup).destroy();
215 } catch (Exception e) {
216 logger.error(e);
217 }
218 }
219 Window window = popupViewRegistration.getWindow(popup);
220 if (window != null) {
221 window.close();
222 popupViewRegistration.remove(popup);
223 }
224 if(AbstractPopupEditor.class.isAssignableFrom(popup.getClass())){
225 ((AbstractPopupEditor)popup).presenter().unsubscribeFromEventBuses();
226 }
227
228 }
229
230 /**
231 * {@inheritDoc}
232 */
233 @Override
234 public void reloadCurrentView() {
235 if(logger.isTraceEnabled()){
236 logger.trace("reloading " + getState());
237 }
238 navigateTo(getState(), false);
239 }
240
241 /**
242 * This method requires that the {@SpringView} annotation is used to set the name of the <code>View</code>.
243 *
244 * @return the current view name or <code>null</code>
245 */
246 @Override
247 public String getCurrentViewName() {
248 if(getCurrentView() != null){
249 SpringView springViewAnnotation = getCurrentView().getClass().getAnnotation(SpringView.class);
250 if(springViewAnnotation != null){
251 return springViewAnnotation.name();
252 }
253 }
254 return null;
255 }
256
257 @Override
258 public List<String> getCurrentViewParameters(){
259 String substate = getState();
260 String currentViewName = getCurrentViewName();
261 if(currentViewName != null){
262 substate = substate.replaceAll("^" + currentViewName + "/?", "");
263
264 }
265 return Arrays.asList(substate.split("/"));
266 }
267
268 /**
269 * {@inheritDoc}
270 */
271 @Override
272 public List<AbstractEditorPresenter<?, ?>> getPopupEditorPresenters() {
273 // TODO Auto-generated method stub
274 return null;
275 }
276
277 /**
278 * @return the defaultViewName
279 */
280 public String getDefaultViewName() {
281 return defaultViewName;
282 }
283
284 /**
285 * @param defaultViewName the defaultViewName to set
286 */
287 public void setDefaultViewName(String defaultViewName) {
288 this.defaultViewName = defaultViewName;
289 }
290
291 public void setViewDisplay(ViewDisplay viewDisplay){
292 this.viewDisplay = viewDisplay;
293 }
294
295 /**
296 * implementation of the interface {@link DisposableBean} and overrides the
297 * method in {@link Navigator} which is not an implementation of {@link DisposableBean}
298 * in this class.
299 */
300 @Override
301 public void destroy() {
302 super.destroy();
303 uiEventBus.unsubscribe(this);
304 popupViewRegistration = null;
305 // release the reference to the view kept in currentView
306 // which could be expensive
307 // by navigating to the default view
308 navigateTo(defaultViewName);
309 }
310
311 public String getUiDisabledErrorView() {
312 return uiDisabledErrorViewName;
313 }
314
315 public void setUiDisabledErrorView(String uiDisabledErrorViewName) {
316 this.uiDisabledErrorViewName = uiDisabledErrorViewName;
317 }
318 }