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    }