2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.vaadin
.ui
.navigation
;
11 import java
.util
.Arrays
;
12 import java
.util
.List
;
13 import java
.util
.Stack
;
15 import org
.apache
.commons
.lang3
.StringUtils
;
16 import org
.apache
.logging
.log4j
.LogManager
;
17 import org
.apache
.logging
.log4j
.Logger
;
18 import org
.springframework
.beans
.factory
.DisposableBean
;
19 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
20 import org
.springframework
.context
.ApplicationContext
;
21 import org
.springframework
.context
.annotation
.Lazy
;
22 import org
.vaadin
.spring
.events
.EventBus
;
23 import org
.vaadin
.spring
.events
.annotation
.EventBusListenerMethod
;
25 import com
.vaadin
.navigator
.ViewChangeListener
;
26 import com
.vaadin
.navigator
.ViewDisplay
;
27 import com
.vaadin
.spring
.annotation
.SpringView
;
28 import com
.vaadin
.spring
.annotation
.UIScope
;
29 import com
.vaadin
.spring
.navigator
.SpringNavigator
;
30 import com
.vaadin
.spring
.navigator
.SpringViewProvider
;
31 import com
.vaadin
.ui
.Field
;
32 import com
.vaadin
.ui
.UI
;
33 import com
.vaadin
.ui
.Window
;
35 import eu
.etaxonomy
.cdm
.vaadin
.event
.EditorActionContext
;
36 import eu
.etaxonomy
.vaadin
.mvp
.AbstractEditorPresenter
;
37 import eu
.etaxonomy
.vaadin
.mvp
.AbstractPopupEditor
;
38 import eu
.etaxonomy
.vaadin
.mvp
.ApplicationView
;
39 import eu
.etaxonomy
.vaadin
.ui
.UIInitializedEvent
;
40 import eu
.etaxonomy
.vaadin
.ui
.view
.DoneWithPopupEvent
;
41 import eu
.etaxonomy
.vaadin
.ui
.view
.PopEditorOpenedEvent
;
42 import eu
.etaxonomy
.vaadin
.ui
.view
.PopupView
;
45 public class NavigationManagerBean
extends SpringNavigator
implements NavigationManager
, DisposableBean
{
47 private static final long serialVersionUID
= 6599898650948333853L;
49 private static final Logger logger
= LogManager
.getLogger();
51 // injecting the viewDisplay as spring bean causes problems with older cdm vaadin code
52 // SingleComponentContainerViewDisplay for example can't be used
53 // the viewDisplay should be configurable per UI therefore it seems more elegant to
54 // let the UI pass the viewDisplay to the Navigator
56 private ViewDisplay viewDisplay
;
59 private SpringViewProvider viewProvider
;
62 private List
<ViewChangeListener
> viewChangeListeners
;
65 protected ApplicationContext applicationContext
;
67 protected EventBus
.UIEventBus uiEventBus
;
70 * if set the navigator will block all other views
71 * and exclusively navigates to the error view to indicate that the
74 private String uiDisabledErrorViewName
= null;
77 protected void setViewEventBus(EventBus
.UIEventBus uiEventBus
){
78 this.uiEventBus
= uiEventBus
;
79 uiEventBus
.subscribe(this);
83 // * This reference will cause the scoped PermissionDebugUtils being initialized.
84 // * It is not used in this class but attaches itself to the vaadin session
85 // * from where it will be accessible via VaadinUserHelper.fromSession()
87 // * <b>NOTE:</b> PermissionDebugUtils is only available if the spring profile "debug" is active,
90 // @Autowired(required=false)
91 // private PermissionDebugUtils permissionDebugUtils;
93 private PopupViewRegistration popupViewRegistration
;
95 private String defaultViewName
= null;
98 * Why UriFragmentManager must be initialized lazily:
100 * when the SpringVaadinServlet usually is being instantiated the ServletUIInitHandler(UIInitHandler).getBrowserDetailsUI(VaadinRequest, VaadinSession) method is called which will
101 * 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
102 * via Page.getCurrent() which is used in the UriFragmentManager constructor. The NavigationManagerBean is initialized with the WebapplicationContext, that is when the current ui is
103 * not yet available, therefore the UriFragmentManager must be initialized lazily.
107 private UriFragmentManager uriFragmentManager
;
110 // public void setUriFragmentManager(UriFragmentManager uriFragmentManager) {
111 // this.uriFragmentManager = uriFragmentManager;
115 public NavigationManagerBean() {
116 popupViewRegistration
= new PopupViewRegistration();
119 // private Collection<PopupView> popupViews = new HashSet<>();
121 // @Autowired(required=false)
122 // private void popUpViews(Collection<PopupView> popupViews){
123 // this.popupViews = popupViews;
124 // // popupViews.forEach(view -> this.popupViews.put(view.getClass(), view));
127 private <P
extends PopupView
> P
findPopupView(Class
<P
> type
){
128 P viewBean
= applicationContext
.getBean(type
);
129 if(viewBean
== null){
130 throw new NullPointerException("no popup-view bean of type " + type
.getName() + " found");
133 // return popupViews.stream().filter(p -> p.getClass().equals(type)).findFirst();
136 @EventBusListenerMethod
137 protected void onUIInitialized(UIInitializedEvent e
) {
138 init(UI
.getCurrent(), uriFragmentManager
, viewDisplay
);
139 addProvider(viewProvider
);
140 viewChangeListeners
.forEach(vcl
-> addViewChangeListener(vcl
));
143 public void navigateTo(String navigationState
, boolean fireNavigationEvent
) {
144 if(getUiDisabledErrorView() != null) {
145 super.navigateTo(getUiDisabledErrorView());
147 if(StringUtils
.isEmpty(navigationState
)){
148 navigationState
= defaultViewName
;
150 if (fireNavigationEvent
) {
151 navigateTo(navigationState
);
153 super.navigateTo(navigationState
);
158 public void navigateTo(String navigationState
) {
159 if(getUiDisabledErrorView() != null) {
160 super.navigateTo(getUiDisabledErrorView());
162 if(StringUtils
.isEmpty(navigationState
)){
163 navigationState
= defaultViewName
;
165 super.navigateTo(navigationState
);
166 //eventBus.publishEvent(new NavigationEvent(navigationState));
169 @EventBusListenerMethod
170 protected void onNavigationEvent(NavigationEvent e
) {
171 navigateTo(e
.getViewName(), false);
175 public <T
extends PopupView
> T
showInPopup(Class
<T
> popupType
, ApplicationView parentView
, Field
<?
> targetField
) {
177 PopupView popupView
= findPopupView(popupType
);
179 if(AbstractPopupEditor
.class.isAssignableFrom(popupView
.getClass())){
180 if(parentView
instanceof AbstractPopupEditor
){
181 // retain the chain of EditorActionContexts when starting a new pupupEditor
182 Stack
<EditorActionContext
> parentEditorActionContext
= ((AbstractPopupEditor
)parentView
).getEditorActionContext();
183 ((AbstractPopupEditor
)popupView
).setParentEditorActionContext(parentEditorActionContext
, targetField
);
187 Window window
= new Window();
188 window
.setCaption(popupView
.getWindowCaption());
190 window
.setResizable(popupView
.isResizable());
191 // due to issue #6673 (https://dev.e-taxonomy.eu/redmine/issues/6673) popup editors must be modal!
192 //window.setModal(popupView.isModal());
193 window
.setModal(true);
194 window
.setCaptionAsHtml(popupView
.isWindowCaptionAsHtml());
195 window
.setWidth(popupView
.getWindowWidth(), popupView
.getWindowWidthUnit());
196 window
.setHeight(popupView
.getWindowHeight(), popupView
.getWindowHeightUnit());
197 window
.setContent(popupView
.asComponent());
198 // TODO need to disallow pressing the close [x] button:
199 // since window.addCloseListener(e -> popupView.cancel()); will
200 // cause sending cancel events even if save has been clicked
201 window
.setClosable(popupView
.isClosable());
202 UI
.getCurrent().addWindow(window
);
203 popupView
.viewEntered();
204 popupView
.focusFirst();
205 uiEventBus
.publish(this, new PopEditorOpenedEvent(this, popupView
));
207 popupViewRegistration
.put(window
, parentView
, popupView
, targetField
);
209 return (T
) popupView
;
213 public Field
<?
> targetFieldOf(PopupView popupView
){
214 return popupViewRegistration
.get(popupView
);
217 @EventBusListenerMethod
218 protected void onDoneWithTheEditor(DoneWithPopupEvent event
) {
220 PopupView popup
= event
.getPopup();
221 if(DisposableBean
.class.isAssignableFrom(popup
.getClass())){
223 ((DisposableBean
)popup
).destroy();
224 } catch (Exception e
) {
228 Window window
= popupViewRegistration
.getWindow(popup
);
229 if (window
!= null) {
231 popupViewRegistration
.remove(popup
);
233 if(AbstractPopupEditor
.class.isAssignableFrom(popup
.getClass())){
234 ((AbstractPopupEditor
)popup
).presenter().unsubscribeFromEventBuses();
243 public void reloadCurrentView() {
244 if(logger
.isTraceEnabled()){
245 logger
.trace("reloading " + getState());
247 navigateTo(getState(), false);
251 * This method requires that the {@SpringView} annotation is used to set the name of the <code>View</code>.
253 * @return the current view name or <code>null</code>
256 public String
getCurrentViewName() {
257 if(getCurrentView() != null){
258 SpringView springViewAnnotation
= getCurrentView().getClass().getAnnotation(SpringView
.class);
259 if(springViewAnnotation
!= null){
260 return springViewAnnotation
.name();
267 public List
<String
> getCurrentViewParameters(){
268 String substate
= getState();
269 String currentViewName
= getCurrentViewName();
270 if(currentViewName
!= null){
271 substate
= substate
.replaceAll("^" + currentViewName
+ "/?", "");
274 return Arrays
.asList(substate
.split("/"));
281 public List
<AbstractEditorPresenter
<?
, ?
>> getPopupEditorPresenters() {
282 // TODO Auto-generated method stub
287 * @return the defaultViewName
289 public String
getDefaultViewName() {
290 return defaultViewName
;
294 * @param defaultViewName the defaultViewName to set
296 public void setDefaultViewName(String defaultViewName
) {
297 this.defaultViewName
= defaultViewName
;
300 public void setViewDisplay(ViewDisplay viewDisplay
){
301 this.viewDisplay
= viewDisplay
;
305 * implementation of the interface {@link DisposableBean} and overrides the
306 * method in {@link Navigator} which is not an implementation of {@link DisposableBean}
310 public void destroy() {
312 uiEventBus
.unsubscribe(this);
313 popupViewRegistration
= null;
314 // release the reference to the view kept in currentView
315 // which could be expensive
316 // by navigating to the default view
317 navigateTo(defaultViewName
);
320 public String
getUiDisabledErrorView() {
321 return uiDisabledErrorViewName
;
324 public void setUiDisabledErrorView(String uiDisabledErrorViewName
) {
325 this.uiDisabledErrorViewName
= uiDisabledErrorViewName
;