001    // Copyright 2008 Thiago H. de Paula Figueiredo
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package br.com.arsmachina.tapestrycrud.base;
016    
017    import java.io.Serializable;
018    
019    import org.apache.tapestry5.ComponentResources;
020    import org.apache.tapestry5.EventContext;
021    import org.apache.tapestry5.PrimaryKeyEncoder;
022    import org.apache.tapestry5.annotations.Cached;
023    import org.apache.tapestry5.annotations.OnEvent;
024    import org.apache.tapestry5.annotations.PageDetached;
025    import org.apache.tapestry5.annotations.Retain;
026    import org.apache.tapestry5.beaneditor.BeanModel;
027    import org.apache.tapestry5.corelib.components.Grid;
028    import org.apache.tapestry5.ioc.annotations.Inject;
029    import org.apache.tapestry5.services.BeanModelSource;
030    import org.apache.tapestry5.services.Request;
031    
032    import br.com.arsmachina.tapestrycrud.Constants;
033    import br.com.arsmachina.tapestrycrud.grid.ControllerGridDataSource;
034    import br.com.arsmachina.tapestrycrud.services.PrimaryKeyEncoderSource;
035    
036    /**
037     * Base class for pages that list entity objects. The <code>object</code> property is meant to be
038     * used as the <code>row</code> parameter of the {@link Grid} component.
039     * 
040     * One example of its use can be found in the Ars Machina Project Example Application (<a
041     * href="http://ars-machina.svn.sourceforge.net/viewvc/ars-machina/example/trunk/src/main/java/br/com/arsmachina/example/web/pages/project/ListProject.java?view=markup"
042     * >page class</a>. <a
043     * href="http://ars-machina.svn.sourceforge.net/viewvc/ars-machina/example/trunk/src/main/webapp/project/ListProject.tml?view=markup"
044     * >template</a>).
045     * 
046     * 
047     * @param <T> the entity class related to this encoder.
048     * @param <K> the type of the class' primary key property.
049     * 
050     * @author Thiago H. de Paula Figueiredo
051     */
052    public abstract class BaseListPage<T, K extends Serializable> extends BasePage<T, K> {
053    
054            @Inject
055            private ComponentResources componentResources;
056    
057            @Inject
058            private Request request;
059    
060            @Inject
061            private PrimaryKeyEncoderSource primaryKeyEncoderSource;
062            
063            @Retain
064            private PrimaryKeyEncoder<K, T> primaryKeyEncoder;
065    
066            @Inject
067            private BeanModelSource beanModelSource;
068    
069            private T object;
070    
071            /**
072             * Single constructor of this class.
073             */
074            @SuppressWarnings("unchecked")
075            public BaseListPage() {
076                    
077                    super();
078                    
079                    primaryKeyEncoder = (PrimaryKeyEncoder<K, T>) primaryKeyEncoderSource.get(getEntityClass());
080                    
081            }
082    
083            /**
084             * Method used as the <code>source</code> parameter of the {@link Grid} component. This
085             * implementation returns <code>new {@link ControllerGridDataSource}(getController())</code>.
086             * 
087             * @return an {@link Object}.
088             */
089            @SuppressWarnings("unchecked")
090            @Cached
091            public Object getObjects() {
092                    return new ControllerGridDataSource(getEntityClass(), getController());
093            }
094    
095            /**
096             * Returns the value of the <code>object</code> property.
097             * 
098             * @return a {@link T}.
099             */
100            public T getObject() {
101                    return object;
102            }
103    
104            /**
105             * Changes the value of the <code>object</code> property.
106             * 
107             * @param object a {@link T}.
108             */
109            public void setObject(T object) {
110                    this.object = object;
111            }
112    
113            /**
114             * Adds an <code>action</code> property to the {@link BeanModel}.
115             * 
116             * @see br.com.arsmachina.tapestrycrud.base.BasePage#getBeanModel()
117             */
118            @SuppressWarnings("unchecked")
119            public BeanModel<T> getBeanModel() {
120    
121                    final BeanModel<T> beanModel = beanModelSource.createDisplayModel(getEntityClass(),
122                                    getMessages());
123                    beanModel.add(Constants.ACTION_PROPERTY_NAME, null);
124    
125                    return beanModel;
126    
127            }
128    
129            /**
130             * Removes or not a given object. This method only removes an object, using
131             * <code>getController().delete(id)</code>, if {@link canRemove(K)} returns <code>true</code>.
132             * 
133             * @param object a {@link K}.
134             */
135            protected final Object remove(T object) {
136                    
137                    if (object == null) {
138                            setRemoveErrorNotFoundMessage();
139                    }
140    
141                    else if (canRemove(object)) {
142    
143                            getController().delete(object);
144                            setRemoveSuccessMessage();
145    
146                    }
147                    else {
148                            setRemoveErrorNotAllowedMessage();
149                    }
150    
151                    return returnFromDoRemove();
152    
153            }
154    
155            /**
156             * Defines what {@link #doRemove()} will return.
157             * 
158             * @return an {@link Object} or <code>null</code>.
159             */
160            protected Object returnFromDoRemove() {
161    
162                    Object returnValue = null;
163    
164                    if (request.isXHR()) {
165    
166                            if (returnZoneOnXHR()) {
167                                    returnValue = getFormZone();
168                            }
169                            else {
170                                    returnValue = componentResources.getBlock(getFormBlockId());
171                            }
172    
173                    }
174    
175                    return returnValue;
176    
177            }
178    
179            /**
180             * Sets the remove success message in this page.
181             */
182            protected void setRemoveSuccessMessage() {
183                    setMessage(getMessages().get(Constants.MESSAGE_SUCCESS_REMOVE));
184            }
185    
186            /**
187             * Sets the remove not done because of lack of priviledge message in this page.
188             */
189            protected void setRemoveErrorNotAllowedMessage() {
190                    setMessage(getMessages().get(Constants.MESSAGE_ERROR_REMOVE_NOT_ALLOWED));
191            }
192    
193            /**
194             * Sets the remove not done because object not found in this page.
195             */
196            protected void setRemoveErrorNotFoundMessage() {
197                    setMessage(getMessages().get(Constants.MESSAGE_ERROR_REMOVE_NOT_FOUND));
198            }
199    
200            /**
201             * Tells if a given object can be removed in this context. It must be overriden if you have some
202             * rules about when an object can be removed. This implementation just returns <code>true</code>.
203             * 
204             * @param object a {@link #T}.
205             * @return a <code>boolean</code>.
206             */
207            protected boolean canRemove(T object) {
208                    return true;
209            }
210    
211            /**
212             * Clears the message after it is shown, preventing the message from appearing twice in AJAX
213             * actions.
214             */
215            @PageDetached
216            void clearMessage() {
217    
218                    if (request.isXHR()) {
219                            setMessage(null);
220                    }
221    
222            }
223    
224            /**
225             * This method listens to the {@link Constants#REMOVE_OBJECT_ACTION} event and removes the
226             * corresponding object.
227             * 
228             * @param context an {@link EventContext}.
229             */
230            @OnEvent(Constants.REMOVE_OBJECT_ACTION)
231            protected Object remove(EventContext context) {
232                    
233                    K id = context.get(getPrimaryKeyClass(), 0);
234                    final T toBeRemoved = primaryKeyEncoder.toValue(id);
235                    return remove(toBeRemoved);
236    
237            }
238    
239            /**
240             * Returns the configured {@link PrimaryKeyEncoder} for a given entity class.
241             * 
242             * @param <X> the type of the entity.
243             * @param clasz a {@link Class}.
244             * @return a {@link PrimaryKeyEncoder}.
245             * @see br.com.arsmachina.tapestrycrud.services.PrimaryKeyEncoderSource#get(java.lang.Class)
246             */
247            protected <X, Y extends Serializable> PrimaryKeyEncoder<Y, X> getPrimaryKeyEncoder(Class<X> clasz) {
248                    return primaryKeyEncoderSource.get(clasz);
249            }
250    
251    }