001: /*
002: * Copyright 2004 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: ClassBaseTableExtent.java,v 1.8 2004/02/01 18:22:42 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.store;
012:
013: import com.triactive.jdo.PersistenceManager;
014: import com.triactive.jdo.model.ClassMetaData;
015: import com.triactive.jdo.util.IntArrayList;
016: import java.util.HashMap;
017: import java.util.Iterator;
018: import javax.jdo.Extent;
019: import org.apache.log4j.Category;
020:
021: /**
022: * An Extent of all persistent objects backed by a base table.
023: *
024: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
025: * @version $Revision: 1.8 $
026: *
027: * @see Extent
028: */
029:
030: class ClassBaseTableExtent implements Extent, Queryable {
031: private static final Category LOG = Category
032: .getInstance(ClassBaseTableExtent.class);
033:
034: private final PersistenceManager pm;
035: private final ClassBaseTable table;
036: private final boolean subclasses;
037: private final StoreManager storeMgr;
038: private final DatabaseAdapter dba;
039: private final Query query;
040: private final int[] prefetchFieldNumbers;
041: private final ColumnMapping[] prefetchFieldMappings;
042:
043: private HashMap queryResultsByIterator = new HashMap();
044:
045: public ClassBaseTableExtent(PersistenceManager pm,
046: ClassBaseTable table, boolean subclasses) {
047: this .pm = pm;
048: this .table = table;
049: this .subclasses = subclasses;
050:
051: storeMgr = table.getStoreManager();
052: dba = storeMgr.getDatabaseAdapter();
053: query = storeMgr.getQuery(pm, null);
054:
055: Class candidateClass = getCandidateClass();
056:
057: query.setClass(candidateClass);
058: query.setCandidates(this );
059:
060: ClassMetaData cmd = ClassMetaData.forClass(candidateClass);
061: int fieldCount = cmd.getInheritedFieldCount()
062: + cmd.getFieldCount();
063: IntArrayList colfn = new IntArrayList(fieldCount);
064: ColumnMapping[] colfm = new ColumnMapping[fieldCount];
065:
066: boolean[] defaultFetchGroupFields = cmd
067: .getDefaultFetchGroupFieldFlags();
068:
069: for (int i = 0; i < fieldCount; ++i) {
070: if (defaultFetchGroupFields[i]) {
071: Mapping m = table.getFieldMapping(i);
072:
073: if (m instanceof ColumnMapping) {
074: colfn.add(i);
075: colfm[i] = (ColumnMapping) m;
076: }
077: }
078: }
079:
080: if (colfn.isEmpty()) {
081: prefetchFieldNumbers = null;
082: prefetchFieldMappings = null;
083: } else {
084: prefetchFieldNumbers = colfn.toArray();
085: prefetchFieldMappings = colfm;
086: }
087: }
088:
089: /**
090: * Returns an iterator over all the instances in the Extent.
091: *
092: * @return an iterator over all the instances in the Extent.
093: */
094:
095: public Iterator iterator() {
096: QueryResult qr = (QueryResult) query.execute();
097: Iterator i = qr.iterator();
098:
099: queryResultsByIterator.put(i, qr);
100:
101: return i;
102: }
103:
104: /**
105: * Returns whether this Extent was defined to contain subclasses.
106: *
107: * @return true if this Extent was defined to contain instances
108: * that are of a subclass type
109: */
110:
111: public boolean hasSubclasses() {
112: return subclasses;
113: }
114:
115: /**
116: * An Extent contains all instances of a particular Class in the data
117: * store; this method returns the Class of the instances
118: *
119: * @return the Class of instances of this Extent
120: */
121:
122: public Class getCandidateClass() {
123: return table.getType();
124: }
125:
126: /**
127: * An Extent is managed by a PersistenceManager; this method gives access
128: * to the owning PersistenceManager.
129: *
130: * @return the owning PersistenceManager
131: */
132:
133: public javax.jdo.PersistenceManager getPersistenceManager() {
134: return pm;
135: }
136:
137: /**
138: * Close an Iterator associated with this Extent instance. Iterators closed
139: * by this method will return false to hasNext() and will throw
140: * NoSuchElementException on next(). The Extent instance can still be used
141: * as a parameter of Query.setCandidates, and to get an Iterator.
142: *
143: * @param it an iterator obtained by the method iterator() on this Extent
144: * instance.
145: */
146:
147: public void close(Iterator it) {
148: QueryResult qr = (QueryResult) queryResultsByIterator
149: .remove(it);
150:
151: qr.close();
152: }
153:
154: /**
155: * Close all Iterators associated with this Extent instance. Iterators
156: * closed by this method will return false to hasNext() and will throw
157: * NoSuchElementException on next(). The Extent instance can still be used
158: * as a parameter of Query.setCandidates, and to get an Iterator.
159: */
160:
161: public void closeAll() {
162: Iterator i = queryResultsByIterator.values().iterator();
163:
164: while (i.hasNext()) {
165: QueryResult qr = (QueryResult) i.next();
166:
167: qr.close();
168: i.remove();
169: }
170: }
171:
172: public QueryStatement newQueryStatement(Class candidateClass) {
173: Class extentType = table.getType();
174:
175: if (!extentType.isAssignableFrom(candidateClass))
176: throw new IncompatibleQueryElementTypeException(extentType,
177: candidateClass);
178:
179: ClassBaseTable candidateTable = storeMgr
180: .getClassBaseTable(candidateClass);
181: QueryStatement stmt = dba.newQueryStatement(candidateTable);
182:
183: ColumnMapping idMapping = table.getIDMapping();
184: Column idColumn = idMapping.getColumn();
185:
186: if (!subclasses) {
187: if (!table.equals(candidateTable))
188: LOG
189: .warn("Query over extent will never return results w/o including subclasses: extent = "
190: + this
191: + ", candidate class = "
192: + candidateClass);
193:
194: int classID = table.getTableID();
195:
196: BooleanExpression filterByClass = new OIDRangeTestExpression(
197: stmt, stmt.getColumn(idColumn), new OID(classID, 0,
198: 0), new OID(classID, OID.MAX_OBJIDHI,
199: OID.MAX_OBJIDLO));
200:
201: stmt.andCondition(filterByClass);
202: }
203:
204: stmt.select(idColumn);
205:
206: return stmt;
207: }
208:
209: public Query.ResultObjectFactory newResultObjectFactory(
210: QueryStatement stmt) {
211: if (stmt.getDistinctResults() || prefetchFieldMappings == null)
212: return new PersistentIDROF(pm, getCandidateClass());
213: else {
214: int[] columnNumbersByField = new int[prefetchFieldMappings.length];
215:
216: for (int i = 0; i < prefetchFieldMappings.length; ++i) {
217: ColumnMapping m = prefetchFieldMappings[i];
218:
219: if (m != null)
220: columnNumbersByField[i] = stmt
221: .select(m.getColumn());
222: }
223:
224: return new PersistentIDROF(pm, getCandidateClass(),
225: prefetchFieldNumbers, prefetchFieldMappings,
226: columnNumbersByField);
227: }
228: }
229:
230: public String toString() {
231: return "Extent of " + getCandidateClass()
232: + (subclasses ? "(including" : "(excluding")
233: + " subclasses)";
234: }
235: }
|