3c8172633b8cb16073de6c85aa24d2e6e9f1b8e2
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / vaadin / mvp / AbstractPresenter.java
1 package eu.etaxonomy.vaadin.mvp;
2
3 import java.io.Serializable;
4
5 import org.apache.log4j.Logger;
6 import org.springframework.beans.factory.DisposableBean;
7 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.beans.factory.annotation.Qualifier;
9 import org.springframework.security.core.context.SecurityContext;
10 import org.springframework.security.core.context.SecurityContextHolder;
11 import org.vaadin.spring.events.EventBus;
12
13 import com.vaadin.ui.Field;
14
15 import eu.etaxonomy.cdm.api.application.CdmRepository;
16 import eu.etaxonomy.cdm.vaadin.event.AbstractEditorAction;
17 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
18 import eu.etaxonomy.vaadin.ui.navigation.NavigationManager;
19 import eu.etaxonomy.vaadin.ui.view.PopupView;
20
21 /**
22 * AbstractPresenter is the base class of all presenter components. Presenter's
23 * role is to govern the view and control the complex UI logic based on
24 * notifications presenter receives from its view.
25 *
26 * @author Peter / Vaadin
27 *
28 * @param <V>
29 * type of the view this presenter governs
30 */
31 public abstract class AbstractPresenter<V extends ApplicationView> implements Serializable, DisposableBean {
32
33
34 private static final long serialVersionUID = 5260910510283481832L;
35
36 public static final Logger logger = Logger.getLogger(AbstractPresenter.class);
37
38 private V view;
39
40 protected V getView() {
41 if(view == null){
42 Logger.getLogger(this.getClass()).warn("CDM-VAADIN#6562: presenter " + toString() + " without view.");
43 }
44 return view;
45 }
46
47 @Autowired
48 @Qualifier("cdmRepository")
49 private CdmRepository repo;
50
51 @Autowired
52 private NavigationManager navigationManager;
53
54 protected EventBus.ViewEventBus viewEventBus;
55
56 @Autowired
57 protected void setViewEventBus(EventBus.ViewEventBus viewEventBus){
58 this.viewEventBus = viewEventBus;
59 eventViewBusSubscription(viewEventBus);
60 }
61
62 /**
63 * Override if needed, e.g. to skip subscription
64 *
65 * @param viewEventBus
66 */
67 protected void eventViewBusSubscription(EventBus.ViewEventBus viewEventBus){
68 viewEventBus.subscribe(this);
69 }
70
71 public void unsubscribeFromEventBuses(){
72 viewEventBus.unsubscribe(this);
73 }
74
75
76 // protected DefaultTransactionDefinition definition = null;
77
78 // protected TransactionDefinition getTransactionDefinition(){
79 // if(definition == null){
80 // definition = new DefaultTransactionDefinition();
81 // definition.setReadOnly(true);
82 // }
83 // return definition;
84 // }
85
86
87 /**
88 * @return the repo
89 */
90 public CdmRepository getRepo() {
91 return repo;
92 }
93
94 /**
95 * @return
96 *
97 * FIXME is it ok to use the SecurityContextHolder or do we need to hold the context in the vaadin session?
98 */
99 protected SecurityContext currentSecurityContext() {
100 return SecurityContextHolder.getContext();
101 }
102
103
104 protected String _toString(){
105 return this.getClass().getSimpleName() + "@" + this.hashCode();
106 }
107
108 /**
109 * Notifies the presenter that its view is initialized so that presenter can
110 * start its own initialization if required.
111 *
112 * @param view
113 */
114 protected void init(V view) {
115 logger.trace(String.format("Presenter %s init()", _toString()));
116 this.view = view;
117 onPresenterReady();
118 }
119
120
121 /**
122 * Extending classes should overwrite this method in order to perform logic
123 * after presenter has finished initializing.
124 *
125 * At this point in the life cycle of the MVP the {@link AbstractView#initContent() initContent()}
126 * method has been executed already.
127 */
128 protected void onPresenterReady() {
129 logger.trace(String.format("Presenter %s ready", _toString()));
130 }
131
132 public final void onViewEnter() {
133 logger.trace(String.format("%s onViewEnter()", _toString()));
134 handleViewEntered();
135 }
136
137 public void onViewExit() {
138 logger.trace(String.format("%s onViewExit()", _toString()));
139 handleViewExit();
140 }
141
142 /**
143 * Extending classes should overwrite this method to react to the event when
144 * user has navigated into the view that this presenter governs.
145 * For implementations of {@link AbstractPopupEditor AbstractPopupEditors} this is usually
146 * called before the data item has been bound. This order is guaranteed since popup editors
147 * are managed through the {@link NavigationManagerBean}
148 */
149 public void handleViewEntered() {
150 // fist of all clear the session to wipe out any left overs from previous usage of the session
151 // this can happen if not all operations within a session are cleaned up correctly in
152 // turn of an exception
153 getRepo().clearSession();
154 }
155
156 /**
157 * Extending classes may overwrite this method to react to
158 * the event when user leaves the view that this presenter
159 * governs. This method is executed before un-binding and closing the
160 * conversation holder.
161 */
162 public void handleViewExit() {
163 }
164
165 /**
166 * @return the navigationManager
167 */
168 public NavigationManager getNavigationManager() {
169 return navigationManager;
170 }
171
172 /**
173 * @param repo the repo to set
174 */
175 protected void setRepo(CdmRepository repo) {
176 this.repo = repo;
177 }
178
179 /**
180 * @param navigationManager the navigationManager to set
181 */
182 protected void setNavigationManager(NavigationManager navigationManager) {
183 this.navigationManager = navigationManager;
184 }
185
186 protected boolean checkFromOwnView(AbstractEditorAction event) {
187 return getView() != null && getView() == event.getSourceView();
188 }
189
190 /**
191 * Opens a new PopView or editor of the type <code>popupViewClass</code> for the current view.
192 * If the <code>event</code> is not null and if it contains a target field the newly created editor
193 * will be registered with this field as target. Once the popup editor is being saved a
194 * {@link eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent} will be emitted which will hold a
195 * reference to the popup editor in the {@link eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent#getSourceView() sourceView}
196 * property. By this reference the target field can be retrieved in editor presenters via the method
197 * {@link AbstractEditorPresenter#boundTargetField(PopupView)}:
198 * <p>
199 * {@code
200 * BoundField targetField = boundTargetField(entityChangeEvent.getSourceView())
201 * }
202 * </p>
203 * In case the target field is bound to a bean property the propertyId is available in the {@link BoundField}
204 * object and can be used to decide on which bean property to update with the data saved in the popup editor or to
205 * act in any other appropriate way.
206 *
207 * @param popupViewClass
208 * @param event
209 * @return
210 */
211 protected <T extends PopupView> T openPopupEditor(Class<T> popupViewClass, AbstractEditorAction<?> event) {
212 Field<?> targetField = event != null? event.getTarget(): null;
213 return getNavigationManager().showInPopup(popupViewClass, getView(), targetField);
214 }
215
216 protected boolean isFromOwnView(EntityChangeEvent event) {
217 return event.getSourceView() != null && event.getSourceView().equals(getView());
218 }
219
220 @Override
221 public void destroy() throws Exception {
222 unsubscribeFromEventBuses();
223 view = null;
224 }
225
226 }