001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.common;
012:
013: import com.versant.core.jdo.VersantPersistenceManager;
014: import com.versant.core.jdo.QueryResultBase;
015: import com.versant.core.metadata.MDStatics;
016: import com.versant.core.server.CompiledQuery;
017: import com.versant.core.server.CachedQueryResult;
018: import com.versant.core.storagemanager.ApplicationContext;
019: import com.versant.core.util.OIDObjectOutput;
020: import com.versant.core.util.OIDObjectInput;
021: import com.versant.core.util.FastExternalizable;
022:
023: import javax.jdo.PersistenceManager;
024: import java.io.*;
025: import java.util.List;
026: import java.util.ArrayList;
027:
028: /**
029: * This is used to transport the query results to the client.
030: */
031: public final class QueryResultContainer implements FastExternalizable {
032:
033: public static final Object[] EMPTY_ARRAY = new Object[0];
034: /**
035: * The compiled qeury that this results is for.
036: */
037: private CompiledQuery cq;
038:
039: public StatesReturned container;
040: /**
041: * A flag to indicate that this is the last results from the results.
042: * i.e. for a forward only result set this will be the end.
043: */
044: public boolean qFinished;
045: /**
046: * The query results.
047: */
048: private Object[] data;
049: private int size;
050: private static final int DEFAULT_SIZE = 20;
051: public boolean allResults;
052:
053: public QueryResultContainer() {
054: }
055:
056: public QueryResultContainer(StatesReturned container) {
057: this .container = container;
058: }
059:
060: public QueryResultContainer(ApplicationContext context,
061: CompiledQuery cq) {
062: container = new StatesReturned(context);
063: init(cq);
064: }
065:
066: /**
067: * Add a row to the results.
068: *
069: * @param val
070: */
071: public void addRow(Object val) {
072: if (val == null) {
073: throw BindingSupportImpl
074: .getInstance()
075: .internal(
076: "Adding a 'Null' value to query result is not supported.s");
077: }
078: checkSize();
079: data[size++] = val;
080: }
081:
082: private void checkSize() {
083: if (data == null) {
084: data = new Object[DEFAULT_SIZE];
085: } else if (size == data.length) {
086: Object[] t = new Object[size * 2];
087: System.arraycopy(data, 0, t, 0, size);
088: data = t;
089: }
090: }
091:
092: public boolean isqFinished() {
093: return qFinished;
094: }
095:
096: public void setqFinished(boolean qFinished) {
097: this .qFinished = qFinished;
098: }
099:
100: /**
101: * This must be called after use.
102: * This clears all the internal datastructures.
103: */
104: public void reset() {
105: data = null;
106: container.clear();
107: qFinished = false;
108: size = 0;
109: allResults = false;
110: }
111:
112: /**
113: * This must be called on a query before use.
114: */
115: public void init(CompiledQuery cq, int capacity) {
116: reset();
117: this .cq = cq;
118: if (capacity < DEFAULT_SIZE) {
119: data = new Object[capacity];
120: } else {
121: data = new Object[DEFAULT_SIZE];
122: }
123: }
124:
125: /**
126: * This must be called on a query before use.
127: * This will not init the data array.
128: */
129: public void init(CompiledQuery cq) {
130: reset();
131: this .cq = cq;
132: }
133:
134: /**
135: * Add this query results to the stack.
136: * The container will be in a 'reset' state after calling this.
137: */
138: public void addToQueryStack(Stack stack) {
139: if (size != 0) {
140: stack.add(data, size);
141: }
142: data = null;
143: size = 0;
144: }
145:
146: /**
147: * This return's the backing array of the container itself.
148: * You must use size() or iterate to first null to determine the amount of valid entries.
149: */
150: public Object[] toResolvedObject(PersistenceManager pm) {
151: int size = this .size;
152: if (size == 0) {
153: return EMPTY_ARRAY;
154: }
155: Object[] res = data;
156: for (int i = 0; i < size; i++) {
157: res[i] = QueryResultBase.resolveRow(res[i], pm);
158: }
159: return res;
160: }
161:
162: public int size() {
163: return size;
164: }
165:
166: public Object getUnique() {
167: if (data == null)
168: return null;
169: else
170: return data[0];
171: }
172:
173: public Object get(int index) {
174: return data[index];
175: }
176:
177: /**
178: * Add the query results to the list and resolve any oids while doing so.
179: */
180: public void resolveAndAddTo(List l, VersantPersistenceManager pm) {
181: int size = this .size;
182: for (int i = 0; i < size; i++) {
183: l.add(QueryResultBase.resolveRow(data[i], pm));
184: }
185: }
186:
187: /**
188: * Add all our results to the cache container. This is used when the query results
189: * can be cached.
190: */
191: public void addResultsTo(CachedQueryResult cacheResults,
192: boolean copy) {
193: int size = this .size;
194: if (cacheResults.results == null) {
195: cacheResults.results = new ArrayList(size);
196: }
197:
198: if (copy
199:
200: ) {
201: //this is only done for if the row is a object[] and it contains oids.
202: for (int i = 0; i < size; i++) {
203: Object[] val = (Object[]) data[i];
204: Object[] cop = new Object[val.length];
205: System.arraycopy(val, 0, cop, 0, val.length);
206: cacheResults.results.add(cop);
207: }
208: } else {
209: for (int i = 0; i < size; i++) {
210: if (data[i] == null) {
211: throw BindingSupportImpl.getInstance().internal("");
212: }
213: cacheResults.results.add(data[i]);
214: }
215: }
216: container.addIndirectOIDs(cacheResults);
217: }
218:
219: /**
220: * This is used to fill this container from cached data.
221: */
222: public void fillFrom(CachedQueryResult qCacheContainer) {
223: final ArrayList lData = qCacheContainer.results;
224: final int n = size = lData.size();
225: if (data == null || n > data.length) {
226: data = new Object[n];
227: for (int i = 0; i < n; i++) {
228: data[i] = lData.get(i);
229: }
230: }
231: size = n;
232: for (int i = 0; i < n; i++) {
233: data[i] = lData.get(i);
234: }
235:
236: }
237:
238: public void dump() {
239: System.out
240: .println("\n\n$$$$$$$$$$$$$ START QueryResultContainer.dump $$$$$$$$$$$$$");
241: System.out.println("results = " + size);
242: int n = size;
243: for (int i = 0; i < n; i++) {
244: System.out.println("results.get(i) = " + data[i]);
245: }
246: System.out
247: .println("$$$$$$$$$$$$$ END QueryResultContainer.dump $$$$$$$$$$$$$\n\n");
248: }
249:
250: public void writeExternal(OIDObjectOutput out) throws IOException {
251: if (cq.isDefaultResult()) {
252: container.ensureDirect(data);
253: }
254: container.writeExternal(out);
255: if (allResults) {
256: out.writeByte(1);
257: } else {
258: out.writeByte(0);
259: }
260: out.writeBoolean(qFinished);
261:
262: if (data == null) {
263: out.writeInt(-1);
264: } else {
265: out.writeInt(size);
266: if (!cq.isDefaultResult()) {
267: out.writeBoolean(false);
268: int[] typeCodes = cq.getResultTypeCodes();
269: if (typeCodes == null || typeCodes.length == 0) {
270: out.writeInt(0);
271: for (int i = 0; i < size; i++) {
272: out.writeObject(data[i]);
273: }
274: } else {
275: out.writeInt(typeCodes.length);
276: for (int i = 0; i < typeCodes.length; i++) {
277: out.writeInt(typeCodes[i]);
278: }
279: if (typeCodes.length == 1) {
280: int typeCode = typeCodes[0];
281: for (int i = 0; i < size; i++) {
282: Object o = data[i];
283: if (typeCode == MDStatics.OID) {
284: OID oid = (OID) o;
285: out.write(oid);
286: } else {
287: SerUtils.writeSimpleField(typeCode,
288: out, o);
289: }
290: }
291: } else {
292: for (int i = 0; i < size; i++) {
293: Object[] row = (Object[]) data[i];
294: for (int j = 0; j < row.length; j++) {
295: Object o = row[j];
296: if (typeCodes[j] == MDStatics.OID) {
297: OID oid = (OID) o;
298: out.write(oid);
299: } else {
300: SerUtils.writeSimpleField(
301: typeCodes[j], out, o);
302: }
303: }
304: }
305: }
306: }
307: } else {
308: out.writeBoolean(true);
309: for (int i = 0; i < size; i++) {
310: out.writeObject(data[i]);
311: }
312: }
313: }
314: }
315:
316: public void readExternal(OIDObjectInput in) throws IOException,
317: ClassNotFoundException {
318: container = new StatesReturned();
319: container.readExternal(in);
320: allResults = in.readByte() == 1;
321: qFinished = in.readBoolean();
322: size = in.readInt();
323: if (size < 0) {
324: data = null;
325: size = 0;
326: } else {
327: data = new Object[size];
328: if (!in.readBoolean()) {
329: int tcLen = in.readInt();
330: if (tcLen == 0) {
331: for (int i = 0; i < size; i++) {
332: data[i] = in.readObject();
333: }
334: } else {
335: int[] typeCodes = new int[tcLen];
336: for (int i = 0; i < typeCodes.length; i++) {
337: typeCodes[i] = in.readInt();
338: }
339: if (typeCodes.length == 1) {
340: int typeCode = typeCodes[0];
341: for (int i = 0; i < size; i++) {
342: if (typeCode == MDStatics.OID) {
343: data[i] = in.readOID();
344: } else {
345: data[i] = SerUtils.readSimpleField(
346: typeCode, in);
347: }
348: }
349: } else {
350: for (int i = 0; i < size; i++) {
351: Object[] row = new Object[typeCodes.length];
352: data[i] = row;
353: for (int j = 0; j < row.length; j++) {
354: if (typeCodes[j] == MDStatics.OID) {
355: row[j] = in.readOID();
356: } else {
357: row[j] = SerUtils.readSimpleField(
358: typeCodes[j], in);
359: }
360: }
361: }
362: }
363: }
364: } else {
365: for (int i = 0; i < size; i++) {
366: data[i] = in.readObject();
367: }
368: }
369: }
370: }
371:
372: /**
373: * Will this be null if there is no data?
374: */
375: public Object[] getDataArray() {
376: return data;
377: }
378: }
|