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 }