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