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    }