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