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.descriptor;
013
014 import java.lang.reflect.InvocationTargetException;
015 import java.lang.reflect.Method;
016 import java.util.List;
017
018 import org.apache.commons.beanutils.BeanUtils;
019 import org.apache.commons.logging.Log;
020 import org.apache.commons.logging.LogFactory;
021 import org.trails.exception.TrailsRuntimeException;
022
023
024 public class CollectionDescriptor extends TrailsPropertyDescriptor
025 {
026 protected static final Log LOG = LogFactory.getLog(CollectionDescriptor.class);
027
028 private Class elementType;
029
030 private boolean childRelationship = false;
031
032 private String inverseProperty = null;
033
034 private boolean oneToMany = false;
035
036 private String addExpression = null;
037
038 private String removeExpression = null;
039
040 private String swapExpression = null;
041
042 private boolean allowRemove = true;
043
044 public CollectionDescriptor(Class beanType, IPropertyDescriptor descriptor)
045 {
046 super(beanType, descriptor);
047 }
048
049 public CollectionDescriptor(Class beanType, CollectionDescriptor collectionDescriptor)
050 {
051 super(beanType, collectionDescriptor.getBeanType());
052 this.copyFrom(collectionDescriptor);
053 }
054
055 public CollectionDescriptor(Class beanType, String name, Class type)
056 {
057 super(beanType, type);
058 this.setName(name);
059 }
060
061
062 /**
063 * (non-Javadoc)
064 *
065 * @see org.trails.descriptor.TrailsPropertyDescriptor#isCollection()
066 */
067 public boolean isCollection()
068 {
069 return true;
070 }
071
072 /**
073 * @return Returns the elementType.
074 */
075 public Class getElementType()
076 {
077 return elementType;
078 }
079
080 /**
081 * @param elementType The elementType to set.
082 */
083 public void setElementType(Class elementType)
084 {
085 this.elementType = elementType;
086 }
087
088 public String getInverseProperty()
089 {
090 return inverseProperty;
091 }
092
093 public void setInverseProperty(String inverseProperty)
094 {
095 this.inverseProperty = inverseProperty;
096 }
097
098 /**
099 * Is this a OneToMany collection? or a ManyToMany collection?
100 */
101 public boolean isOneToMany()
102 {
103 return oneToMany;
104 }
105
106 public void setOneToMany(boolean oneToMany)
107 {
108 this.oneToMany = oneToMany;
109 }
110
111
112 /**
113 * @return Returns the childRelationship.
114 */
115 public boolean isChildRelationship()
116 {
117 return childRelationship;
118 }
119
120 /**
121 * @param childRelationship The childRelationship to set.
122 */
123 public void setChildRelationship(boolean childRelationship)
124 {
125 this.childRelationship = childRelationship;
126 if (this.childRelationship)
127 {
128 setSearchable(false);
129 }
130 }
131
132 public String getAddExpression()
133 {
134 if (addExpression == null)
135 {
136 addExpression = findAddExpression();
137 }
138 return addExpression;
139 }
140
141 public void setAddExpression(String addExpression)
142 {
143 this.addExpression = addExpression;
144 }
145
146 public String getRemoveExpression()
147 {
148 if (removeExpression == null)
149 {
150 removeExpression = findRemoveExpression();
151 }
152 return removeExpression;
153 }
154
155 public void setRemoveExpression(String removeExpression)
156 {
157 this.removeExpression = removeExpression;
158 }
159
160 public String getSwapExpression()
161 {
162 return swapExpression;
163 }
164
165 public void setSwapExpression(String swapExpression)
166 {
167 this.swapExpression = swapExpression;
168 }
169
170 public boolean isAllowRemove()
171 {
172 return allowRemove;
173 }
174
175 public void setAllowRemove(boolean allowRemove)
176 {
177 this.allowRemove = allowRemove;
178 }
179
180 public Object clone()
181 {
182 return new CollectionDescriptor(getBeanType(), this);
183 }
184
185 private String findAddExpression()
186 {
187 final String method = "add";
188
189 /**
190 * Awful patch for TRAILS-78 bug.
191 * It evaluates if the object is in the list before adding it.
192 * If it is already there then do nothing else add it.
193 * eg: "bazzes.contains(#member) ? bazzes.size() : bazzes.add"
194 *
195 */
196 if (isChildRelationship() && List.class.isAssignableFrom(getType()))
197 {
198 return findExpression(method,
199 getName() + ".contains(#member) ? " + getName() + ".size() : " + getName() + "." + method);
200 } else
201 {
202 return findExpression(method);
203 }
204
205 }
206
207 private String findRemoveExpression()
208 {
209 return findExpression("remove");
210 }
211
212 /**
213 * @param method the method to look for, usually add or remove
214 * @return the ogln expression to use to add or remove a member to the collection. Will look for a addName method
215 * where Name is the unqualified element class name, if there isn't one it will use the collection's add
216 * method.
217 */
218 private String findExpression(String method, String defaultValue)
219 {
220 Method addMethod = null;
221
222 try
223 {
224 addMethod =
225 getBeanType().getMethod(method + getElementType().getSimpleName(), new Class[]{getElementType()});
226 } catch (NoSuchMethodException ex)
227 {
228 // if we don't have one...
229 return defaultValue;
230 } catch (Exception e)
231 {
232 throw new TrailsRuntimeException(e);
233 }
234
235 return addMethod.getName();
236 }
237
238 String findExpression(String method)
239 {
240 return findExpression(method, getName() + "." + method);
241 }
242
243 private void copyFrom(CollectionDescriptor collectionDescriptor)
244 {
245 LOG.debug("Cloning CollectionDescriptor");
246 try
247 {
248 BeanUtils.copyProperties(this, collectionDescriptor);
249 } catch (IllegalAccessException e)
250 {
251 LOG.error(e.getMessage());
252 } catch (InvocationTargetException e)
253 {
254 LOG.error(e.getMessage());
255 }
256 }
257 }