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    }