001 package org.tynamo.components;
002
003 import org.apache.tapestry5.*;
004 import org.apache.tapestry5.annotations.Environmental;
005 import org.apache.tapestry5.annotations.InjectComponent;
006 import org.apache.tapestry5.annotations.Parameter;
007 import org.apache.tapestry5.annotations.Property;
008 import org.apache.tapestry5.beaneditor.BeanModel;
009 import org.apache.tapestry5.corelib.components.Form;
010 import org.apache.tapestry5.corelib.components.Zone;
011 import org.apache.tapestry5.dom.Element;
012 import org.apache.tapestry5.ioc.Messages;
013 import org.apache.tapestry5.ioc.annotations.Inject;
014 import org.apache.tapestry5.ioc.services.PropertyAccess;
015 import org.apache.tapestry5.ioc.services.TypeCoercer;
016 import org.apache.tapestry5.services.BeanModelSource;
017 import org.apache.tapestry5.services.Heartbeat;
018 import org.apache.tapestry5.services.Request;
019 import org.apache.tapestry5.services.ValueEncoderSource;
020 import org.tynamo.builder.BuilderDirector;
021 import org.tynamo.descriptor.CollectionDescriptor;
022 import org.tynamo.services.DescriptorService;
023 import org.tynamo.services.PersistenceService;
024 import org.tynamo.util.DisplayNameUtils;
025
026 import java.util.Collection;
027 import java.util.List;
028
029 public class Composition
030 {
031
032 @Inject
033 private DescriptorService descriptorService;
034
035 @Inject
036 private PersistenceService persitenceService;
037
038 @Inject
039 private PropertyAccess propertyAccess;
040
041 @Inject
042 private BeanModelSource beanModelSource;
043
044 @Inject
045 private Messages messages;
046
047 @Inject
048 private BuilderDirector builderDirector;
049
050 @Inject
051 private ComponentResources resources;
052
053 @Inject
054 private ValueEncoderSource valueEncoderSource;
055
056 @Inject
057 private Request request;
058
059 @Environmental
060 private Heartbeat heartbeat;
061
062 /**
063 * The id used to generate a page-unique client-side identifier for the component. If a component renders multiple
064 * times, a suffix will be appended to the to id to ensure uniqueness. The uniqued value may be accessed via the
065 * {@link #getClientId() clientId property}.
066 */
067 @Parameter(value = "prop:resources.id", defaultPrefix = BindingConstants.LITERAL)
068 @Property(write = false)
069 private String clientId;
070
071 /**
072 * A Block to render when the source is empty. The default is simply the text "There is no data to display".
073 * This parameter is used to customize that message.
074 */
075 @Parameter(value = "block:empty", defaultPrefix = BindingConstants.LITERAL)
076 @Property(write = false)
077 private Block empty;
078
079 @Parameter(required = false)
080 private List instances;
081
082 @Parameter(required = true)
083 @Property(write = false)
084 private Collection collection;
085
086 /**
087 * The object which owns the collection being edited
088 */
089 @Parameter(required = true)
090 private Object owner;
091
092 /**
093 * Ognl expression to invoke on the model to create a new child instance
094 */
095 @Parameter(required = false)
096 private String createExpression;
097
098 /**
099 * @return The CollectionDescriptor for the collection being edited
100 */
101 @Parameter(required = true)
102 @Property(write = false)
103 private CollectionDescriptor collectionDescriptor;
104
105 @Property
106 private Object collectionIterator;
107
108 @Property
109 private Object formBean;
110
111 @Parameter(value = "true")
112 private boolean allowCreate;
113
114 @InjectComponent
115 private Zone compositionZone;
116
117 @InjectComponent
118 @Property(write = false)
119 private Form form;
120
121 @Inject
122 private TypeCoercer typeCoercer;
123
124 public boolean isAllowCreate()
125 {
126 return propertyAccess.get(owner, descriptorService.getClassDescriptor(owner.getClass())
127 .getIdentifierDescriptor().getName()) != null;
128 }
129
130 @Parameter(value = "prop:collectionDescriptor.allowRemove")
131 private boolean allowRemove;
132
133 @Property
134 private int index;
135
136 @Parameter(value = "asset:move_up.gif")
137 @Property(write = false)
138 private Asset upImage;
139
140 @Parameter(value = "asset:move_down.gif")
141 @Property(write = false)
142 private Asset downImage;
143
144 /**
145 * The image to use for the delete icon
146 */
147 @Parameter(value = "asset:delete.png")
148 @Property(write = false)
149 private Asset deleteIcon;
150
151 @Property(write = false)
152 private BeanModel beanModel;
153
154 private Element addLink;
155
156 void onPrepareFromForm()
157 {
158 resources.triggerEvent(EventConstants.PREPARE, null, null);
159
160 if (beanModel == null)
161 {
162 beanModel = beanModelSource.createEditModel(collectionDescriptor.getElementType(), messages);
163 }
164 }
165
166 void setupRender()
167 {
168 beanModel = beanModelSource.createEditModel(collectionDescriptor.getElementType(), messages);
169 formBean = builderDirector.createNewInstance(collectionDescriptor.getElementType());
170 }
171
172 boolean beginRender(MarkupWriter writer)
173 {
174
175 writer.element("div", "class", "t-add-child");
176 addLink = writer.element("a", "id", "add-" + collectionDescriptor.getName() + "-link", "href", "#", "class", "t-add-child-link");
177 writer.write(messages.get("org.tynamo.i18n.add-child"));
178
179 Runnable command = new Runnable()
180 {
181 public void run()
182 {
183 String fieldId = form.getClientId();
184 addLink.forceAttributes("onclick", "Element.toggle('" + form.getClientId() + "'); this.blur(); return false;");
185 }
186 };
187 heartbeat.defer(command);
188
189 writer.end();
190 writer.end();
191
192 return true;
193 }
194
195 public Object onSuccess()
196 {
197 persitenceService.addToCollection(collectionDescriptor, formBean, owner);
198
199 if (request.isXHR()) return compositionZone.getBody();
200 else return null;
201 }
202
203 public Object onActionFromDeleteChild(String elementid)
204 {
205 ValueEncoder valueEncoder = valueEncoderSource.getValueEncoder(collectionDescriptor.getElementType());
206 Object element = valueEncoder.toValue(elementid);
207
208 persitenceService.removeFromCollection(collectionDescriptor, element, owner);
209
210 if (request.isXHR())
211 {
212 return compositionZone.getBody();
213 }
214
215 return null;
216 }
217
218
219 /* public boolean isList()
220 {
221 return collection instanceof List;
222 }
223
224 public void moveUp()
225 {
226 List list = (List) collection;
227 for (int i = 1; i < getSelected().size(); i++)
228 {
229 if (getSelected().get(i))
230 {
231 if (collectionDescriptor.getSwapExpression() == null)
232 {
233 Collections.swap(list, i, i - 1);
234 } else
235 {
236 try
237 {
238 Ognl.getValue(collectionDescriptor.getSwapExpression() + "(" + i + "," + (i - 1) + ")", owner);
239 } catch (OgnlException e)
240 {
241 LOGGER.error(e.getMessage());
242 }
243 }
244 }
245 }
246 }
247
248 public void moveDown()
249 {
250 List list = (List) collection;
251 for (int i = 0; i < getSelected().size() - 1; i++)
252 {
253 if (collectionDescriptor.getSwapExpression() == null)
254 {
255 Collections.swap(list, i, i + 1);
256 } else
257 {
258 try
259 {
260 Ognl.getValue(collectionDescriptor.getSwapExpression() + "(" + i + "," + (i + 1) + ")",
261 owner);
262 } catch (OgnlException e)
263 {
264 LOG.error(e.getMessage());
265 }
266 }
267 }
268 }*/
269
270 public Object[] getShowPageContext()
271 {
272 return new Object[]{collectionIterator.getClass(), collectionIterator};
273 }
274
275 public Object[] getDeleteContext()
276 {
277 return new Object[]{collectionIterator};
278 }
279
280 public String getLegendMessage()
281 {
282 return messages.format("org.tynamo.i18n.add",
283 DisplayNameUtils.getDisplayName(collectionDescriptor.getElementType(), messages));
284 }
285
286 public String getCompositionZoneClientId()
287 {
288 return compositionZone.getClientId();
289 }
290
291 }