001 /*
002 * Copyright 2004 Chris Nelson
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
007 * Unless required by applicable law or agreed to in writing,
008 * software distributed under the License is distributed on an "AS IS" BASIS,
009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010 * See the License for the specific language governing permissions and limitations under the License.
011 */
012 package org.trails.component;
013
014 import ognl.Ognl;
015 import ognl.OgnlException;
016 import org.apache.commons.logging.Log;
017 import org.apache.commons.logging.LogFactory;
018 import org.apache.tapestry.IAsset;
019 import org.apache.tapestry.IRequestCycle;
020 import org.apache.tapestry.annotations.*;
021 import org.apache.tapestry.contrib.palette.SortMode;
022 import org.apache.tapestry.form.IPropertySelectionModel;
023 import org.apache.tapestry.link.ILinkRenderer;
024 import org.trails.descriptor.CollectionDescriptor;
025 import org.trails.descriptor.DescriptorService;
026 import org.trails.descriptor.IClassDescriptor;
027 import org.trails.page.PageResolver;
028 import org.trails.persistence.PersistenceService;
029 import org.trails.util.Utils;
030
031 import java.util.ArrayList;
032 import java.util.Collection;
033 import java.util.Collections;
034 import java.util.List;
035
036
037 /**
038 * This component produces a editor for a ManyToOne or ManyToMany collection. It allows a user to edit a collection
039 * property
040 *
041 * @author Chris Nelson
042 */
043 @ComponentClass(allowBody = false)
044 public abstract class EditCollection extends TrailsComponent
045 {
046
047 protected static final Log LOG = LogFactory.getLog(EditCollection.class);
048
049 @Parameter(required = false)
050 public abstract List getInstances();
051
052 @Parameter(required = true)
053 public abstract Collection getCollection();
054
055 /**
056 * The object which owns the collection being edited
057 */
058 @Parameter(required = false, defaultValue = "page.model")
059 public abstract Object getModel();
060
061 /**
062 * Ognl expression to invoke on the model to create a new child instance
063 */
064 @Parameter(required = false)
065 public abstract String getCreateExpression();
066
067 /**
068 * @return The CollectionDescriptor for the collection being edited
069 */
070 @Parameter(required = true)
071 public abstract CollectionDescriptor getCollectionDescriptor();
072
073 public abstract Object getCurrentObject();
074
075 public abstract void setCurrentObject(Object CurrentObject);
076
077 @Parameter(defaultValue = "page.descriptorService")
078 public abstract DescriptorService getDescriptorService();
079
080 @Parameter(defaultValue = "page.persistenceService")
081 public abstract PersistenceService getPersistenceService();
082
083 @Parameter(defaultValue = "not(collectionDescriptor.childRelationship)")
084 public abstract boolean getAddFromExisting();
085
086 @Parameter(defaultValue = "true")
087 public abstract boolean isAllowCreate();
088
089 @Parameter(defaultValue = "collectionDescriptor.allowRemove")
090 public abstract boolean isAllowRemove();
091
092 public abstract int getIndex();
093
094 public abstract void setIndex(int index);
095
096 @Asset("classpath:move_up.gif")
097 public abstract IAsset getUpImage();
098
099 @Asset("classpath:move_down.gif")
100 public abstract IAsset getDownImage();
101
102 private List<Boolean> selected = new ArrayList<Boolean>();
103
104 /**
105 * org.apache.tapestry.contrib.link.ButtonLinkRenderer
106 *
107 * @return
108 */
109 @InjectObject(value = "service:trails.core.AddNewLinkRenderer")
110 public abstract ILinkRenderer getRenderer();
111
112 /**
113 * (non-Javadoc)
114 *
115 * @see org.apache.tapestry.AbstractComponent#prepareForRender(org.apache.tapestry.IRequestCycle)
116 */
117 protected void prepareForRender(IRequestCycle cycle)
118 {
119 super.prepareForRender(cycle);
120 selected = buildSelectedList();
121 }
122
123 public List<Boolean> buildSelectedList()
124 {
125 ArrayList<Boolean> selected = new ArrayList<Boolean>();
126 if (getCollection() != null)
127 {
128 selected = new ArrayList<Boolean>(getCollection().size());
129 for (Object o : getCollection())
130 {
131 selected.add(false);
132 }
133 }
134 return selected;
135 }
136
137 @InjectObject("service:trails.core.PageResolver")
138 public abstract PageResolver getPageResolver();
139
140 public void remove()
141 {
142 int i = 0;
143 // TODO CN - This code stinks (I wrote it). Isn't there a better way??
144 ArrayList deleting = new ArrayList();
145 for (Object element : getCollection())
146 {
147 if (getSelected().get(i))
148 {
149 deleting.add(element);
150 }
151 i++;
152 }
153
154 for (Object element : deleting)
155 {
156 Utils.executeOgnlExpression(getCollectionDescriptor().getRemoveExpression(), element, getModel());
157 }
158 }
159
160 public List getSelectedList()
161 {
162 ArrayList selectedList = new ArrayList();
163 selectedList.addAll(getCollection());
164 return selectedList;
165 }
166
167 public void setSelectedList(List selected)
168 {
169 if (selected != null)
170 {
171 getCollection().clear();
172 getCollection().addAll(selected);
173 }
174 }
175
176 /**
177 * @return Returns the toBeDeleted.
178 */
179 public List<Boolean> getSelected()
180 {
181 return selected;
182 }
183
184 protected void setSelected(List<Boolean> selected)
185 {
186 this.selected = selected;
187 }
188
189 /**
190 * @return
191 */
192 public String getSortMode()
193 {
194 return isList() ? SortMode.USER : SortMode.LABEL;
195 }
196
197 public boolean isList()
198 {
199 return getCollection() instanceof List;
200 }
201
202 /**
203 * @return
204 */
205 public IPropertySelectionModel getSelectionModel()
206 {
207 IClassDescriptor elementDescriptor =
208 getDescriptorService().getClassDescriptor(getCollectionDescriptor().getElementType());
209 // don't allow use to select from all here
210 if (getInstances() != null)
211 {
212 return new IdentifierSelectionModel(getInstances(), elementDescriptor.getIdentifierDescriptor().getName());
213 } else if (getCollectionDescriptor().isChildRelationship())
214 {
215 return new IdentifierSelectionModel(getSelectedList(),
216 elementDescriptor.getIdentifierDescriptor().getName());
217 } else
218 {
219 // but do here
220 return new IdentifierSelectionModel(getPersistenceService().getAllInstances(getCollectionDescriptor().getElementType()),
221 elementDescriptor.getIdentifierDescriptor().getName());
222 }
223 }
224
225 public void moveUp()
226 {
227 List list = (List) getCollection();
228 for (int i = 1; i < getSelected().size(); i++)
229 {
230 if (getSelected().get(i))
231 {
232 if (getCollectionDescriptor().getSwapExpression() == null)
233 {
234 Collections.swap(list, i, i - 1);
235 } else
236 {
237 try
238 {
239 Ognl.getValue(getCollectionDescriptor().getSwapExpression() + "(" + i + "," + (i - 1) + ")",
240 getModel());
241 } catch (OgnlException e)
242 {
243 LOG.error(e.getMessage());
244 }
245 }
246 }
247 }
248 }
249
250 public void moveDown()
251 {
252 List list = (List) getCollection();
253 for (int i = 0; i < getSelected().size() - 1; i++)
254 {
255 if (getCollectionDescriptor().getSwapExpression() == null)
256 {
257 Collections.swap(list, i, i + 1);
258 } else
259 {
260 try
261 {
262 Ognl.getValue(getCollectionDescriptor().getSwapExpression() + "(" + i + "," + (i + 1) + ")",
263 getModel());
264 } catch (OgnlException e)
265 {
266 LOG.error(e.getMessage());
267 }
268 }
269 }
270 }
271 }