0001: /*
0002:
0003: Derby - Class org.apache.derby.iapi.types.SQLChar
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.iapi.types;
0023:
0024: import org.apache.derby.iapi.services.context.ContextService;
0025:
0026: import org.apache.derby.iapi.services.sanity.SanityManager;
0027:
0028: import org.apache.derby.iapi.services.io.Storable;
0029: import org.apache.derby.iapi.services.io.StoredFormatIds;
0030: import org.apache.derby.iapi.services.io.StreamStorable;
0031: import org.apache.derby.iapi.services.io.FormatIdInputStream;
0032:
0033: import org.apache.derby.iapi.types.DataTypeDescriptor;
0034: import org.apache.derby.iapi.types.DataValueDescriptor;
0035: import org.apache.derby.iapi.types.TypeId;
0036: import org.apache.derby.iapi.types.StringDataValue;
0037: import org.apache.derby.iapi.types.NumberDataValue;
0038: import org.apache.derby.iapi.types.BooleanDataValue;
0039: import org.apache.derby.iapi.types.ConcatableDataValue;
0040: import org.apache.derby.iapi.reference.SQLState;
0041:
0042: import org.apache.derby.iapi.error.StandardException;
0043:
0044: import org.apache.derby.iapi.services.cache.ClassSize;
0045: import org.apache.derby.iapi.services.io.ArrayInputStream;
0046: import org.apache.derby.iapi.util.StringUtil;
0047: import org.apache.derby.iapi.services.i18n.LocaleFinder;
0048:
0049: import org.apache.derby.iapi.db.DatabaseContext;
0050:
0051: import org.apache.derby.iapi.types.SQLInteger;
0052: import org.apache.derby.iapi.types.SQLDate;
0053: import org.apache.derby.iapi.types.SQLTime;
0054: import org.apache.derby.iapi.types.SQLTimestamp;
0055:
0056: import java.io.InputStream;
0057: import java.io.ObjectOutput;
0058: import java.io.ObjectInput;
0059: import java.io.IOException;
0060: import java.io.UTFDataFormatException;
0061: import java.io.EOFException;
0062: import java.sql.Date;
0063: import java.sql.ResultSet;
0064: import java.sql.PreparedStatement;
0065: import java.sql.SQLException;
0066: import java.sql.Time;
0067: import java.sql.Timestamp;
0068: import java.text.CollationElementIterator;
0069: import java.text.RuleBasedCollator;
0070: import java.text.CollationKey;
0071: import java.text.DateFormat;
0072: import java.util.Locale;
0073: import java.util.Calendar;
0074:
0075: /**
0076: * SQLChar satisfies the DataValueDescriptor
0077: * interfaces (i.e., OrderableDataType). It implements an String holder,
0078: * e.g. for storing a column value; it can be specified
0079: * when constructed to not allow nulls. Nullability cannot be changed
0080: * after construction.
0081: * <p>
0082: * Because OrderableDataType is a subclass of DataType,
0083: * SQLChar can play a role in either a DataType/ValueRow
0084: * or a OrderableDataType/KeyRow, interchangeably.
0085: */
0086: public class SQLChar extends DataType implements StringDataValue,
0087: StreamStorable {
0088:
0089: /**
0090: * threshold, that decides when we return space back to the VM
0091: * see getString() where it is used
0092: */
0093: protected final static int RETURN_SPACE_THRESHOLD = 4096;
0094:
0095: /**
0096: * when we know that the array needs to grow by at least
0097: * one byte, it is not performant to grow by just one byte
0098: * instead this amount is used to provide a reasonable growby size.
0099: */
0100: private final static int GROWBY_FOR_CHAR = 64;
0101: /**
0102: Static array that can be used for blank padding.
0103: */
0104: private static final char[] BLANKS = new char[40];
0105: static {
0106: for (int i = 0; i < BLANKS.length; i++) {
0107: BLANKS[i] = ' ';
0108: }
0109: }
0110:
0111: private static void appendBlanks(char[] ca, int offset, int howMany) {
0112: while (howMany > 0) {
0113:
0114: int count = howMany > BLANKS.length ? BLANKS.length
0115: : howMany;
0116:
0117: System.arraycopy(BLANKS, 0, ca, offset, count);
0118: howMany -= count;
0119: offset += count;
0120: }
0121: }
0122:
0123: /*
0124: * DataValueDescriptor interface
0125: * (mostly implemented in DataType)
0126: * casts to the
0127: * numeric and date/time types as well, "for valid strings"
0128: */
0129:
0130: /**
0131: * @see DataValueDescriptor#getBoolean
0132: *
0133: * @exception StandardException Thrown on error
0134: */
0135: public boolean getBoolean() throws StandardException {
0136: if (isNull())
0137: return false;
0138:
0139: // match JCC, match only "0" or "false" for false. No case insensitivity.
0140: // everything else is true.
0141:
0142: String cleanedValue = getString().trim();
0143:
0144: return !(cleanedValue.equals("0") || cleanedValue
0145: .equals("false"));
0146: }
0147:
0148: /**
0149: * @see DataValueDescriptor#getByte
0150: * @exception StandardException thrown on failure to convert
0151: */
0152: public byte getByte() throws StandardException {
0153: if (isNull())
0154: return (byte) 0;
0155: try {
0156: return Byte.parseByte(getString().trim());
0157: } catch (NumberFormatException nfe) {
0158: throw StandardException.newException(
0159: SQLState.LANG_FORMAT_EXCEPTION, "byte");
0160: }
0161: }
0162:
0163: /**
0164: * @see DataValueDescriptor#getShort
0165: * @exception StandardException thrown on failure to convert
0166: */
0167: public short getShort() throws StandardException {
0168: if (isNull())
0169: return (short) 0;
0170: try {
0171: return Short.parseShort(getString().trim());
0172: } catch (NumberFormatException nfe) {
0173: throw StandardException.newException(
0174: SQLState.LANG_FORMAT_EXCEPTION, "short");
0175: }
0176: }
0177:
0178: /**
0179: * @see DataValueDescriptor#getInt
0180: * @exception StandardException thrown on failure to convert
0181: */
0182: public int getInt() throws StandardException {
0183: if (isNull())
0184: return 0;
0185: try {
0186: return Integer.parseInt(getString().trim());
0187: } catch (NumberFormatException nfe) {
0188: throw StandardException.newException(
0189: SQLState.LANG_FORMAT_EXCEPTION, "int");
0190: }
0191: }
0192:
0193: /**
0194: * @see DataValueDescriptor#getLong
0195: * @exception StandardException thrown on failure to convert
0196: */
0197: public long getLong() throws StandardException {
0198: if (isNull())
0199: return 0;
0200: try {
0201: return Long.parseLong(getString().trim());
0202: } catch (NumberFormatException nfe) {
0203: throw StandardException.newException(
0204: SQLState.LANG_FORMAT_EXCEPTION, "long");
0205: }
0206: }
0207:
0208: /**
0209: * @see DataValueDescriptor#getFloat
0210: * @exception StandardException thrown on failure to convert
0211: */
0212: public float getFloat() throws StandardException {
0213: if (isNull())
0214: return 0;
0215: try {
0216: return new Float(getString().trim()).floatValue();
0217: } catch (NumberFormatException nfe) {
0218: throw StandardException.newException(
0219: SQLState.LANG_FORMAT_EXCEPTION, "float");
0220: }
0221: }
0222:
0223: /**
0224: * @see DataValueDescriptor#getDouble
0225: * @exception StandardException thrown on failure to convert
0226: */
0227: public double getDouble() throws StandardException {
0228: if (isNull())
0229: return 0;
0230: try {
0231: return new Double(getString().trim()).doubleValue();
0232: } catch (NumberFormatException nfe) {
0233: throw StandardException.newException(
0234: SQLState.LANG_FORMAT_EXCEPTION, "double");
0235: }
0236: }
0237:
0238: /**
0239: * CHAR/VARCHAR/LONG VARCHAR implementation. Convert to a BigDecimal using getString.
0240: */
0241: public int typeToBigDecimal() throws StandardException {
0242: return java.sql.Types.CHAR;
0243: }
0244:
0245: /**
0246: * @see DataValueDescriptor#getDate
0247: * @exception StandardException thrown on failure to convert
0248: */
0249: public Date getDate(Calendar cal) throws StandardException {
0250: return getDate(cal, getString(), getLocaleFinder());
0251: }
0252:
0253: public static Date getDate(java.util.Calendar cal, String str,
0254: LocaleFinder localeFinder) throws StandardException {
0255: if (str == null)
0256: return null;
0257: SQLDate internalDate = new SQLDate(str, false, localeFinder);
0258: return internalDate.getDate(cal);
0259: }
0260:
0261: /**
0262: * @see DataValueDescriptor#getTime
0263: * @exception StandardException thrown on failure to convert
0264: */
0265: public Time getTime(Calendar cal) throws StandardException {
0266: return getTime(cal, getString(), getLocaleFinder());
0267: }
0268:
0269: /**
0270: * @exception StandardException thrown on failure to convert
0271: */
0272: public static Time getTime(Calendar cal, String str,
0273: LocaleFinder localeFinder) throws StandardException {
0274: if (str == null)
0275: return null;
0276: SQLTime internalTime = new SQLTime(str, false, localeFinder,
0277: cal);
0278: return internalTime.getTime(cal);
0279: }
0280:
0281: /**
0282: * @see DataValueDescriptor#getTimestamp
0283: * @exception StandardException thrown on failure to convert
0284: */
0285: public Timestamp getTimestamp(Calendar cal)
0286: throws StandardException {
0287: return getTimestamp(cal, getString(), getLocaleFinder());
0288: }
0289:
0290: /**
0291: * @see DataValueDescriptor#getTimestamp
0292: * @exception StandardException thrown on failure to convert
0293: */
0294: public static Timestamp getTimestamp(java.util.Calendar cal,
0295: String str, LocaleFinder localeFinder)
0296: throws StandardException {
0297: if (str == null)
0298: return null;
0299: SQLTimestamp internalTimestamp = new SQLTimestamp(str, false,
0300: localeFinder, cal);
0301: return internalTimestamp.getTimestamp(cal);
0302: }
0303:
0304: /**
0305: * @exception StandardException Thrown on error
0306: */
0307: public Object getObject() throws StandardException {
0308: return getString();
0309: }
0310:
0311: /**
0312: * @exception StandardException Thrown on error
0313: */
0314: public InputStream getStream() throws StandardException {
0315: return stream;
0316: }
0317:
0318: /**
0319: * @exception StandardException Thrown on error
0320: */
0321: public int getLength() throws StandardException {
0322: if (rawLength != -1)
0323: return rawLength;
0324:
0325: String tmpString = getString();
0326: return (tmpString == null) ? 0 : tmpString.length();
0327: }
0328:
0329: public String getTypeName() {
0330: return TypeId.CHAR_NAME;
0331: }
0332:
0333: /**
0334: * If possible, use getCharArray() if you don't really
0335: * need a string. getString() will cause an extra
0336: * char array to be allocated when it calls the the String()
0337: * constructor (the first time through), so may be
0338: * cheaper to use getCharArray().
0339: *
0340: * @exception StandardException Thrown on error
0341: */
0342: public String getString() throws StandardException {
0343: if (value == null) {
0344:
0345: int len = rawLength;
0346:
0347: if (len != -1) {
0348:
0349: // data is stored in the char[] array
0350:
0351: value = new String(rawData, 0, len);
0352: if (len > RETURN_SPACE_THRESHOLD) {
0353: // free up this char[] array to reduce memory usage
0354: rawData = null;
0355: rawLength = -1;
0356: // clear out the int array as well, so it will stay current
0357: intArray = null;
0358: intLength = 0;
0359: cKey = null;
0360: }
0361:
0362: } else if (stream != null) {
0363:
0364: // data stored as a stream
0365: try {
0366:
0367: if (stream instanceof FormatIdInputStream) {
0368: readExternal((FormatIdInputStream) stream);
0369: } else {
0370: readExternal(new FormatIdInputStream(stream));
0371: }
0372: stream = null;
0373:
0374: // at this point the value is only in the char[]
0375: // so call again to convert to a String
0376: return getString();
0377:
0378: } catch (IOException ioe) {
0379:
0380: throw StandardException
0381: .newException(
0382: SQLState.LANG_STREAMING_COLUMN_I_O_EXCEPTION,
0383: ioe, "java.sql.String");
0384: }
0385: }
0386: }
0387:
0388: return value;
0389: }
0390:
0391: /**
0392: * Get a char array. Typically, this is a simple
0393: * getter that is cheaper than getString() because
0394: * we always need to create a char array when
0395: * doing I/O. Use this instead of getString() where
0396: * reasonable.
0397: * <p>
0398: * <b>WARNING</b>: may return a character array that has spare
0399: * characters at the end. MUST be used in conjunction
0400: * with getLength() to be safe.
0401: *
0402: * @exception StandardException Thrown on error
0403: */
0404: public char[] getCharArray() throws StandardException {
0405: if (isNull()) {
0406: return (char[]) null;
0407: } else if (rawLength != -1) {
0408: return rawData;
0409: } else {
0410: // this is expensive -- we are getting a
0411: // copy of the char array that the
0412: // String wrapper uses.
0413: getString();
0414: rawData = value.toCharArray();
0415: rawLength = rawData.length;
0416: // clear out the int array as well, so it will stay current
0417: intArray = null;
0418: intLength = 0;
0419: cKey = null;
0420: return rawData;
0421: }
0422: }
0423:
0424: /*
0425: * StreamStorable interface :
0426: */
0427: public InputStream returnStream() {
0428: return stream;
0429: }
0430:
0431: /**
0432: * Set this value to the on-disk format stream.
0433: */
0434: public final void setStream(InputStream newStream) {
0435: this .value = null;
0436: this .rawLength = -1;
0437: this .stream = newStream;
0438: // clear out the int array as well, so it will stay current
0439: intArray = null;
0440: intLength = 0;
0441: cKey = null;
0442: }
0443:
0444: public void loadStream() throws StandardException {
0445: getString();
0446: }
0447:
0448: /*
0449: * Storable interface, implies Externalizable, TypedFormat
0450: */
0451:
0452: /**
0453: Return my format identifier.
0454:
0455: @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
0456: */
0457: public int getTypeFormatId() {
0458: return StoredFormatIds.SQL_CHAR_ID;
0459: }
0460:
0461: /**
0462: * see if the String value is null.
0463: @see Storable#isNull
0464: */
0465: public boolean isNull() {
0466: return ((value == null) && (rawLength == -1) && (stream == null));
0467: }
0468:
0469: /**
0470: The maximum stored size is based upon the UTF format
0471: used to stored the String. The format consists of
0472: a two byte length field and a maximum number of three
0473: bytes for each character.
0474: <BR>
0475: This puts an upper limit on the length of a stored
0476: String. The maximum stored length is 65535, these leads to
0477: the worse case of a maximum string length of 21844 ((65535 - 2) / 3).
0478: <BR>
0479: Strings with stored length longer than 64K is handled with
0480: the following format:
0481: (1) 2 byte length: will be assigned 0.
0482: (2) UTF formated string data.
0483: (3) terminate the string with the following 3 bytes:
0484: first byte is:
0485: +---+---+---+---+---+---+---+---+
0486: | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
0487: +---+---+---+---+---+---+---+---+
0488: second byte is:
0489: +---+---+---+---+---+---+---+---+
0490: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0491: +---+---+---+---+---+---+---+---+
0492: third byte is:
0493: +---+---+---+---+---+---+---+---+
0494: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0495: +---+---+---+---+---+---+---+---+
0496:
0497:
0498: The UTF format:
0499: Writes a string to the underlying output stream using UTF-8
0500: encoding in a machine-independent manner.
0501: <p>
0502: First, two bytes are written to the output stream as if by the
0503: <code>writeShort</code> method giving the number of bytes to
0504: follow. This value is the number of bytes actually written out,
0505: not the length of the string. Following the length, each character
0506: of the string is output, in sequence, using the UTF-8 encoding
0507: for the character.
0508: @exception IOException if an I/O error occurs.
0509: @since JDK1.0
0510:
0511:
0512: @exception IOException thrown by writeUTF
0513:
0514: @see java.io.DataInputStream
0515:
0516: */
0517: public void writeExternal(ObjectOutput out) throws IOException {
0518: // never called when value is null
0519: if (SanityManager.DEBUG)
0520: SanityManager.ASSERT(!isNull());
0521:
0522: String lvalue = null;
0523: char[] data = null;
0524:
0525: int strlen = rawLength;
0526: boolean isRaw;
0527:
0528: if (strlen < 0) {
0529: lvalue = value;
0530: strlen = lvalue.length();
0531: isRaw = false;
0532: } else {
0533: data = rawData;
0534: isRaw = true;
0535: }
0536:
0537: // byte length will always be at least string length
0538: int utflen = strlen;
0539:
0540: for (int i = 0; (i < strlen) && (utflen <= 65535); i++) {
0541: int c = isRaw ? data[i] : lvalue.charAt(i);
0542: if ((c >= 0x0001) && (c <= 0x007F)) {
0543: // 1 byte for character
0544: } else if (c > 0x07FF) {
0545: utflen += 2; // 3 bytes for character
0546: } else {
0547: utflen += 1; // 2 bytes for character
0548: }
0549: }
0550:
0551: boolean isLongUTF = false;
0552: // for length than 64K, see format description above
0553: if (utflen > 65535) {
0554: isLongUTF = true;
0555: utflen = 0;
0556: }
0557:
0558: out.write((utflen >>> 8) & 0xFF);
0559: out.write((utflen >>> 0) & 0xFF);
0560: for (int i = 0; i < strlen; i++) {
0561: int c = isRaw ? data[i] : lvalue.charAt(i);
0562: if ((c >= 0x0001) && (c <= 0x007F)) {
0563: out.write(c);
0564: } else if (c > 0x07FF) {
0565: out.write(0xE0 | ((c >> 12) & 0x0F));
0566: out.write(0x80 | ((c >> 6) & 0x3F));
0567: out.write(0x80 | ((c >> 0) & 0x3F));
0568: } else {
0569: out.write(0xC0 | ((c >> 6) & 0x1F));
0570: out.write(0x80 | ((c >> 0) & 0x3F));
0571: }
0572: }
0573:
0574: if (isLongUTF) {
0575: // write the following 3 bytes to terminate the string:
0576: // (11100000, 00000000, 00000000)
0577: out.write(0xE0);
0578: out.write(0);
0579: out.write(0);
0580: }
0581: }
0582:
0583: /**
0584: * Reads in a string from the specified data input stream. The
0585: * string has been encoded using a modified UTF-8 format.
0586: * <p>
0587: * The first two bytes are read as if by
0588: * <code>readUnsignedShort</code>. This value gives the number of
0589: * following bytes that are in the encoded string, not
0590: * the length of the resulting string. The following bytes are then
0591: * interpreted as bytes encoding characters in the UTF-8 format
0592: * and are converted into characters.
0593: * <p>
0594: * This method blocks until all the bytes are read, the end of the
0595: * stream is detected, or an exception is thrown.
0596: *
0597: * @param in a data input stream.
0598: * @exception EOFException if the input stream reaches the end
0599: * before all the bytes.
0600: * @exception IOException if an I/O error occurs.
0601: * @exception UTFDataFormatException if the bytes do not represent a
0602: * valid UTF-8 encoding of a Unicode string.
0603: * @see java.io.DataInputStream#readUnsignedShort()
0604:
0605: * @see java.io.Externalizable#readExternal
0606: */
0607: public void readExternalFromArray(ArrayInputStream in)
0608: throws IOException {
0609: arg_passer[0] = rawData;
0610:
0611: rawLength = in.readCloudscapeUTF(arg_passer);
0612:
0613: rawData = arg_passer[0];
0614:
0615: // restoreToNull();
0616: value = null;
0617: stream = null;
0618:
0619: // clear out the int array, so it will stay current
0620: intArray = null;
0621: intLength = 0;
0622: cKey = null;
0623: }
0624:
0625: char[][] arg_passer = new char[1][];
0626:
0627: public void readExternal(ObjectInput in) throws IOException {
0628: // if in.available() blocked at 0, use this default string size
0629:
0630: int utflen = in.readUnsignedShort();
0631:
0632: int requiredLength;
0633: // minimum amount that is reasonable to grow the array
0634: // when we know the array needs to growby at least one
0635: // byte but we dont want to grow by one byte as that
0636: // is not performant
0637: int minGrowBy = growBy();
0638: if (utflen != 0) {
0639: // the object was not stored as a streaming column
0640: // we know exactly how long it is
0641: requiredLength = utflen;
0642: } else {
0643: // the object was stored as a streaming column
0644: // and we have a clue how much we can read unblocked
0645: // OR
0646: // The original string was a 0 length string.
0647: requiredLength = in.available();
0648: if (requiredLength < minGrowBy)
0649: requiredLength = minGrowBy;
0650: }
0651:
0652: char str[];
0653: if ((rawData == null) || (requiredLength > rawData.length)) {
0654:
0655: str = new char[requiredLength];
0656: } else {
0657: str = rawData;
0658: }
0659: int arrayLength = str.length;
0660:
0661: // Set these to null to allow GC of the array if required.
0662: rawData = null;
0663: restoreToNull();
0664:
0665: int count = 0;
0666: int strlen = 0;
0667:
0668: readingLoop: while (((count < utflen) || (utflen == 0))) {
0669: int c;
0670:
0671: try {
0672:
0673: c = in.readUnsignedByte();
0674: } catch (EOFException eof) {
0675: if (utflen != 0)
0676: throw new EOFException();
0677:
0678: // This is the case for a 0 length string.
0679: // OR the string was originally streamed in
0680: // which puts a 0 for utflen but no trailing
0681: // E0,0,0 markers.
0682: break readingLoop;
0683: }
0684:
0685: //if (c == -1) // read EOF
0686: //{
0687: // if (utflen != 0)
0688: // throw new EOFException();
0689:
0690: // break;
0691: //}
0692:
0693: // change it to an unsigned byte
0694: //c &= 0xFF;
0695:
0696: if (strlen >= arrayLength) // the char array needs to be grown
0697: {
0698: int growby = in.available();
0699: // We know that the array needs to be grown by at least one.
0700: // However, even if the input stream wants to block on every
0701: // byte, we don't want to grow by a byte at a time.
0702: // Note, for large data (clob > 32k), it is performant
0703: // to grow the array by atleast 4k rather than a small amount
0704: // Even better maybe to grow by 32k but then may be
0705: // a little excess(?) for small data.
0706: // hopefully in.available() will give a fair
0707: // estimate of how much data can be read to grow the
0708: // array by larger and necessary chunks.
0709: // This performance issue due to
0710: // the slow growth of this array was noticed since inserts
0711: // on clobs was taking a really long time as
0712: // the array here grew previously by 64 bytes each time
0713: // till stream was drained. (Derby-302)
0714: // for char, growby 64 seems reasonable, but for varchar
0715: // clob 4k or 32k is performant and hence
0716: // growBy() is override correctly to ensure this
0717: if (growby < minGrowBy)
0718: growby = minGrowBy;
0719:
0720: int newstrlength = arrayLength + growby;
0721: char oldstr[] = str;
0722: str = new char[newstrlength];
0723:
0724: System.arraycopy(oldstr, 0, str, 0, arrayLength);
0725: arrayLength = newstrlength;
0726: }
0727:
0728: /// top fours bits of the first unsigned byte that maps to a
0729: // 1,2 or 3 byte character
0730: //
0731: // 0000xxxx - 0 - 1 byte char
0732: // 0001xxxx - 1 - 1 byte char
0733: // 0010xxxx - 2 - 1 byte char
0734: // 0011xxxx - 3 - 1 byte char
0735: // 0100xxxx - 4 - 1 byte char
0736: // 0101xxxx - 5 - 1 byte char
0737: // 0110xxxx - 6 - 1 byte char
0738: // 0111xxxx - 7 - 1 byte char
0739: // 1000xxxx - 8 - error
0740: // 1001xxxx - 9 - error
0741: // 1010xxxx - 10 - error
0742: // 1011xxxx - 11 - error
0743: // 1100xxxx - 12 - 2 byte char
0744: // 1101xxxx - 13 - 2 byte char
0745: // 1110xxxx - 14 - 3 byte char
0746: // 1111xxxx - 15 - error
0747:
0748: int char2, char3;
0749: char actualChar;
0750: if ((c & 0x80) == 0x00) {
0751: // one byte character
0752: count++;
0753: actualChar = (char) c;
0754: } else if ((c & 0x60) == 0x40) // we know the top bit is set here
0755: {
0756: // two byte character
0757: count += 2;
0758: if (utflen != 0 && count > utflen)
0759: throw new UTFDataFormatException();
0760: char2 = in.readUnsignedByte();
0761: if ((char2 & 0xC0) != 0x80)
0762: throw new UTFDataFormatException();
0763: actualChar = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
0764: } else if ((c & 0x70) == 0x60) // we know the top bit is set here
0765: {
0766: // three byte character
0767: count += 3;
0768: if (utflen != 0 && count > utflen)
0769: throw new UTFDataFormatException();
0770: char2 = in.readUnsignedByte();
0771: char3 = in.readUnsignedByte();
0772: if ((c == 0xE0) && (char2 == 0) && (char3 == 0)
0773: && (utflen == 0)) {
0774: // we reached the end of a long string,
0775: // that was terminated with
0776: // (11100000, 00000000, 00000000)
0777: break readingLoop;
0778: }
0779:
0780: if (((char2 & 0xC0) != 0x80)
0781: || ((char3 & 0xC0) != 0x80))
0782: throw new UTFDataFormatException();
0783:
0784: actualChar = (char) (((c & 0x0F) << 12)
0785: | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
0786: } else {
0787:
0788: throw new UTFDataFormatException();
0789: }
0790:
0791: str[strlen++] = actualChar;
0792: }
0793:
0794: rawData = str;
0795: rawLength = strlen;
0796:
0797: // clear out the int array, so it will stay current
0798: intArray = null;
0799: intLength = 0;
0800: cKey = null;
0801: }
0802:
0803: /**
0804: * returns the reasonable minimum amount by
0805: * which the array can grow . See readExternal.
0806: * when we know that the array needs to grow by at least
0807: * one byte, it is not performant to grow by just one byte
0808: * instead this amount is used to provide a resonable growby size.
0809: * @return minimum reasonable growby size
0810: */
0811: protected int growBy() {
0812: return GROWBY_FOR_CHAR; //seems reasonable for a char
0813: }
0814:
0815: /**
0816: * @see Storable#restoreToNull
0817: *
0818: */
0819: public void restoreToNull() {
0820: value = null;
0821: stream = null;
0822: rawLength = -1;
0823: // clear out the int array as well, so it will stay current
0824: intArray = null;
0825: intLength = 0;
0826: cKey = null;
0827: }
0828:
0829: /**
0830: @exception StandardException thrown on error
0831: */
0832: public boolean compare(int op, DataValueDescriptor other,
0833: boolean orderedNulls, boolean unknownRV)
0834: throws StandardException {
0835: if (!orderedNulls) // nulls are unordered
0836: {
0837: if (this .isNull() || ((DataValueDescriptor) other).isNull())
0838: return unknownRV;
0839: }
0840:
0841: /* When comparing String types to non-string types, we always
0842: * convert the string type to the non-string type.
0843: */
0844: if (!(other instanceof SQLChar)) {
0845: return other.compare(flip(op), this , orderedNulls,
0846: unknownRV);
0847: }
0848:
0849: /* Do the comparison */
0850: return super .compare(op, other, orderedNulls, unknownRV);
0851: }
0852:
0853: /**
0854: @exception StandardException thrown on error
0855: */
0856: public int compare(DataValueDescriptor other)
0857: throws StandardException {
0858: /* Use compare method from dominant type, negating result
0859: * to reflect flipping of sides.
0860: */
0861: if (typePrecedence() < other.typePrecedence()) {
0862: return -(other.compare(this ));
0863: }
0864:
0865: // stringCompare deals with null as comparable and smallest
0866: return stringCompare(this , (SQLChar) other);
0867: }
0868:
0869: /*
0870: * CloneableObject interface
0871: */
0872:
0873: /** From CloneableObject
0874: * Shallow clone a StreamStorable without objectifying. This is used to avoid
0875: * unnecessary objectifying of a stream object. The only difference of this method
0876: * from getClone is this method does not objectify a stream. beetle 4896
0877: */
0878: public Object cloneObject() {
0879: if (stream == null)
0880: return getClone();
0881: SQLChar self = (SQLChar) getNewNull();
0882: self.copyState(this );
0883: return self;
0884: }
0885:
0886: /*
0887: * DataValueDescriptor interface
0888: */
0889:
0890: /** @see DataValueDescriptor#getClone */
0891: public DataValueDescriptor getClone() {
0892: try {
0893: return new SQLChar(getString());
0894: } catch (StandardException se) {
0895: if (SanityManager.DEBUG)
0896: SanityManager.THROWASSERT("Unexpected exception " + se);
0897: return null;
0898: }
0899: }
0900:
0901: /**
0902: * @see DataValueDescriptor#getNewNull
0903: *
0904: */
0905: public DataValueDescriptor getNewNull() {
0906: return new SQLChar();
0907: }
0908:
0909: /**
0910: * @see DataValueDescriptor#setValueFromResultSet
0911: *
0912: * @exception SQLException Thrown on error
0913: */
0914: public final void setValueFromResultSet(ResultSet resultSet,
0915: int colNumber, boolean isNullable) throws SQLException {
0916: setValue(resultSet.getString(colNumber));
0917: }
0918:
0919: /**
0920: Set the value into a PreparedStatement.
0921: */
0922: public final void setInto(PreparedStatement ps, int position)
0923: throws SQLException, StandardException {
0924:
0925: ps.setString(position, getString());
0926: }
0927:
0928: /*
0929: * class interface
0930: */
0931:
0932: /*
0933: * constructors
0934: */
0935:
0936: /**
0937: no-arg constructor, required by Formattable.
0938: */
0939: public SQLChar() {
0940: }
0941:
0942: public SQLChar(String val) {
0943: value = val;
0944: }
0945:
0946: public void setValue(String theValue) {
0947: stream = null;
0948: rawLength = -1;
0949: // clear out the int array as well, so it will stay current
0950: intArray = null;
0951: intLength = 0;
0952: cKey = null;
0953:
0954: value = theValue;
0955: }
0956:
0957: public void setValue(boolean theValue) throws StandardException {
0958: // match JCC.
0959: setValue(theValue ? "1" : "0");
0960: }
0961:
0962: public void setValue(int theValue) throws StandardException {
0963: setValue(Integer.toString(theValue));
0964: }
0965:
0966: public void setValue(double theValue) throws StandardException {
0967: setValue(Double.toString(theValue));
0968: }
0969:
0970: public void setValue(float theValue) throws StandardException {
0971: setValue(Float.toString(theValue));
0972: }
0973:
0974: public void setValue(short theValue) throws StandardException {
0975: setValue(Short.toString(theValue));
0976: }
0977:
0978: public void setValue(long theValue) throws StandardException {
0979: setValue(Long.toString(theValue));
0980: }
0981:
0982: public void setValue(byte theValue) throws StandardException {
0983: setValue(Byte.toString(theValue));
0984: }
0985:
0986: public void setValue(byte[] theValue) throws StandardException {
0987: if (theValue == null) {
0988: restoreToNull();
0989: return;
0990: }
0991:
0992: /*
0993: ** We can't just do a new String(theValue)
0994: ** because that method assumes we are converting
0995: ** ASCII and it will take on char per byte.
0996: ** So we need to convert the byte array to a
0997: ** char array and go from there.
0998: **
0999: ** If we have an odd number of bytes pad out.
1000: */
1001: int mod = (theValue.length % 2);
1002: int len = (theValue.length / 2) + mod;
1003: char[] carray = new char[len];
1004: int cindex = 0;
1005: int bindex = 0;
1006:
1007: /*
1008: ** If we have a left over byte, then get
1009: ** that now.
1010: */
1011: if (mod == 1) {
1012: carray[--len] = (char) (theValue[theValue.length - 1] << 8);
1013: }
1014:
1015: for (; cindex < len; bindex += 2, cindex++) {
1016: carray[cindex] = (char) ((theValue[bindex] << 8) | (theValue[bindex + 1] & 0x00ff));
1017: }
1018:
1019: setValue(new String(carray));
1020: }
1021:
1022: /**
1023: Only to be called when an application through JDBC is setting a
1024: SQLChar to a java.math.BigDecimal.
1025: */
1026: public void setBigDecimal(Number bigDecimal)
1027: throws StandardException {
1028: if (bigDecimal == null)
1029: setToNull();
1030: else
1031: setValue(bigDecimal.toString());
1032: }
1033:
1034: /** @exception StandardException Thrown on error */
1035: public void setValue(Date theValue, Calendar cal)
1036: throws StandardException {
1037: String strValue = null;
1038: if (theValue != null) {
1039: if (cal == null)
1040: strValue = theValue.toString();
1041: else {
1042: cal.setTime(theValue);
1043: StringBuffer sb = new StringBuffer();
1044: formatJDBCDate(cal, sb);
1045: strValue = sb.toString();
1046: }
1047: }
1048: setValue(strValue);
1049: }
1050:
1051: /** @exception StandardException Thrown on error */
1052: public void setValue(Time theValue, Calendar cal)
1053: throws StandardException {
1054: String strValue = null;
1055: if (theValue != null) {
1056: if (cal == null)
1057: strValue = theValue.toString();
1058: else {
1059: cal.setTime(theValue);
1060: StringBuffer sb = new StringBuffer();
1061: formatJDBCTime(cal, sb);
1062: strValue = sb.toString();
1063: }
1064: }
1065: setValue(strValue);
1066: }
1067:
1068: /** @exception StandardException Thrown on error */
1069: public void setValue(Timestamp theValue, Calendar cal)
1070: throws StandardException {
1071: String strValue = null;
1072: if (theValue != null) {
1073: if (cal == null)
1074: strValue = theValue.toString();
1075: else {
1076: cal.setTime(theValue);
1077: StringBuffer sb = new StringBuffer();
1078: formatJDBCDate(cal, sb);
1079: sb.append(' ');
1080: formatJDBCTime(cal, sb);
1081: int micros = (theValue.getNanos() + SQLTimestamp.FRACTION_TO_NANO / 2)
1082: / SQLTimestamp.FRACTION_TO_NANO;
1083: if (micros > 0) {
1084: sb.append('.');
1085: String microsStr = Integer.toString(micros);
1086: if (microsStr.length() > SQLTimestamp.MAX_FRACTION_DIGITS)
1087: sb.append(microsStr.substring(0,
1088: SQLTimestamp.MAX_FRACTION_DIGITS));
1089: else {
1090: for (int i = microsStr.length(); i < SQLTimestamp.MAX_FRACTION_DIGITS; i++)
1091: sb.append('0');
1092: sb.append(microsStr);
1093: }
1094: }
1095: strValue = sb.toString();
1096: }
1097: }
1098: setValue(strValue);
1099: }
1100:
1101: private void formatJDBCDate(Calendar cal, StringBuffer sb) {
1102: SQLDate.dateToString(cal.get(Calendar.YEAR), cal
1103: .get(Calendar.MONTH)
1104: - Calendar.JANUARY + 1, cal.get(Calendar.DAY_OF_MONTH),
1105: sb);
1106: }
1107:
1108: private void formatJDBCTime(Calendar cal, StringBuffer sb) {
1109: SQLTime.timeToString(cal.get(Calendar.HOUR), cal
1110: .get(Calendar.MINUTE), cal.get(Calendar.SECOND), sb);
1111: }
1112:
1113: /**
1114: * Set the value from the stream which is in the on-disk format.
1115: * @param theStream On disk format of the stream
1116: * @param valueLength length of the logical value in characters.
1117: */
1118: public final void setValue(InputStream theStream, int valueLength) {
1119: setStream(theStream);
1120: }
1121:
1122: /**
1123: * Allow any Java type to be cast to a character type using
1124: * Object.toString.
1125: * @see DataValueDescriptor#setObjectForCast
1126: *
1127: * @exception StandardException
1128: * thrown on failure
1129: */
1130: public void setObjectForCast(Object theValue,
1131: boolean instanceOfResultType, String resultTypeClassName)
1132: throws StandardException {
1133:
1134: if (theValue == null) {
1135: setToNull();
1136: return;
1137: }
1138:
1139: if ("java.lang.String".equals(resultTypeClassName))
1140: setValue(theValue.toString());
1141: else
1142: super .setObjectForCast(theValue, instanceOfResultType,
1143: resultTypeClassName);
1144: }
1145:
1146: protected void setFrom(DataValueDescriptor theValue)
1147: throws StandardException {
1148:
1149: setValue(theValue.getString());
1150: }
1151:
1152: /**
1153: * Normalization method - this method may be called when putting
1154: * a value into a SQLChar, for example, when inserting into a SQLChar
1155: * column. See NormalizeResultSet in execution.
1156: *
1157: * @param desiredType The type to normalize the source column to
1158: * @param source The value to normalize
1159: *
1160: *
1161: * @exception StandardException Thrown for null into
1162: * non-nullable column, and for
1163: * truncation error
1164: */
1165:
1166: public void normalize(DataTypeDescriptor desiredType,
1167: DataValueDescriptor source) throws StandardException {
1168:
1169: normalize(desiredType, source.getString());
1170:
1171: }
1172:
1173: protected void normalize(DataTypeDescriptor desiredType,
1174: String sourceValue) throws StandardException {
1175:
1176: int desiredWidth = desiredType.getMaximumWidth();
1177: int sourceWidth = sourceValue.length();
1178:
1179: /*
1180: ** If the input is already the right length, no normalization is
1181: ** necessary - just return the source.
1182: */
1183: if (sourceWidth == desiredWidth) {
1184: setValue(sourceValue);
1185: return;
1186: }
1187:
1188: /*
1189: ** If the input is shorter than the desired type, construct a new
1190: ** SQLChar padded with blanks to the right length.
1191: */
1192: if (sourceWidth < desiredWidth) {
1193: setToNull();
1194:
1195: char[] ca;
1196: if ((rawData == null) || (desiredWidth > rawData.length)) {
1197:
1198: ca = rawData = new char[desiredWidth];
1199: } else {
1200: ca = rawData;
1201: }
1202:
1203: sourceValue.getChars(0, sourceWidth, ca, 0);
1204: SQLChar.appendBlanks(ca, sourceWidth, desiredWidth
1205: - sourceWidth);
1206:
1207: rawLength = desiredWidth;
1208:
1209: return;
1210: }
1211:
1212: /*
1213: ** Check whether any non-blank characters will be truncated.
1214: */
1215: hasNonBlankChars(sourceValue, desiredWidth, sourceWidth);
1216:
1217: /*
1218: ** No non-blank characters will be truncated. Truncate the blanks
1219: ** to the desired width.
1220: */
1221:
1222: String truncatedString = sourceValue.substring(0, desiredWidth);
1223: setValue(truncatedString);
1224: }
1225:
1226: /*
1227: ** Method to check for truncation of non blank chars.
1228: */
1229: protected final void hasNonBlankChars(String source, int start,
1230: int end) throws StandardException {
1231: /*
1232: ** Check whether any non-blank characters will be truncated.
1233: */
1234: for (int posn = start; posn < end; posn++) {
1235: if (source.charAt(posn) != ' ') {
1236: throw StandardException.newException(
1237: SQLState.LANG_STRING_TRUNCATION, getTypeName(),
1238: StringUtil.formatForPrint(source), String
1239: .valueOf(start));
1240: }
1241: }
1242: }
1243:
1244: ///////////////////////////////////////////////////////////////
1245: //
1246: // VariableSizeDataValue INTERFACE
1247: //
1248: ///////////////////////////////////////////////////////////////
1249:
1250: /**
1251: * Set the width of the to the desired value. Used
1252: * when CASTing. Ideally we'd recycle normalize(), but
1253: * the behavior is different (we issue a warning instead
1254: * of an error, and we aren't interested in nullability).
1255: *
1256: * @param desiredWidth the desired length
1257: * @param desiredScale the desired scale (ignored)
1258: * @param errorOnTrunc throw an error on truncation
1259: *
1260: * @exception StandardException Thrown when errorOnTrunc
1261: * is true and when a shrink will truncate non-white
1262: * spaces.
1263: */
1264: public void setWidth(int desiredWidth, int desiredScale, // Ignored
1265: boolean errorOnTrunc) throws StandardException {
1266: int sourceWidth;
1267:
1268: /*
1269: ** If the input is NULL, nothing to do.
1270: */
1271: if (getString() == null) {
1272: return;
1273: }
1274:
1275: sourceWidth = getLength();
1276:
1277: /*
1278: ** If the input is shorter than the desired type, construct a new
1279: ** SQLChar padded with blanks to the right length. Only
1280: ** do this if we have a SQLChar -- SQLVarchars don't
1281: ** pad.
1282: */
1283: if (sourceWidth < desiredWidth) {
1284: if (!(this instanceof SQLVarchar)) {
1285: StringBuffer strbuf;
1286:
1287: strbuf = new StringBuffer(getString());
1288:
1289: for (; sourceWidth < desiredWidth; sourceWidth++) {
1290: strbuf.append(' ');
1291: }
1292:
1293: setValue(new String(strbuf));
1294: }
1295: } else if (sourceWidth > desiredWidth && desiredWidth > 0) {
1296: /*
1297: ** Check whether any non-blank characters will be truncated.
1298: */
1299: if (errorOnTrunc)
1300: hasNonBlankChars(getString(), desiredWidth, sourceWidth);
1301: //RESOLVE: should issue a warning instead
1302:
1303: /*
1304: ** Truncate to the desired width.
1305: */
1306: setValue(getString().substring(0, desiredWidth));
1307: }
1308: return;
1309: }
1310:
1311: /*
1312: ** SQL Operators
1313: */
1314:
1315: /**
1316: * The = operator as called from the language module, as opposed to
1317: * the storage module.
1318: *
1319: * @param left The value on the left side of the =
1320: * @param right The value on the right side of the =
1321: *
1322: * @return A SQL boolean value telling whether the two parameters are equal
1323: *
1324: * @exception StandardException Thrown on error
1325: */
1326:
1327: public BooleanDataValue equals(DataValueDescriptor left,
1328: DataValueDescriptor right) throws StandardException {
1329: boolean comparison;
1330:
1331: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1332: comparison = stringCompare((SQLChar) left, (SQLChar) right) == 0;
1333: } else {
1334: comparison = stringCompare(left.getString(), right
1335: .getString()) == 0;
1336: }
1337:
1338: return SQLBoolean.truthValue(left, right, comparison);
1339: }
1340:
1341: /**
1342: * The <> operator as called from the language module, as opposed to
1343: * the storage module.
1344: *
1345: * @param left The value on the left side of the <>
1346: * @param right The value on the right side of the <>
1347: *
1348: * @return A SQL boolean value telling whether the two parameters
1349: * are not equal
1350: *
1351: * @exception StandardException Thrown on error
1352: */
1353:
1354: public BooleanDataValue notEquals(DataValueDescriptor left,
1355: DataValueDescriptor right) throws StandardException {
1356: boolean comparison;
1357:
1358: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1359: comparison = stringCompare((SQLChar) left, (SQLChar) right) != 0;
1360: } else {
1361: comparison = stringCompare(left.getString(), right
1362: .getString()) != 0;
1363: }
1364:
1365: return SQLBoolean.truthValue(left, right, comparison);
1366: }
1367:
1368: /**
1369: * The < operator as called from the language module, as opposed to
1370: * the storage module.
1371: *
1372: * @param left The value on the left side of the <
1373: * @param right The value on the right side of the <
1374: *
1375: * @return A SQL boolean value telling whether the first operand is
1376: * less than the second operand
1377: *
1378: * @exception StandardException Thrown on error
1379: */
1380:
1381: public BooleanDataValue lessThan(DataValueDescriptor left,
1382: DataValueDescriptor right) throws StandardException {
1383: boolean comparison;
1384:
1385: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1386: comparison = stringCompare((SQLChar) left, (SQLChar) right) < 0;
1387: } else {
1388: comparison = stringCompare(left.getString(), right
1389: .getString()) < 0;
1390: }
1391:
1392: return SQLBoolean.truthValue(left, right, comparison);
1393: }
1394:
1395: /**
1396: * The > operator as called from the language module, as opposed to
1397: * the storage module.
1398: *
1399: * @param left The value on the left side of the >
1400: * @param right The value on the right side of the >
1401: *
1402: * @return A SQL boolean value telling whether the first operand is
1403: * greater than the second operand
1404: *
1405: * @exception StandardException Thrown on error
1406: */
1407:
1408: public BooleanDataValue greaterThan(DataValueDescriptor left,
1409: DataValueDescriptor right) throws StandardException {
1410: boolean comparison;
1411:
1412: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1413: comparison = stringCompare((SQLChar) left, (SQLChar) right) > 0;
1414: } else {
1415: comparison = stringCompare(left.getString(), right
1416: .getString()) > 0;
1417: }
1418:
1419: return SQLBoolean.truthValue(left, right, comparison);
1420: }
1421:
1422: /**
1423: * The <= operator as called from the language module, as opposed to
1424: * the storage module.
1425: *
1426: * @param left The value on the left side of the <=
1427: * @param right The value on the right side of the <=
1428: *
1429: * @return A SQL boolean value telling whether the first operand is
1430: * less than or equal to the second operand
1431: *
1432: * @exception StandardException Thrown on error
1433: */
1434:
1435: public BooleanDataValue lessOrEquals(DataValueDescriptor left,
1436: DataValueDescriptor right) throws StandardException {
1437: boolean comparison;
1438:
1439: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1440: comparison = stringCompare((SQLChar) left, (SQLChar) right) <= 0;
1441: } else {
1442: comparison = stringCompare(left.getString(), right
1443: .getString()) <= 0;
1444: }
1445:
1446: return SQLBoolean.truthValue(left, right, comparison);
1447: }
1448:
1449: /**
1450: * The >= operator as called from the language module, as opposed to
1451: * the storage module.
1452: *
1453: * @param left The value on the left side of the >=
1454: * @param right The value on the right side of the >=
1455: *
1456: * @return A SQL boolean value telling whether the first operand is
1457: * greater than or equal to the second operand
1458: *
1459: * @exception StandardException Thrown on error
1460: */
1461:
1462: public BooleanDataValue greaterOrEquals(DataValueDescriptor left,
1463: DataValueDescriptor right) throws StandardException {
1464: boolean comparison;
1465:
1466: if ((left instanceof SQLChar) && (right instanceof SQLChar)) {
1467: comparison = stringCompare((SQLChar) left, (SQLChar) right) >= 0;
1468: } else {
1469: comparison = stringCompare(left.getString(), right
1470: .getString()) >= 0;
1471: }
1472:
1473: return SQLBoolean.truthValue(left, right, comparison);
1474: }
1475:
1476: /*
1477: ** Concatable interface
1478: */
1479: /**
1480: * This method implements the char_length function for char.
1481: *
1482: * @param result The result of a previous call to this method, null
1483: * if not called yet
1484: *
1485: * @return A SQLInteger containing the length of the char value
1486: *
1487: * @exception StandardException Thrown on error
1488: *
1489: * @see ConcatableDataValue#charLength(NumberDataValue)
1490: */
1491: public NumberDataValue charLength(NumberDataValue result)
1492: throws StandardException {
1493: if (result == null) {
1494: result = new SQLInteger();
1495: }
1496:
1497: if (this .isNull()) {
1498: result.setToNull();
1499: return result;
1500: }
1501:
1502: result.setValue(this .getLength());
1503: return result;
1504: }
1505:
1506: /**
1507: * @see StringDataValue#concatenate
1508: *
1509: * @exception StandardException Thrown on error
1510: */
1511: public StringDataValue concatenate(StringDataValue leftOperand,
1512: StringDataValue rightOperand, StringDataValue result)
1513: throws StandardException {
1514: if (leftOperand.isNull() || leftOperand.getString() == null
1515: || rightOperand.isNull()
1516: || rightOperand.getString() == null) {
1517: result.setToNull();
1518: return result;
1519: }
1520:
1521: result.setValue(leftOperand.getString().concat(
1522: rightOperand.getString()));
1523: return result;
1524: }
1525:
1526: /**
1527: * This method implements the like function for char (with no escape value).
1528: *
1529: * @param pattern The pattern to use
1530: *
1531: * @return A SQL boolean value telling whether the first operand is
1532: * like the second operand
1533: *
1534: * @exception StandardException Thrown on error
1535: */
1536: public BooleanDataValue like(DataValueDescriptor pattern)
1537: throws StandardException {
1538: Boolean likeResult;
1539:
1540: if (!isNationalString()) {
1541: // note that we call getLength() because the length
1542: // of the char array may be different than the
1543: // length we should be using (i.e. getLength()).
1544: // see getCharArray() for more info
1545: char[] evalCharArray = getCharArray();
1546: char[] patternCharArray = ((SQLChar) pattern)
1547: .getCharArray();
1548: likeResult = Like.like(evalCharArray, getLength(),
1549: patternCharArray, pattern.getLength());
1550: } else {
1551: SQLChar patternSQLChar = (SQLChar) pattern;
1552: likeResult = Like.like(getIntArray(), getIntLength(),
1553: patternSQLChar.getIntArray(), patternSQLChar
1554: .getIntLength(), getLocaleFinder()
1555: .getCollator());
1556: }
1557:
1558: return SQLBoolean.truthValue(this , pattern, likeResult);
1559: }
1560:
1561: /**
1562: * This method implements the like function for char with an escape value.
1563: *
1564: * @param pattern The pattern to use
1565: *
1566: * @return A SQL boolean value telling whether the first operand is
1567: * like the second operand
1568: *
1569: * @exception StandardException Thrown on error
1570: */
1571:
1572: public BooleanDataValue like(DataValueDescriptor pattern,
1573: DataValueDescriptor escape) throws StandardException {
1574: Boolean likeResult;
1575:
1576: if (SanityManager.DEBUG)
1577: SanityManager
1578: .ASSERT(pattern instanceof StringDataValue
1579: && escape instanceof StringDataValue,
1580: "All three operands must be instances of StringDataValue");
1581:
1582: // ANSI states a null escape yields 'unknown' results
1583: //
1584: // This method is only called when we have an escape clause, so this
1585: // test is valid
1586:
1587: if (escape.isNull()) {
1588: throw StandardException
1589: .newException(SQLState.LANG_ESCAPE_IS_NULL);
1590: }
1591:
1592: if (!isNationalString()) {
1593: // note that we call getLength() because the length
1594: // of the char array may be different than the
1595: // length we should be using (i.e. getLength()).
1596: // see getCharArray() for more info
1597: char[] evalCharArray = getCharArray();
1598: char[] patternCharArray = ((SQLChar) pattern)
1599: .getCharArray();
1600: char[] escapeCharArray = (((SQLChar) escape).getCharArray());
1601: int escapeLength = escape.getLength();
1602:
1603: if (escapeCharArray != null && escapeLength != 1) {
1604: throw StandardException.newException(
1605: SQLState.LANG_INVALID_ESCAPE_CHARACTER,
1606: new String(escapeCharArray));
1607: } else {
1608: // Make sure we fail for both varchar an nvarchar
1609: // for multiple collation characters.
1610: SQLChar escapeSQLChar = (SQLChar) escape;
1611: int[] escapeIntArray = escapeSQLChar.getIntArray();
1612: if (escapeIntArray != null
1613: && (escapeIntArray.length != 1)) {
1614: throw StandardException.newException(
1615: SQLState.LANG_INVALID_ESCAPE_CHARACTER,
1616: new String(escapeSQLChar.getCharArray()));
1617: }
1618: }
1619: likeResult = Like.like(evalCharArray, getLength(),
1620: patternCharArray, pattern.getLength(),
1621: escapeCharArray, escapeLength);
1622: } else {
1623: SQLChar patternSQLChar = (SQLChar) pattern;
1624: SQLChar escapeSQLChar = (SQLChar) escape;
1625: int[] escapeIntArray = escapeSQLChar.getIntArray();
1626: int escapeLength = escapeSQLChar.getIntLength();
1627:
1628: if (escapeIntArray != null && (escapeIntArray.length != 1)) {
1629: throw StandardException.newException(
1630: SQLState.LANG_INVALID_ESCAPE_CHARACTER,
1631: new String(escapeSQLChar.getCharArray()));
1632: }
1633: likeResult = Like.like(getIntArray(), getIntLength(),
1634: patternSQLChar.getIntArray(), patternSQLChar
1635: .getIntLength(), escapeIntArray,
1636: escapeLength, getLocaleFinder().getCollator());
1637: }
1638:
1639: return SQLBoolean.truthValue(this , pattern, likeResult);
1640: }
1641:
1642: /**
1643: * This method implements the locate function for char.
1644: * @param searchFrom - The string to search from
1645: * @param start - The position to search from in string searchFrom
1646: * @param result - The object to return
1647: *
1648: * Note: use getString() to get the string to search for.
1649: *
1650: * @return The position in searchFrom the fist occurrence of this.value.
1651: * 0 is returned if searchFrom does not contain this.value.
1652: * @exception StandardException Thrown on error
1653: */
1654: public NumberDataValue locate(StringDataValue searchFrom,
1655: NumberDataValue start, NumberDataValue result)
1656: throws StandardException {
1657: int startVal;
1658:
1659: if (result == null) {
1660: result = new SQLInteger();
1661: }
1662:
1663: if (start.isNull()) {
1664: startVal = 1;
1665: } else {
1666: startVal = start.getInt();
1667: }
1668:
1669: if (isNull() || searchFrom.isNull()) {
1670: result.setToNull();
1671: return result;
1672: }
1673:
1674: String mySearchFrom = searchFrom.getString();
1675: String mySearchFor = this .getString();
1676:
1677: /* the below 2 if conditions are to emulate DB2's behavior */
1678: if (startVal < 1) {
1679: throw StandardException
1680: .newException(
1681: SQLState.LANG_INVALID_PARAMETER_FOR_SEARCH_POSITION,
1682: new String(getString()), new String(
1683: mySearchFrom),
1684: new Integer(startVal));
1685: }
1686:
1687: if (mySearchFor.length() == 0) {
1688: result.setValue(startVal);
1689: return result;
1690: }
1691:
1692: result
1693: .setValue(mySearchFrom.indexOf(mySearchFor,
1694: startVal - 1) + 1);
1695: return result;
1696: }
1697:
1698: /**
1699: * The SQL substr() function.
1700: *
1701: * @param start Start of substr
1702: * @param length Length of substr
1703: * @param result The result of a previous call to this method,
1704: * null if not called yet.
1705: * @param maxLen Maximum length of the result
1706: *
1707: * @return A ConcatableDataValue containing the result of the substr()
1708: *
1709: * @exception StandardException Thrown on error
1710: */
1711: public ConcatableDataValue substring(NumberDataValue start,
1712: NumberDataValue length, ConcatableDataValue result,
1713: int maxLen) throws StandardException {
1714: int startInt;
1715: int lengthInt;
1716: StringDataValue stringResult;
1717:
1718: if (result == null) {
1719: result = getNewVarchar();
1720: }
1721:
1722: stringResult = (StringDataValue) result;
1723:
1724: /* The result is null if the receiver (this) is null or if the length is negative.
1725: * We will return null, which is the only sensible thing to do.
1726: * (If the user did not specify a length then length is not a user null.)
1727: */
1728: if (this .isNull() || start.isNull()
1729: || (length != null && length.isNull())) {
1730: stringResult.setToNull();
1731: return stringResult;
1732: }
1733:
1734: startInt = start.getInt();
1735:
1736: // If length is not specified, make it till end of the string
1737: if (length != null) {
1738: lengthInt = length.getInt();
1739: } else
1740: lengthInt = maxLen - startInt + 1;
1741:
1742: /* DB2 Compatibility: Added these checks to match DB2. We currently enforce these
1743: * limits in both modes. We could do these checks in DB2 mode only, if needed, so
1744: * leaving earlier code for out of range in for now, though will not be exercised
1745: */
1746: if ((startInt <= 0 || lengthInt < 0 || startInt > maxLen || lengthInt > maxLen
1747: - startInt + 1))
1748: throw StandardException
1749: .newException(SQLState.LANG_SUBSTR_START_OR_LEN_OUT_OF_RANGE);
1750:
1751: // Return null if length is non-positive
1752: if (lengthInt < 0) {
1753: stringResult.setToNull();
1754: return stringResult;
1755: }
1756:
1757: /* If startInt < 0 then we count from the right of the string */
1758: if (startInt < 0) {
1759: // Return '' if window is to left of string.
1760: if (startInt + getLength() < 0
1761: && (startInt + getLength() + lengthInt <= 0)) {
1762: stringResult.setValue("");
1763: return stringResult;
1764: }
1765:
1766: // Convert startInt to positive to get substring from right
1767: startInt += getLength();
1768:
1769: while (startInt < 0) {
1770: startInt++;
1771: lengthInt--;
1772: }
1773: } else if (startInt > 0) {
1774: /* java substring() is 0 based */
1775: startInt--;
1776: }
1777:
1778: /* Oracle docs don't say what happens if the window is to the
1779: * left of the string. Return "" if the window
1780: * is to the left or right.
1781: */
1782: if (lengthInt == 0 || lengthInt <= 0 - startInt
1783: || startInt > getLength()) {
1784: stringResult.setValue("");
1785: return stringResult;
1786: }
1787:
1788: if (lengthInt >= getLength() - startInt) {
1789: stringResult.setValue(getString().substring(startInt));
1790: } else {
1791: stringResult.setValue(getString().substring(startInt,
1792: startInt + lengthInt));
1793: }
1794:
1795: return stringResult;
1796: }
1797:
1798: /**
1799: * The SQL trim(), ltrim() and rtrim() functions.
1800: *
1801: * @param trimType Type of trim
1802: * @param result The result of a previous call to this method,
1803: * null if not called yet.
1804: *
1805: * @return A StringDataValue containing the result of the trim()
1806: *
1807: * @exception StandardException Thrown on error
1808: */
1809: public StringDataValue trim(int trimType, StringDataValue result)
1810: throws StandardException {
1811:
1812: if (result == null) {
1813: result = getNewVarchar();
1814: }
1815:
1816: /* The result is null if any of the parameters is a user null */
1817: if (this .isNull()) {
1818: result.setToNull();
1819: return result;
1820: }
1821:
1822: char[] trimChars = { ' ' };
1823: String tmpValue = getString();
1824:
1825: // Trim leading characters if appropriate
1826: if (trimType == LEADING) {
1827: int start = 0;
1828: // Find the 1st character which doesn't get trimmed
1829: for (; start < tmpValue.length(); start++) {
1830: boolean found = false;
1831: for (int index = 0; index < trimChars.length; index++) {
1832: if (tmpValue.charAt(start) == trimChars[index]) {
1833: found = true;
1834: break;
1835: }
1836: }
1837:
1838: if (!found) {
1839: break;
1840: }
1841: }
1842:
1843: // Trim if appropriate
1844: if (start == tmpValue.length()) {
1845: tmpValue = "";
1846: } else if (start > 0) {
1847: tmpValue = tmpValue.substring(start);
1848: }
1849: }
1850:
1851: // Trim trailing characters if appropriate
1852: if (trimType == TRAILING) {
1853: int start = tmpValue.length();
1854: // Find the 1st character which doesn't get trimmed
1855: for (; start > 0; start--) {
1856: boolean found = false;
1857: for (int index = 0; index < trimChars.length; index++) {
1858: if (tmpValue.charAt(start - 1) == trimChars[index]) {
1859: found = true;
1860: break;
1861: }
1862: }
1863:
1864: if (!found) {
1865: break;
1866: }
1867: }
1868:
1869: // Trim if appropriate
1870: if (start == 0) {
1871: tmpValue = "";
1872: } else if (start < tmpValue.length()) {
1873: tmpValue = tmpValue.substring(0, start);
1874: }
1875: }
1876:
1877: result.setValue(tmpValue);
1878: return result;
1879: }
1880:
1881: /** @see StringDataValue#upper
1882: *
1883: * @exception StandardException Thrown on error
1884: */
1885: public StringDataValue upper(StringDataValue result)
1886: throws StandardException {
1887: if (result == null) {
1888: result = (StringDataValue) getNewNull();
1889: }
1890:
1891: if (this .isNull()) {
1892: result.setToNull();
1893: return result;
1894: }
1895:
1896: String upper = getString();
1897: upper = upper.toUpperCase(getLocale());
1898: result.setValue(upper);
1899: return result;
1900: }
1901:
1902: /** @see StringDataValue#lower
1903: *
1904: * @exception StandardException Thrown on error
1905: */
1906: public StringDataValue lower(StringDataValue result)
1907: throws StandardException {
1908: if (result == null) {
1909: result = (StringDataValue) getNewNull();
1910: }
1911:
1912: if (this .isNull()) {
1913: result.setToNull();
1914: return result;
1915: }
1916:
1917: String lower = getString();
1918: lower = lower.toLowerCase(getLocale());
1919: result.setValue(lower);
1920: return result;
1921: }
1922:
1923: /*
1924: * DataValueDescriptor interface
1925: */
1926:
1927: /** @see DataValueDescriptor#typePrecedence */
1928: public int typePrecedence() {
1929: return TypeId.CHAR_PRECEDENCE;
1930: }
1931:
1932: /**
1933: * Compare two Strings using standard SQL semantics.
1934: *
1935: * @param op1 The first String
1936: * @param op2 The second String
1937: *
1938: * @return -1 - op1 < op2
1939: * 0 - op1 == op2
1940: * 1 - op1 > op2
1941: */
1942: protected static int stringCompare(String op1, String op2) {
1943: int posn;
1944: char leftchar;
1945: char rightchar;
1946: int leftlen;
1947: int rightlen;
1948: int retvalIfLTSpace;
1949: String remainingString;
1950: int remainingLen;
1951:
1952: /*
1953: ** By convention, nulls sort High, and null == null
1954: */
1955: if (op1 == null || op2 == null) {
1956: if (op1 != null) // op2 == null
1957: return -1;
1958: if (op2 != null) // op1 == null
1959: return 1;
1960: return 0; // both null
1961: }
1962: /*
1963: ** Compare characters until we find one that isn't equal, or until
1964: ** one String or the other runs out of characters.
1965: */
1966:
1967: leftlen = op1.length();
1968: rightlen = op2.length();
1969:
1970: int shorterLen = leftlen < rightlen ? leftlen : rightlen;
1971:
1972: for (posn = 0; posn < shorterLen; posn++) {
1973: leftchar = op1.charAt(posn);
1974: rightchar = op2.charAt(posn);
1975: if (leftchar != rightchar) {
1976: if (leftchar < rightchar)
1977: return -1;
1978: else
1979: return 1;
1980: }
1981: }
1982:
1983: /*
1984: ** All the characters are equal up to the length of the shorter
1985: ** string. If the two strings are of equal length, the values are
1986: ** equal.
1987: */
1988: if (leftlen == rightlen)
1989: return 0;
1990:
1991: /*
1992: ** One string is shorter than the other. Compare the remaining
1993: ** characters in the longer string to spaces (the SQL standard says
1994: ** that in this case the comparison is as if the shorter string is
1995: ** padded with blanks to the length of the longer string.
1996: */
1997: if (leftlen > rightlen) {
1998: /*
1999: ** Remaining characters are on the left.
2000: */
2001:
2002: /* If a remaining character is less than a space, return -1 (op1 < op2) */
2003: retvalIfLTSpace = -1;
2004: remainingString = op1;
2005: posn = rightlen;
2006: remainingLen = leftlen;
2007: } else {
2008: /*
2009: ** Remaining characters are on the right.
2010: */
2011:
2012: /* If a remaining character is less than a space, return 1 (op1 > op2) */
2013: retvalIfLTSpace = 1;
2014: remainingString = op2;
2015: posn = leftlen;
2016: remainingLen = rightlen;
2017: }
2018:
2019: /* Look at the remaining characters in the longer string */
2020: for (; posn < remainingLen; posn++) {
2021: char remainingChar;
2022:
2023: /*
2024: ** Compare the characters to spaces, and return the appropriate
2025: ** value, depending on which is the longer string.
2026: */
2027:
2028: remainingChar = remainingString.charAt(posn);
2029:
2030: if (remainingChar < ' ')
2031: return retvalIfLTSpace;
2032: else if (remainingChar > ' ')
2033: return -retvalIfLTSpace;
2034: }
2035:
2036: /* The remaining characters in the longer string were all spaces,
2037: ** so the strings are equal.
2038: */
2039: return 0;
2040: }
2041:
2042: /**
2043: * Compare two SQLChars. This method will be overriden in the
2044: * National char wrappers so that the appropriate comparison
2045: * is done.
2046: *
2047: * @exception StandardException Thrown on error
2048: */
2049: protected int stringCompare(SQLChar char1, SQLChar char2)
2050: throws StandardException {
2051: return stringCompare(char1.getCharArray(), char1.getLength(),
2052: char2.getCharArray(), char2.getLength());
2053: }
2054:
2055: /**
2056: * Compare two Strings using standard SQL semantics.
2057: *
2058: * @param op1 The first String
2059: * @param op2 The second String
2060: *
2061: * @return -1 - op1 < op2
2062: * 0 - op1 == op2
2063: * 1 - op1 > op2
2064: */
2065: protected static int stringCompare(char[] op1, int leftlen,
2066: char[] op2, int rightlen) {
2067: int posn;
2068: char leftchar;
2069: char rightchar;
2070: int retvalIfLTSpace;
2071: char[] remainingString;
2072: int remainingLen;
2073:
2074: /*
2075: ** By convention, nulls sort High, and null == null
2076: */
2077: if (op1 == null || op2 == null) {
2078: if (op1 != null) // op2 == null
2079: return -1;
2080: if (op2 != null) // op1 == null
2081: return 1;
2082: return 0; // both null
2083: }
2084: /*
2085: ** Compare characters until we find one that isn't equal, or until
2086: ** one String or the other runs out of characters.
2087: */
2088: int shorterLen = leftlen < rightlen ? leftlen : rightlen;
2089: for (posn = 0; posn < shorterLen; posn++) {
2090: leftchar = op1[posn];
2091: rightchar = op2[posn];
2092: if (leftchar != rightchar) {
2093: if (leftchar < rightchar)
2094: return -1;
2095: else
2096: return 1;
2097: }
2098: }
2099:
2100: /*
2101: ** All the characters are equal up to the length of the shorter
2102: ** string. If the two strings are of equal length, the values are
2103: ** equal.
2104: */
2105: if (leftlen == rightlen)
2106: return 0;
2107:
2108: /*
2109: ** One string is shorter than the other. Compare the remaining
2110: ** characters in the longer string to spaces (the SQL standard says
2111: ** that in this case the comparison is as if the shorter string is
2112: ** padded with blanks to the length of the longer string.
2113: */
2114: if (leftlen > rightlen) {
2115: /*
2116: ** Remaining characters are on the left.
2117: */
2118:
2119: /* If a remaining character is less than a space, return -1 (op1 < op2) */
2120: retvalIfLTSpace = -1;
2121: remainingString = op1;
2122: posn = rightlen;
2123: remainingLen = leftlen;
2124: } else {
2125: /*
2126: ** Remaining characters are on the right.
2127: */
2128:
2129: /* If a remaining character is less than a space, return 1 (op1 > op2) */
2130: retvalIfLTSpace = 1;
2131: remainingString = op2;
2132: posn = leftlen;
2133: remainingLen = rightlen;
2134: }
2135:
2136: /* Look at the remaining characters in the longer string */
2137: for (; posn < remainingLen; posn++) {
2138: char remainingChar;
2139:
2140: /*
2141: ** Compare the characters to spaces, and return the appropriate
2142: ** value, depending on which is the longer string.
2143: */
2144:
2145: remainingChar = remainingString[posn];
2146:
2147: if (remainingChar < ' ')
2148: return retvalIfLTSpace;
2149: else if (remainingChar > ' ')
2150: return -retvalIfLTSpace;
2151: }
2152:
2153: /* The remaining characters in the longer string were all spaces,
2154: ** so the strings are equal.
2155: */
2156: return 0;
2157: }
2158:
2159: /**
2160: * Compare a localized string with this one.
2161: *
2162: * @param str2 The other string
2163: *
2164: * @return -1 - this < str2
2165: * 0 - this == str2
2166: * 1 - this > str2
2167: */
2168: protected int stringCollatorCompare(SQLChar str2)
2169: throws StandardException {
2170: CollationKey ckey1 = this .getCollationKey();
2171: CollationKey ckey2 = str2.getCollationKey();
2172:
2173: /*
2174: ** By convention, nulls sort High, and null == null
2175: */
2176: if (ckey1 == null || ckey2 == null) {
2177: if (ckey1 != null) // str2 == null
2178: return -1;
2179: if (ckey2 != null) // this == null
2180: return 1;
2181: return 0; // both == null
2182: }
2183:
2184: return ckey1.compareTo(ckey2);
2185: }
2186:
2187: protected CollationKey getCollationKey() throws StandardException {
2188: char tmpCharArray[];
2189:
2190: if (cKey != null)
2191: return cKey;
2192:
2193: if (rawLength == -1) {
2194: /* materialize the string if input is a stream */
2195: tmpCharArray = getCharArray();
2196: if (tmpCharArray == null)
2197: return null;
2198: }
2199:
2200: int lastNonspaceChar = rawLength;
2201:
2202: while (lastNonspaceChar > 0
2203: && rawData[lastNonspaceChar - 1] == '\u0020')
2204: lastNonspaceChar--; // count off the trailing spaces.
2205:
2206: RuleBasedCollator rbc = getLocaleFinder().getCollator();
2207: cKey = rbc.getCollationKey(new String(rawData, 0,
2208: lastNonspaceChar));
2209:
2210: return cKey;
2211: }
2212:
2213: /*
2214: * String display of value
2215: */
2216:
2217: public String toString() {
2218: if (isNull()) {
2219: return "NULL";
2220: }
2221:
2222: if ((value == null) && (rawLength != -1)) {
2223:
2224: return new String(rawData, 0, rawLength);
2225: }
2226:
2227: if (stream != null) {
2228: try {
2229: return getString();
2230: } catch (Exception e) {
2231: return e.toString();
2232: }
2233: }
2234:
2235: return value;
2236: }
2237:
2238: /*
2239: * Hash code
2240: */
2241: public int hashCode() {
2242: try {
2243: if (getString() == null) {
2244: return 0;
2245: }
2246: } catch (StandardException se) {
2247: if (SanityManager.DEBUG)
2248: SanityManager.THROWASSERT("Unexpected exception " + se);
2249: return 0;
2250: }
2251:
2252: /* value.hashCode() doesn't work because of the SQL blank padding behavior
2253: * We want the hash code to be based on the value after the
2254: * trailing blanks have been trimmed. Calling trim() is too expensive
2255: * since it will create a new object, so here's what we do:
2256: * o Walk from the right until we've found the 1st
2257: * non-blank character.
2258: * o Add up the characters from that character to the 1st in
2259: * the string and return that as the hash code.
2260: */
2261: int index;
2262: int hashcode = 0;
2263:
2264: // value will have been set by the getString() above
2265: String lvalue = value;
2266:
2267: // Find 1st non-blank from the right
2268: for (index = lvalue.length() - 1; index >= 0
2269: && lvalue.charAt(index) == ' '; index--) {
2270: ;
2271: }
2272:
2273: // Build the hash code
2274: for (; index >= 0; index--) {
2275: hashcode += lvalue.charAt(index);
2276: }
2277:
2278: return hashcode;
2279: }
2280:
2281: /**
2282: * Implementation of hashCode() for the national character types,
2283: * put here to make it accessible to all the national types.
2284: */
2285: protected int nationalHashCode() {
2286: CollationKey tmpCKey = null;
2287:
2288: try {
2289: tmpCKey = getCollationKey();
2290: } catch (StandardException se) {
2291: if (SanityManager.DEBUG) {
2292: SanityManager.THROWASSERT("Unexpected exception " + se);
2293: }
2294: }
2295:
2296: if (tmpCKey == null)
2297: return 0;
2298: return tmpCKey.hashCode();
2299: }
2300:
2301: private int[] getIntArray() throws StandardException {
2302: if (isNull()) {
2303: return (int[]) null;
2304: }
2305:
2306: if (intArray != null) {
2307: return intArray;
2308: }
2309:
2310: // intLength should always be 0 when intArray is null
2311: if (SanityManager.DEBUG) {
2312: if (intLength != 0) {
2313: SanityManager
2314: .THROWASSERT("intLength expected to be 0, not "
2315: + intLength);
2316: }
2317: }
2318:
2319: intArray = new int[getLength()];
2320:
2321: RuleBasedCollator rbc = getLocaleFinder().getCollator();
2322: CollationElementIterator cei = rbc
2323: .getCollationElementIterator(getString());
2324: int nextInt;
2325: while ((nextInt = cei.next()) != CollationElementIterator.NULLORDER) {
2326: /* Believe it or not, a String might have more
2327: * collation elements than characters.
2328: * So, we handle that case by increasing the int array
2329: * by 5 and copying array elements.
2330: */
2331: if (intLength == intArray.length) {
2332: int[] tempArray = intArray;
2333: intArray = new int[intLength + 5];
2334: for (int index = 0; index < tempArray.length; index++) {
2335: intArray[index] = tempArray[index];
2336: }
2337: }
2338: intArray[intLength++] = nextInt;
2339: }
2340:
2341: return intArray;
2342: }
2343:
2344: private int getIntLength() {
2345: return intLength;
2346: }
2347:
2348: /**
2349: * Get a SQLVarchar for a built-in string function.
2350: * (Could be either a SQLVarchar or SQLNationalVarchar.)
2351: *
2352: * @return a SQLVarchar or SQLNationalVarchar.
2353: *
2354: * @exception StandardException Thrown on error
2355: */
2356: protected StringDataValue getNewVarchar() throws StandardException {
2357: return new SQLVarchar();
2358: }
2359:
2360: /**
2361: * Return whether or not this is a national character datatype.
2362: */
2363: protected boolean isNationalString() {
2364: return false;
2365: }
2366:
2367: /**
2368: * This implements getDate() for the national types. It lives
2369: * here so it can be shared between all the national types.
2370: *
2371: * @exception StandardException thrown on failure to convert
2372: */
2373: protected Date nationalGetDate(Calendar cal)
2374: throws StandardException {
2375: if (isNull())
2376: return null;
2377: SQLDate internalDate = new SQLDate(getString(), false,
2378: getLocaleFinder(), cal);
2379: return internalDate.getDate(cal);
2380: }
2381:
2382: /**
2383: * This implements getTime() for the national types. It lives
2384: * here so it can be shared between all the national types.
2385: *
2386: * @exception StandardException thrown on failure to convert
2387: */
2388: protected Time nationalGetTime(Calendar cal)
2389: throws StandardException {
2390: if (isNull())
2391: return null;
2392: SQLTime internalTime = new SQLTime(getString(), false,
2393: getLocaleFinder(), cal);
2394: return internalTime.getTime(cal);
2395: }
2396:
2397: /**
2398: * This implements getTimestamp() for the national types. It lives
2399: * here so it can be shared between all the national types.
2400: *
2401: * @exception StandardException thrown on failure to convert
2402: */
2403: protected Timestamp nationalGetTimestamp(Calendar cal)
2404: throws StandardException {
2405: // DB2 does not support internationalized timestamps
2406: return getTimestamp(cal, getString(), getLocaleFinder());
2407: }
2408:
2409: protected void setLocaleFinder(LocaleFinder localeFinder) {
2410: this .localeFinder = localeFinder;
2411: }
2412:
2413: /** @exception StandardException Thrown on error */
2414: private Locale getLocale() throws StandardException {
2415: return getLocaleFinder().getCurrentLocale();
2416: }
2417:
2418: protected LocaleFinder getLocaleFinder() {
2419: // This is not very satisfactory, as it creates a dependency on
2420: // the DatabaseContext. It's the best I could do on short notice,
2421: // though. - Jeff
2422: if (localeFinder == null) {
2423: DatabaseContext dc = (DatabaseContext) ContextService
2424: .getContext(DatabaseContext.CONTEXT_ID);
2425: if (dc != null)
2426: localeFinder = dc.getDatabase();
2427: }
2428:
2429: return localeFinder;
2430: }
2431:
2432: protected DateFormat getDateFormat() throws StandardException {
2433: return getLocaleFinder().getDateFormat();
2434: }
2435:
2436: protected DateFormat getTimeFormat() throws StandardException {
2437: return getLocaleFinder().getTimeFormat();
2438: }
2439:
2440: protected DateFormat getTimestampFormat() throws StandardException {
2441: return getLocaleFinder().getTimestampFormat();
2442: }
2443:
2444: protected DateFormat getDateFormat(Calendar cal)
2445: throws StandardException {
2446: return setDateFormatCalendar(getLocaleFinder().getDateFormat(),
2447: cal);
2448: }
2449:
2450: protected DateFormat getTimeFormat(Calendar cal)
2451: throws StandardException {
2452: return setDateFormatCalendar(getLocaleFinder().getTimeFormat(),
2453: cal);
2454: }
2455:
2456: protected DateFormat getTimestampFormat(Calendar cal)
2457: throws StandardException {
2458: return setDateFormatCalendar(getLocaleFinder()
2459: .getTimestampFormat(), cal);
2460: }
2461:
2462: private DateFormat setDateFormatCalendar(DateFormat df, Calendar cal) {
2463: if (cal != null && df.getTimeZone() != cal.getTimeZone()) {
2464: // The DateFormat returned by getDateFormat may be cached and used by other threads.
2465: // Therefore we cannot change its calendar.
2466: df = (DateFormat) df.clone();
2467: df.setCalendar(cal);
2468: }
2469: return df;
2470: }
2471:
2472: /*
2473: * object state
2474: */
2475:
2476: // Don't use value directly in most situations. Use getString()
2477: // OR use the rawData array if rawLength != -1.
2478: private String value;
2479:
2480: // rawData holds the reusable array for reading in
2481: // SQLChars. It contains a valid value if rawLength
2482: // is greater than or equal to 0. See getString() to see how it is
2483: // converted to a String. Even when converted to a String
2484: // object the rawData array remains for potential future
2485: // use, unless rawLength is > 4096. In this case the
2486: // rawData is set to null to avoid huge memory use.
2487: private char[] rawData;
2488: private int rawLength = -1;
2489:
2490: // For null strings, cKey = null.
2491: private CollationKey cKey;
2492:
2493: /**
2494: * The value as a stream in the on-disk format.
2495: */
2496: InputStream stream;
2497:
2498: /* Comparison info for National subclasses) */
2499: private int[] intArray;
2500: private int intLength;
2501:
2502: /* Locale info (for International support) */
2503: private LocaleFinder localeFinder;
2504:
2505: private static final int BASE_MEMORY_USAGE = ClassSize
2506: .estimateBaseFromCatalog(SQLChar.class);
2507:
2508: public int estimateMemoryUsage() {
2509: int sz = BASE_MEMORY_USAGE
2510: + ClassSize.estimateMemoryUsage(value);
2511: if (null != rawData)
2512: sz += 2 * rawData.length;
2513: // Assume that cKey, stream, and localFinder are shared, so do not count their memory usage
2514: if (null != intArray)
2515: sz += intArray.length * ClassSize.getIntSize();
2516: return sz;
2517: } // end of estimateMemoryUsage
2518:
2519: protected void copyState(SQLChar other) {
2520:
2521: this.value = other.value;
2522: this.rawData = other.rawData;
2523: this.rawLength = other.rawLength;
2524: this.cKey = other.cKey;
2525: this.stream = other.stream;
2526: this.intArray = intArray;
2527: this.intLength = intLength;
2528: this.localeFinder = localeFinder;
2529: }
2530:
2531: }
|