001: // jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: // Copyright (C) 2004 The jTDS Project
003: //
004: // This library is free software; you can redistribute it and/or
005: // modify it under the terms of the GNU Lesser General Public
006: // License as published by the Free Software Foundation; either
007: // version 2.1 of the License, or (at your option) any later version.
008: //
009: // This library is distributed in the hope that it will be useful,
010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: // Lesser General Public License for more details.
013: //
014: // You should have received a copy of the GNU Lesser General Public
015: // License along with this library; if not, write to the Free Software
016: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.jdbc;
019:
020: import java.sql.SQLException;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.io.Reader;
024: import java.io.IOException;
025: import java.io.UnsupportedEncodingException;
026:
027: /**
028: * This class is a descriptor for procedure and prepared statement parameters.
029: *
030: * @author Mike Hutchinson
031: * @version $Id: ParamInfo.java,v 1.16 2005/04/25 11:47:01 alin_sinpalean Exp $
032: */
033: class ParamInfo implements Cloneable {
034: /** Flag as an input parameter. */
035: final static int INPUT = 0;
036: /** Flag as an output parameter. */
037: final static int OUTPUT = 1;
038: /** Flag as an return value parameter. */
039: final static int RETVAL = 2;
040: /** Flag as a unicode parameter. */
041: final static int UNICODE = 4;
042:
043: /** Internal TDS data type */
044: int tdsType;
045: /** JDBC type constant from java.sql.Types */
046: int jdbcType;
047: /** Formal parameter name eg @P1 */
048: String name;
049: /** SQL type name eg varchar(10) */
050: String sqlType;
051: /** Parameter offset in target SQL statement */
052: int markerPos = -1;
053: /** Current parameter value */
054: Object value;
055: /** Parameter decimal precision */
056: int precision = -1;
057: /** Parameter decimal scale */
058: int scale = -1;
059: /** Length of InputStream */
060: int length = -1;
061: /** Parameter is an output parameter */
062: boolean isOutput;
063: /** Parameter is used as SP return value */
064: boolean isRetVal;
065: /** IN parameter has been set */
066: boolean isSet;
067: /** Parameter should be sent as unicode */
068: boolean isUnicode;
069: /** TDS 8 Collation string. */
070: byte collation[];
071: /** Character set descriptor (if different from default) */
072: CharsetInfo charsetInfo;
073: /** OUT parameter value is set.*/
074: boolean isSetOut;
075: /** OUT Parameter value. */
076: Object outValue;
077:
078: /**
079: * Construct a parameter with parameter marker offset.
080: *
081: * @param pos the offset of the ? symbol in the target SQL string
082: * @param isUnicode <code>true</code> if the parameter is Unicode encoded
083: */
084: ParamInfo(int pos, boolean isUnicode) {
085: markerPos = pos;
086: this .isUnicode = isUnicode;
087: }
088:
089: /**
090: * Construct a parameter for statement caching.
091: *
092: * @param name the formal name of the parameter
093: * @param pos the offset of the ? symbol in the parsed SQL string
094: * @param isRetVal <code>true</code> if the parameter is a return value
095: * @param isUnicode <code>true</code> if the parameter is Unicode encoded
096: */
097: ParamInfo(String name, int pos, boolean isRetVal, boolean isUnicode) {
098: this .name = name;
099: this .markerPos = pos;
100: this .isRetVal = isRetVal;
101: this .isUnicode = isUnicode;
102: }
103:
104: /**
105: * Construct an initialised parameter with extra attributes.
106: *
107: * @param jdbcType the <code>java.sql.Type</code> constant describing this type
108: * @param value the initial parameter value
109: * @param flags the additional attributes eg OUTPUT, RETVAL, UNICODE etc.
110: */
111: ParamInfo(int jdbcType, Object value, int flags) {
112: this .jdbcType = jdbcType;
113: this .value = value;
114: this .isSet = true;
115: this .isOutput = ((flags & OUTPUT) > 0)
116: || ((flags & RETVAL) > 0);
117: this .isRetVal = ((flags & RETVAL) > 0);
118: this .isUnicode = ((flags & UNICODE) > 0);
119: if (value instanceof String) {
120: this .length = ((String) value).length();
121: } else if (value instanceof byte[]) {
122: this .length = ((byte[]) value).length;
123: }
124: }
125:
126: /**
127: * Construct a parameter based on a result set column.
128: *
129: * @param ci the column descriptor
130: * @param name the name for this parameter or null
131: * @param value the column data value
132: * @param length the column data length
133: */
134: ParamInfo(ColInfo ci, String name, Object value, int length) {
135: this .name = name;
136: this .tdsType = ci.tdsType;
137: this .scale = ci.scale;
138: this .precision = ci.precision;
139: this .jdbcType = ci.jdbcType;
140: this .sqlType = ci.sqlType;
141: this .collation = ci.collation;
142: this .charsetInfo = ci.charsetInfo;
143: this .isUnicode = TdsData.isUnicode(ci);
144: this .isSet = true;
145: this .value = value;
146: this .length = length;
147: }
148:
149: /**
150: * Get the output parameter value.
151: *
152: * @return the OUT value as an <code>Object</code>
153: * @throws SQLException if the parameter has not been set
154: */
155: Object getOutValue() throws SQLException {
156: if (!isSetOut) {
157: throw new SQLException(Messages
158: .get("error.callable.outparamnotset"), "HY010");
159: }
160: return outValue;
161: }
162:
163: /**
164: * Set the OUT parameter value.
165: * @param value The data value.
166: */
167: void setOutValue(Object value) {
168: outValue = value;
169: isSetOut = true;
170: }
171:
172: /**
173: * Clear the OUT parameter value and status.
174: */
175: void clearOutValue() {
176: outValue = null;
177: isSetOut = false;
178: }
179:
180: /**
181: * Clear the IN parameter value and status.
182: */
183: void clearInValue() {
184: value = null;
185: isSet = false;
186: }
187:
188: /**
189: * Get the string value of the parameter.
190: *
191: * @return The data value as a <code>String</code> or null.
192: */
193: String getString(String charset) throws IOException {
194: if (value == null || value instanceof String) {
195: return (String) value;
196: }
197:
198: if (value instanceof InputStream) {
199: try {
200: value = loadFromReader(new InputStreamReader(
201: (InputStream) value, charset), length);
202: length = ((String) value).length();
203:
204: return (String) value;
205: } catch (UnsupportedEncodingException e) {
206: throw new IOException(
207: "I/O Error: UnsupportedEncodingException: "
208: + e.getMessage());
209: }
210: }
211:
212: if (value instanceof Reader) {
213: value = loadFromReader((Reader) value, length);
214: return (String) value;
215: }
216:
217: return value.toString();
218: }
219:
220: /**
221: * Get the byte array value of the parameter.
222: *
223: * @return The data value as a <code>byte[]</code> or null.
224: */
225: byte[] getBytes(String charset) throws IOException {
226: if (value == null || value instanceof byte[]) {
227: return (byte[]) value;
228: }
229:
230: if (value instanceof InputStream) {
231: value = loadFromStream((InputStream) value, length);
232:
233: return (byte[]) value;
234: }
235:
236: if (value instanceof Reader) {
237: String tmp = loadFromReader((Reader) value, length);
238: value = Support.encodeString(charset, tmp);
239: return (byte[]) value;
240: }
241:
242: if (value instanceof String) {
243: return Support.encodeString(charset, (String) value);
244: }
245:
246: return new byte[0];
247: }
248:
249: /**
250: * Load a byte array from an InputStream
251: *
252: * @param in The InputStream to read from.
253: * @param length The length of the stream.
254: * @return The data as a <code>byte[]</code>.
255: * @throws IOException
256: */
257: private static byte[] loadFromStream(InputStream in, int length)
258: throws IOException {
259: byte[] buf = new byte[length];
260:
261: int pos = 0, res;
262: while (pos != length
263: && (res = in.read(buf, pos, length - pos)) != -1) {
264: pos += res;
265: }
266: if (pos != length) {
267: throw new java.io.IOException(
268: "Data in stream less than specified by length");
269: }
270:
271: if (in.read() >= 0) {
272: throw new java.io.IOException(
273: "More data in stream than specified by length");
274: }
275:
276: return buf;
277: }
278:
279: /**
280: * Create a String from a Reader stream.
281: *
282: * @param in The Reader object with the data.
283: * @param length Number of characters to read.
284: * @return The data as a <code>String</code>.
285: * @throws IOException
286: */
287: private static String loadFromReader(Reader in, int length)
288: throws IOException {
289: char[] buf = new char[length];
290:
291: int pos = 0, res;
292: while (pos != length
293: && (res = in.read(buf, pos, length - pos)) != -1) {
294: pos += res;
295: }
296: if (pos != length) {
297: throw new java.io.IOException(
298: "Data in stream less than specified by length");
299: }
300:
301: if (in.read() >= 0) {
302: throw new java.io.IOException(
303: "More data in stream than specified by length");
304: }
305:
306: return new String(buf);
307: }
308:
309: /**
310: * Creates a shallow copy of this <code>ParamInfo</code> instance. Used by
311: * the <code>PreparedStatement</code> batching implementation to duplicate
312: * parameters.
313: */
314: public Object clone() {
315: try {
316: return super .clone();
317: } catch (CloneNotSupportedException ex) {
318: // Will not happen
319: return null;
320: }
321: }
322: }
|