001    package org.apache.ojb.broker.metadata;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.Iterator;
021    import java.util.Vector;
022    
023    import org.apache.commons.lang.SystemUtils;
024    import org.apache.ojb.broker.PersistenceBrokerException;
025    import org.apache.ojb.broker.accesslayer.QueryCustomizer;
026    
027    
028    /**
029     * mapping Description for member fields that are Collections
030     * <br>
031     * Note: Be careful when use references of this class or caching instances of this class,
032     * because instances could become invalid (see {@link MetadataManager}).
033     *
034     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
035     * @version $Id: CollectionDescriptor.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
036     */
037    public class CollectionDescriptor extends ObjectReferenceDescriptor
038    {
039        private static final long serialVersionUID = -8570280662286424937L;
040    
041        /**
042         * Represents the type of the collection, if set to null,
043         * a java.util.Vector will be used.
044         * If set to a valid collection type it will be used to build typed collections.
045         */
046        private Class collectionClass = null;
047        /**
048         * the Collection of orderby Fields
049         */
050        private Collection m_orderby = new ArrayList();
051        /**
052         * For m:n related Classes this is the indirection table.
053         */
054        private String indirectionTable = null;
055        private Vector fksToItemClass = null;
056        private Vector fksToThisClass = null;
057        private String[] fksToItemClassAry;
058        private String[] fksToThisClassAry;
059        private QueryCustomizer m_queryCustomizer;
060        private Boolean m_hasProxyItems;
061    
062        public CollectionDescriptor(ClassDescriptor descriptor)
063        {
064            super(descriptor);
065        }
066    
067        public String[] getFksToThisClass()
068        {
069            if (fksToThisClassAry == null)
070            {
071                fksToThisClassAry = (String[]) fksToThisClass.toArray(
072                        new String[fksToThisClass.size()]);
073            }
074            return fksToThisClassAry;
075        }
076    
077        public void setFksToThisClass(Vector fksToThisClass)
078        {
079            this.fksToThisClass = fksToThisClass;
080            fksToThisClassAry = null;
081        }
082    
083        /**
084         * add a FK column pointing to This Class
085         */
086        public void addFkToThisClass(String column)
087        {
088            if (fksToThisClass == null)
089            {
090                fksToThisClass = new Vector();
091            }
092            fksToThisClass.add(column);
093            fksToThisClassAry = null;
094        }
095    
096        /**
097         * add a FK column pointing to the item Class
098         */
099        public void addFkToItemClass(String column)
100        {
101            if (fksToItemClass == null)
102            {
103                fksToItemClass = new Vector();
104            }
105            fksToItemClass.add(column);
106            fksToItemClassAry = null;
107        }
108    
109        /**
110         * returns the type of the collection.
111         * @return java.lang.Class
112         */
113        public Class getCollectionClass()
114        {
115            return collectionClass;
116        }
117    
118        /**
119         * set the type of the collection
120         * @param c the collection type
121         */
122        public void setCollectionClass(Class c)
123        {
124            collectionClass = c;
125        }
126    
127        /**
128         * Retrieve the classname of the collection.
129         */
130        public String getCollectionClassName()
131        {
132            return collectionClass != null ? collectionClass.getName() : null;
133        }
134    
135        public String getIndirectionTable()
136        {
137            return indirectionTable;
138        }
139    
140        public void setIndirectionTable(String indirectionTable)
141        {
142            this.indirectionTable = indirectionTable;
143        }
144    
145        public String[] getFksToItemClass()
146        {
147            if (fksToItemClassAry == null)
148            {
149                fksToItemClassAry = (String[]) fksToItemClass.toArray(
150                        new String[fksToItemClass.size()]);
151            }
152            return fksToItemClassAry;
153        }
154    
155        public void setFksToItemClass(Vector fksToItemClass)
156        {
157            this.fksToItemClass = fksToItemClass;
158            fksToItemClassAry = null;
159        }
160    
161        public boolean isMtoNRelation()
162        {
163            return (indirectionTable != null);
164        }
165    
166        /**
167         * Adds a field for orderBy
168         * @param  fieldName    The field name to be used
169         * @param  sortAscending    true for ASCENDING, false for DESCENDING
170         */
171        public void addOrderBy(String fieldName, boolean sortAscending)
172        {
173            if (fieldName != null)
174            {
175                m_orderby.add(new FieldHelper(fieldName, sortAscending));
176            }
177        }
178    
179        /**
180         * Returns the orderby Collection of Fields.
181         * @return Collection
182         */
183        public Collection getOrderBy()
184        {
185            return m_orderby;
186        }
187    
188        protected int getCascadeDeleteValue(String cascade)
189        {
190            if(cascade.equalsIgnoreCase("false") && isMtoNRelation())
191            {
192                /*
193                "old" implementation does always delete entries in indirection table for
194                m:n relations. For 1:n relations referenced objects are not touched.
195                */
196                return CASCADE_LINK;
197            }
198            return super.getCascadeDeleteValue(cascade);
199        }
200    
201        /*
202         * @see XmlCapable#toXML()
203         */
204        public String toXML()
205        {
206            RepositoryTags tags = RepositoryTags.getInstance();
207            String eol = SystemUtils.LINE_SEPARATOR;
208    
209            // write opening tag
210            String result = "      " + tags.getOpeningTagNonClosingById(COLLECTION_DESCRIPTOR) + eol;
211    
212            // write attributes
213            // name
214            result       += "        " + tags.getAttribute(FIELD_NAME,this.getAttributeName()) + eol;
215    
216            // collection class is optional
217            if (getCollectionClassName() != null)
218            {
219                result       += "        " + tags.getAttribute(COLLECTION_CLASS,this.getCollectionClassName()) + eol;
220            }
221    
222            // element-class-ref
223             result       += "        " + tags.getAttribute(ITEMS_CLASS,this.getItemClassName()) + eol;
224    
225            // indirection-table is optional
226            if (isMtoNRelation())
227            {
228                 result += "        " + tags.getAttribute(INDIRECTION_TABLE,getIndirectionTable()) + eol;
229            }
230    
231            // proxyReference is optional, disabled by default
232            if (isLazy())
233            {
234                result       += "        " + tags.getAttribute(PROXY_REFERENCE,"true") + eol;
235                result       += "        " + tags.getAttribute(PROXY_PREFETCHING_LIMIT, "" + this.getProxyPrefetchingLimit()) + eol;
236            }
237    
238            //reference refresh is optional, disabled by default
239            if (isRefresh())
240            {
241                 result       += "        " + tags.getAttribute(REFRESH,"true") + eol;
242            }
243    
244            //auto retrieve
245            result += "        " + tags.getAttribute(AUTO_RETRIEVE, "" + getCascadeRetrieve()) + eol;
246    
247            //auto update
248            result += "        " + tags.getAttribute(AUTO_UPDATE, getCascadeAsString(getCascadingStore())) + eol;
249    
250            //auto delete
251            result += "        " + tags.getAttribute(AUTO_DELETE, getCascadeAsString(getCascadingDelete())) + eol;
252    
253            //otm-dependent is optional, disabled by default
254            if (getOtmDependent())
255            {
256                result += "        " + tags.getAttribute(OTM_DEPENDENT, "true") + eol;
257            }
258    
259            // close opening tag
260            result       += "      >" + eol;
261    
262            // write elements
263             // inverse fk elements
264            for (int i=0;i<getForeignKeyFields().size();i++)
265            {
266            Object obj = getForeignKeyFields().get(i);
267            if (obj instanceof Integer)
268            {
269                    String fkId = obj.toString();
270                result += "        " + tags.getOpeningTagNonClosingById(INVERSE_FK) + " ";
271                    result += tags.getAttribute(FIELD_ID_REF, fkId) + "/>" + eol;
272            }
273            else
274            {
275                    String fk = (String) obj;
276                result += "        " + tags.getOpeningTagNonClosingById(INVERSE_FK) + " ";
277                    result += tags.getAttribute(FIELD_REF, fk) + "/>" + eol;
278            }
279            }
280    
281            // write optional M:N elements
282            // m:n relationship settings, optional
283            if (isMtoNRelation())
284            {
285                // foreign keys to this class
286                 for (int i=0;i<getFksToThisClass().length;i++)
287                 {
288                    String fkId = getFksToThisClass()[i];
289                    result += "        " + tags.getOpeningTagNonClosingById(FK_POINTING_TO_THIS_CLASS) + " ";
290                    result += tags.getAttribute(COLUMN_NAME, fkId) + "/>" + eol;
291                 }
292    
293                // foreign keys to item class
294                 for (int i=0;i<getFksToItemClass().length;i++)
295                 {
296                    String fkId = getFksToItemClass()[i];
297                    result += "        " + tags.getOpeningTagNonClosingById(FK_POINTING_TO_ITEMS_CLASS) + " ";
298                    result += tags.getAttribute(COLUMN_NAME, fkId) + "/>" + eol;
299                 }
300            }
301    
302            // closing tag
303            result       += "      " + tags.getClosingTagById(COLLECTION_DESCRIPTOR) + eol;
304            return result;
305        }
306    
307            /**
308             * @return QueryCustomizer
309             */
310            public QueryCustomizer getQueryCustomizer()
311            {
312                    return m_queryCustomizer;
313            }
314    
315            /**
316             * Sets the queryCustomizer.
317             * @param queryCustomizer The queryCustomizer to set
318             */
319            public void setQueryCustomizer(QueryCustomizer queryCustomizer)
320            {
321                    m_queryCustomizer = queryCustomizer;
322            }
323    
324        public boolean hasProxyItems() throws PersistenceBrokerException
325        {
326            if (m_hasProxyItems == null)
327            {
328                DescriptorRepository repo = getClassDescriptor().getRepository();
329                ClassDescriptor cld = repo.getDescriptorFor(getItemClass());
330                if (cld.getProxyClass() != null)
331                {
332                    m_hasProxyItems = Boolean.TRUE;
333                }
334                else
335                {
336                    Collection extents = cld.getExtentClasses();
337                    m_hasProxyItems = Boolean.FALSE;
338                    for (Iterator it = extents.iterator(); it.hasNext(); )
339                    {
340                        Class ext = (Class) it.next();
341                        ClassDescriptor cldExt = repo.getDescriptorFor(ext);
342                        if (cldExt.getProxyClass() != null)
343                        {
344                            m_hasProxyItems = Boolean.TRUE;
345                            break;
346                        }
347                    }
348                }
349            }
350    
351            return (m_hasProxyItems.booleanValue());
352        }
353    }