001: package com.quadcap.sql;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.Externalizable;
042: import java.io.IOException;
043: import java.io.ObjectInput;
044: import java.io.ObjectOutput;
045:
046: import java.util.HashMap;
047: import java.util.Iterator;
048: import java.util.Vector;
049:
050: import java.sql.SQLException;
051:
052: import com.quadcap.sql.types.Type;
053:
054: import com.quadcap.util.Debug;
055:
056: /**
057: * Base class for tuple implementations.
058: *
059: * @author Stan Bailes
060: */
061: public class TupleImpl implements Tuple, Externalizable {
062: protected String qual;
063: protected String qualifier;
064: protected Vector columns = new Vector();
065: protected HashMap names = new HashMap();
066:
067: /**
068: * Default constructor
069: */
070: public TupleImpl() {
071: this .qualifier = "";
072: this .qual = "";
073: }
074:
075: /**
076: * Named tuple constructor
077: */
078: public TupleImpl(String qualifier) {
079: setName(qualifier);
080: }
081:
082: /**
083: * Set the tuple's name
084: */
085: public void setName(String name) {
086: if (name == null)
087: name = "";
088: this .qualifier = name;
089: if (qualifier.length() > 0) {
090: this .qual = qualifier + ".";
091: } else {
092: this .qual = "";
093: }
094: }
095:
096: /**
097: * Add, in order, the columns from the definition of tuple 't' to
098: * the column list for this tuple.
099: */
100: public void addColumns(Session session, Tuple t)
101: throws SQLException {
102: addColumns(session, t, true);
103: }
104:
105: /**
106: * Add all the columns from the other tuple to this one.
107: */
108: public void addColumns(Session session, Tuple t, boolean resolve)
109: throws SQLException {
110: Connection conn = session.getConnection();
111: boolean as = qual.length() > 0;
112: for (int i = 1; i <= t.getColumnCount(); i++) {
113: Column col = t.getColumn(i);
114: String name = conn.resolveColname(col.getName(), this );
115: //Debug.println("[" + getClass().getName() + "].addColumns " + i + ": " +
116: // col.getName() + "/" + col.getShortName() + "/" + name);
117: Column ncol = new Column(name, col);
118: if (col.isJoinColumn())
119: ncol.setJoinColumn(true);
120: addColumn(ncol);
121: }
122: if (resolve)
123: resolveColumns();
124: }
125:
126: /**
127: * Once all of the column names are available, we can look for duplicates
128: * and shortest-unique names, etc.
129: */
130: public void resolveColumns() throws SQLException {
131: for (int i = 1; i <= getColumnCount(); i++) {
132: Column col = getColumn(i);
133: String name = col.getName();
134: for (int j = name.length() - 1; j > 0; j--) {
135: if (name.charAt(j) == '.') {
136: String qname = name.substring(j + 1);
137: Object obj = names.get(qname);
138: if (obj == col) {
139: col.setShortName(qname);
140: break;
141: }
142: }
143: }
144: }
145: //Debug.println("resolveColumns: " + this);
146: }
147:
148: /**
149: * addColumn helper
150: */
151: private final void addName(String name, Column col) {
152: if (names.get(name) != null) {
153: names.put(name, "duplicate");
154: } else {
155: names.put(name, col);
156: }
157: }
158:
159: /**
160: * Return the number of columns in this tuple
161: */
162: public int getColumnCount() throws SQLException {
163: return columns.size();
164: }
165:
166: /**
167: * Return the specified (one-based) column
168: */
169: public Column getColumn(int i) throws SQLException {
170: return (Column) columns.elementAt(i - 1);
171: }
172:
173: /**
174: * Return the column with the specified name/shortname
175: */
176: public Column getColumn(String name) throws SQLException {
177: Column c = null;
178: Object obj = names.get(name);
179: if (obj != null && obj instanceof Column) {
180: c = (Column) obj;
181: }
182: return c;
183: }
184:
185: /**
186: * Map the specified column names into an array of integer (one-based)
187: * column indexes
188: */
189: public int[] mapColumns(Vector names) throws SQLException {
190: int[] ret;
191: if (names == null) {
192: ret = new int[columns.size()];
193: for (int i = 0; i < ret.length; i++) {
194: ret[i] = i + 1;
195: }
196: } else {
197: ret = new int[names.size()];
198: for (int i = 0; i < ret.length; i++) {
199: String name = (String) names.elementAt(i);
200: ret[i] = getColumnIndex(name);
201: if (ret[i] <= 0) {
202: throw new SQLException("No such column: " + name,
203: "42000");
204: }
205: }
206: }
207: return ret;
208: }
209:
210: /**
211: * Return the tuple's name
212: */
213: public String getName() {
214: return qualifier;
215: }
216:
217: /**
218: * Find the first occurence of the '.' name separator in
219: * the given SQL identifier string.
220: */
221: static int nextUnquotedPeriod(String s) {
222: boolean q = false;
223: for (int i = 0; i < s.length(); i++) {
224: char c = s.charAt(i);
225: if (c == '"')
226: q = !q;
227: if (!q && c == '.')
228: return i;
229: }
230: return -1;
231: }
232:
233: /**
234: * Add a new column to the tuple
235: */
236: public void addColumn(Column col) throws SQLException {
237: columns.addElement(col);
238: col.setColumn(columns.size());
239: col.setTable(this );
240: String name = col.getName();
241: addName(name, col);
242: int idx = nextUnquotedPeriod(name);
243: while (idx >= 0) {
244: name = name.substring(idx + 1);
245: addName(name, col);
246: idx = nextUnquotedPeriod(name);
247: }
248: }
249:
250: /**
251: * Add a new column to the tuple in a specified position,
252: * moving columns at that position and greater on position
253: * to the right.
254: */
255: public void addColumn(Column col, int pos) throws SQLException {
256: columns.addElement(col);
257: int np = columns.size();
258: while (np > pos) {
259: Column col2 = (Column) columns.get(np - 2);
260: col2.setColumn(np);
261: columns.set(np - 1, col2);
262: np--;
263: }
264: columns.set(pos - 1, col);
265: col.setColumn(pos);
266: col.setTable(this );
267: String name = col.getName();
268: addName(name, col);
269: int idx = nextUnquotedPeriod(name);
270: while (idx >= 0) {
271: name = name.substring(idx + 1);
272: addName(name, col);
273: idx = nextUnquotedPeriod(name);
274: }
275: }
276:
277: /**
278: * Add a column of the specified name and type to the tuple
279: */
280: public void addColumn(String name, Type type) throws SQLException {
281: addColumn(new Column(name, type));
282: }
283:
284: /**
285: * Delete the specified column
286: */
287: public void deleteColumn(int col) throws SQLException, IOException {
288: Column delCol = getColumn(col);
289: int del = 0;
290: for (int i = 1; i <= columns.size(); i++) {
291: if (i == col)
292: del++;
293: else {
294: Column cx = getColumn(i);
295: cx.setColumn(i - del);
296: columns.set(i - del - 1, cx);
297: }
298: }
299: columns.setSize(columns.size() - 1);
300:
301: Iterator iter = names.keySet().iterator();
302: while (iter.hasNext()) {
303: String name = iter.next().toString();
304: if (names.get(name) == delCol)
305: iter.remove();
306: }
307: }
308:
309: /**
310: * Return the (one-based) column index of the column with the
311: * specified name
312: */
313: public int getColumnIndex(String name) throws SQLException {
314: Column col = getColumn(name);
315: if (col == null) {
316: return -1;
317: }
318: return col.getColumn();
319: }
320:
321: /**
322: * Externalizable.readExternal(): Read me from a stream.
323: */
324: public void readExternal(ObjectInput in) throws IOException,
325: ClassNotFoundException {
326: qualifier = (String) in.readObject();
327: if (qualifier.length() > 0) {
328: this .qual = qualifier + ".";
329: } else {
330: this .qual = "";
331: }
332: Vector c = (Vector) in.readObject();
333: this .columns = new Vector();
334: this .names = new HashMap();
335: for (int i = 0; i < c.size(); i++) {
336: try {
337: addColumn((Column) c.elementAt(i));
338: } catch (SQLException e) {
339: //#ifdef DEBUG
340: Debug.print(e);
341: //#endif
342: throw new IOException(e.toString());
343: }
344: }
345: try {
346: resolveColumns();
347: } catch (SQLException e) {
348: //#ifdef DEBUG
349: Debug.print(e);
350: //#endif
351: throw new IOException(e.toString());
352: }
353: }
354:
355: /**
356: * Externalizable.writeExternal(): Write me to a stream.
357: */
358: public void writeExternal(ObjectOutput out) throws IOException {
359: out.writeObject(qualifier);
360: out.writeObject(columns);
361: }
362:
363: //#ifdef DEBUG
364: /**
365: * Return a string representation for debugging
366: */
367: public String toString() {
368: try {
369: StringBuffer sb = new StringBuffer(Table.strip(getClass()
370: .getName()));
371: sb.append(" ");
372: sb.append(qualifier);
373: sb.append("\n");
374: for (int i = 1; i <= getColumnCount(); i++) {
375: Column c = getColumn(i);
376: if (i > 1)
377: sb.append("\n");
378: sb.append(" col " + i + ": ");
379: sb.append(c.toString());
380: }
381: sb.append("\n");
382: //sb.append(names.toString());
383: return sb.toString();
384: } catch (Exception e) {
385: Debug.print(e);
386: return e.toString();
387: }
388: }
389: //#endif
390:
391: }
|