/* Reference Implementation of
* Stax2 extension API (for basic Stax API, JSR-173)
*
* Copyright (c) 2008- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.stax2.ri.typed;
import java.math.
BigDecimal;
import java.math.
BigInteger;
import javax.xml.namespace.
NamespaceContext;
import javax.xml.namespace.
QName;
import org.codehaus.stax2.typed.
TypedArrayDecoder;
import org.codehaus.stax2.typed.
TypedValueDecoder;
/**
* Factory class used to construct all
* {@link org.codehaus.stax2.typed.TypedValueDecoder}
* (and {@link org.codehaus.stax2.typed.TypedArrayDecoder})
* instances needed by a
* single stream reader instance. Some decoders are also recycled
* (for the lifetime of an encoder, which is same as its owners,
* i.e. stream reader or writer's) to minimize overhead.
*<p>
* Since encoders may be recycled, instances are not thread-safe.
*
* @since 3.0
*/
public final class
ValueDecoderFactory
{
// // // Lazily-constructed, recycled decoder instances
// // // (only for simple commonly needed types)
protected
BooleanDecoder mBooleanDecoder = null;
protected
IntDecoder mIntDecoder = null;
protected
LongDecoder mLongDecoder = null;
protected
FloatDecoder mFloatDecoder = null;
protected
DoubleDecoder mDoubleDecoder = null;
public
ValueDecoderFactory() { }
/*
/////////////////////////////////////////////////////
// Factory methods, scalar decoders
/////////////////////////////////////////////////////
*/
public
BooleanDecoder getBooleanDecoder()
{
if (
mBooleanDecoder == null) {
mBooleanDecoder = new
BooleanDecoder();
}
return
mBooleanDecoder;
}
public
IntDecoder getIntDecoder()
{
if (
mIntDecoder == null) {
mIntDecoder = new
IntDecoder();
}
return
mIntDecoder;
}
public
LongDecoder getLongDecoder()
{
if (
mLongDecoder == null) {
mLongDecoder = new
LongDecoder();
}
return
mLongDecoder;
}
public
FloatDecoder getFloatDecoder()
{
if (
mFloatDecoder == null) {
mFloatDecoder = new
FloatDecoder();
}
return
mFloatDecoder;
}
public
DoubleDecoder getDoubleDecoder()
{
if (
mDoubleDecoder == null) {
mDoubleDecoder = new
DoubleDecoder();
}
return
mDoubleDecoder;
}
// // // Other scalar decoders: not recycled
public
IntegerDecoder getIntegerDecoder() { return new
IntegerDecoder(); }
public
DecimalDecoder getDecimalDecoder() { return new
DecimalDecoder(); }
public
QNameDecoder getQNameDecoder(
NamespaceContext nsc) {
return new
QNameDecoder(
nsc);
}
/*
/////////////////////////////////////////////////////
// Factory methods, array decoders
/////////////////////////////////////////////////////
*/
/**
* Method for constructing
* integer array value decoder
* that uses provided fixed array for storing results.
*/
public
IntArrayDecoder getIntArrayDecoder(int[]
result, int
offset, int
len)
{
return new
IntArrayDecoder(
result,
offset,
len,
getIntDecoder());
}
/**
* Method for constructing
* integer array value decoder
* that automatically allocates and resizes result array as necessary.
*/
public
IntArrayDecoder getIntArrayDecoder()
{
return new
IntArrayDecoder(
getIntDecoder());
}
public
LongArrayDecoder getLongArrayDecoder(long[]
result, int
offset, int
len)
{
return new
LongArrayDecoder(
result,
offset,
len,
getLongDecoder());
}
public
LongArrayDecoder getLongArrayDecoder()
{
return new
LongArrayDecoder(
getLongDecoder());
}
public
FloatArrayDecoder getFloatArrayDecoder(float[]
result, int
offset, int
len)
{
return new
FloatArrayDecoder(
result,
offset,
len,
getFloatDecoder());
}
public
FloatArrayDecoder getFloatArrayDecoder()
{
return new
FloatArrayDecoder(
getFloatDecoder());
}
public
DoubleArrayDecoder getDoubleArrayDecoder(double[]
result, int
offset, int
len)
{
return new
DoubleArrayDecoder(
result,
offset,
len,
getDoubleDecoder());
}
public
DoubleArrayDecoder getDoubleArrayDecoder()
{
return new
DoubleArrayDecoder(
getDoubleDecoder());
}
/*
/////////////////////////////////////////////////////
// Shared decoder base class
/////////////////////////////////////////////////////
*/
/**
* There are some things common to all textual decoders (like
* white space trimming).
*/
public abstract static class
DecoderBase
extends
TypedValueDecoder
{
final static long
L_BILLION = 1000000000;
final static long
L_MAX_INT = (long)
Integer.
MAX_VALUE;
final static long
L_MIN_INT = (long)
Integer.
MIN_VALUE;
final static
BigInteger BD_MIN_LONG =
BigInteger.
valueOf(
Long.
MIN_VALUE);
final static
BigInteger BD_MAX_LONG =
BigInteger.
valueOf(
Long.
MAX_VALUE);
/**
* Pointer to the next character to check, within lexical value
*/
protected int
mNextPtr;
protected
DecoderBase() { }
public abstract
String getType();
/**
* Method called if the value to decode does not contain
* any non-white space characters (including the case where
* typed accessor is called for an empty element).
*/
@
Override
public void
handleEmptyValue()
{
/* Defalt behavior for all types implemented within
* this class is to just throw an exception
*/
throw new
IllegalArgumentException("Empty value (all white space) not a valid lexical representation of "+
getType());
}
/*
//////////////////////////////////////////////////
// Shared methods, trimming
//////////////////////////////////////////////////
*/
/**
* Method called to check that remaining String consists of zero or
* more digits
*/
protected void
verifyDigits(
String lexical, int
start, int
end)
{
for (;
start <
end; ++
start) {
char
ch =
lexical.
charAt(
start);
if (
ch > '9' ||
ch < '0') {
throw
constructInvalidValue(
lexical);
}
}
}
protected void
verifyDigits(char[]
lexical, int
start, int
end, int
ptr)
{
for (;
ptr <
end; ++
ptr) {
char
ch =
lexical[
ptr];
if (
ch > '9' ||
ch < '0') {
throw
constructInvalidValue(
lexical,
start,
end);
}
}
}
/**
* @return Numeric value of the first non-zero character (or, in
* case of a zero value, zero)
*/
protected int
skipSignAndZeroes(
String lexical, char
ch, boolean
hasSign, final int
end)
{
int
ptr;
// Then optional sign
if (
hasSign) {
ptr = 1;
if (
ptr >=
end) {
throw
constructInvalidValue(
lexical);
}
ch =
lexical.
charAt(
ptr++);
} else {
ptr = 1;
}
// Has to start with a digit
int
value =
ch - '0';
if (
value < 0 ||
value > 9) {
throw
constructInvalidValue(
lexical);
}
// Then, leading zero(es) to skip? (or just value zero itself)
while (
value == 0 &&
ptr <
end) {
int
v2 =
lexical.
charAt(
ptr) - '0';
if (
v2 < 0 ||
v2 > 9) {
break;
}
++
ptr;
value =
v2;
}
mNextPtr =
ptr;
return
value;
}
protected int
skipSignAndZeroes(char[]
lexical, char
ch, boolean
hasSign, final int
start, final int
end)
{
int
ptr =
start+1;
if (
hasSign) {
if (
ptr >=
end) {
throw
constructInvalidValue(
lexical,
start,
end);
}
ch =
lexical[
ptr++];
}
// Has to start with a digit
int
value =
ch - '0';
if (
value < 0 ||
value > 9) {
throw
constructInvalidValue(
lexical,
start,
end);
}
// Then leading zero(es) to skip? (or just value zero itself)
while (
value == 0 &&
ptr <
end) {
int
v2 =
lexical[
ptr] - '0';
if (
v2 < 0 ||
v2 > 9) {
break;
}
++
ptr;
value =
v2;
}
mNextPtr =
ptr;
return
value;
}
/*
///////////////////////////////////////////////
// Shared methods, int conversions
///////////////////////////////////////////////
*/
/**
* Fast method for parsing integers that are known to fit into
* regular 32-bit signed int type. This means that length is
* between 1 and 9 digits (inclusive)
*
* @return Parsed integer value
*/
protected final static int
parseInt(char[]
digitChars, int
start, int
end)
{
/* This looks ugly, but appears to be the fastest way
* (based on perf testing, profiling)
*/
int
num =
digitChars[
start] - '0';
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
}
}
}
}
}
}
}
}
return
num;
}
protected final static int
parseInt(int
num, char[]
digitChars, int
start, int
end)
{
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars[
start] - '0');
}
}
}
}
}
}
}
return
num;
}
protected final static int
parseInt(
String digitChars, int
start, int
end)
{
int
num =
digitChars.
charAt(
start) - '0';
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
}
}
}
}
}
}
}
}
return
num;
}
protected final static int
parseInt(int
num,
String digitChars, int
start, int
end)
{
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
if (++
start <
end) {
num = (
num * 10) + (
digitChars.
charAt(
start) - '0');
}
}
}
}
}
}
}
return
num;
}
protected final static long
parseLong(char[]
digitChars, int
start, int
end)
{
// Note: caller must ensure length is [10, 18]
int
start2 =
end-9;
long
val =
parseInt(
digitChars,
start,
start2) *
L_BILLION;
return
val + (long)
parseInt(
digitChars,
start2,
end);
}
protected final static long
parseLong(
String digitChars, int
start, int
end)
{
// Note: caller must ensure length is [10, 18]
int
start2 =
end-9;
long
val =
parseInt(
digitChars,
start,
start2) *
L_BILLION;
return
val + (long)
parseInt(
digitChars,
start2,
end);
}
/*
///////////////////////////////////////////////
// Shared methods, error reporting
///////////////////////////////////////////////
*/
protected
IllegalArgumentException constructInvalidValue(
String lexical)
{
// !!! Should we escape ctrl+chars etc?
return new
IllegalArgumentException("Value \""+
lexical+"\" not a valid lexical representation of "+
getType());
}
protected
IllegalArgumentException constructInvalidValue(char[]
lexical, int
startOffset, int
end)
{
return new
IllegalArgumentException("Value \""+
lexicalDesc(
lexical,
startOffset,
end)+"\" not a valid lexical representation of "+
getType());
}
protected
String lexicalDesc(char[]
lexical, int
startOffset, int
end)
{
return
_clean(new
String(
lexical,
startOffset,
end-
startOffset));
}
protected
String lexicalDesc(
String lexical)
{
return
_clean(
lexical);
}
protected
String _clean(
String str)
{
// !!! Should we escape ctrl+chars etc?
return
str.
trim();
}
}
/*
/////////////////////////////////////////////////////
// Decoders, scalar primitives
/////////////////////////////////////////////////////
*/
public final static class
BooleanDecoder
extends
DecoderBase
{
protected boolean
mValue;
public
BooleanDecoder() { }
@
Override
public
String getType() { return "boolean"; }
public boolean
getValue() { return
mValue; }
@
Override
public void
decode(
String lexical) throws
IllegalArgumentException
{
int
len =
lexical.
length();
char
c =
lexical.
charAt(0);
if (
c == 't') {
if (
len == 4
&&
lexical.
charAt(1) == 'r'
&&
lexical.
charAt(2) == 'u'
&&
lexical.
charAt(3) == 'e') {
mValue = true;
return;
}
} else if (
c == 'f') {
if (
len == 5
&&
lexical.
charAt(1) == 'a'
&&
lexical.
charAt(2) == 'l'
&&
lexical.
charAt(3) == 's'
&&
lexical.
charAt(4) == 'e') {
mValue = false;
return;
}
} else if (
c == '0') {
if (
len == 1) {
mValue = false;
return;
}
} else if (
c == '1') {
if (
len == 1) {
mValue = true;
return;
}
}
throw
constructInvalidValue(
lexical);
}
@
Override
public void
decode(char[]
lexical, int
start, int
end)
throws
IllegalArgumentException
{
// First, skip leading ws if any
int
len =
end-
start;
char
c =
lexical[
start];
if (
c == 't') {
if (
len == 4
&&
lexical[
start+1] == 'r'
&&
lexical[
start+2] == 'u'
&&
lexical[
start+3] == 'e') {
mValue = true;
return;
}
} else if (
c == 'f') {
if (
len == 5
&&
lexical[
start+1] == 'a'
&&
lexical[
start+2] == 'l'
&&
lexical[
start+3] == 's'
&&
lexical[
start+4] == 'e') {
mValue = false;
return;
}
} else if (
c == '0') {
if (
len == 1) {
mValue = false;
return;
}
} else if (
c == '1') {
if (
len == 1) {
mValue = true;
return;
}
}
throw
constructInvalidValue(
lexical,
start,
end);
}
}
public final static class
IntDecoder
extends
DecoderBase
{
protected int
mValue;
public
IntDecoder() { }
@
Override
public
String getType() { return "int"; }
public int
getValue() { return
mValue; }
@
Override
public void
decode(
String lexical) throws
IllegalArgumentException
{
final int
end =
lexical.
length();
char
ch =
lexical.
charAt(0);
boolean
neg = (
ch == '-');
int
nr;
if (
neg || (
ch == '+')) {
nr =
skipSignAndZeroes(
lexical,
ch, true,
end);
} else {
nr =
skipSignAndZeroes(
lexical,
ch, false,
end);
}
int
ptr =
mNextPtr;
// Otherwise, need to verify that is [digit*][ws*]
int
charsLeft =
end-
ptr;
if (
charsLeft == 0) {
mValue =
neg ? -
nr :
nr;
return;
}
verifyDigits(
lexical,
ptr,
end);
// Note: charsLeft one less than total length (skipped first digit)
if (
charsLeft <= 8) { // no overflow
int
i =
parseInt(
nr,
lexical,
ptr,
ptr+
charsLeft);
mValue =
neg ? -
i :
i;
return;
}
// Otherwise, may have overflow
// Max 10 digits for a legal int
if (
charsLeft == 9 &&
nr < 3) { // min/max is ~2 billion (+/-)
long
base =
L_BILLION;
if (
nr == 2) {
base +=
L_BILLION;
}
int
i =
parseInt(
lexical,
ptr,
ptr+
charsLeft);
long
l =
base + (long)
i;
if (
neg) {
l = -
l;
if (
l >=
L_MIN_INT) {
mValue = (int)
l;
return;
}
} else {
if (
l <=
L_MAX_INT) {
mValue = (int)
l;
return;
}
}
}
throw new
IllegalArgumentException("value \""+
lexicalDesc(
lexical)+"\" not a valid 32-bit integer: overflow.");
}
@
Override
public void
decode(char[]
lexical, final int
start, final int
end)
throws
IllegalArgumentException
{
char
ch =
lexical[
start];
boolean
neg = (
ch == '-');
int
nr;
if (
neg || (
ch == '+')) {
nr =
skipSignAndZeroes(
lexical,
ch, true,
start,
end);
} else {
nr =
skipSignAndZeroes(
lexical,
ch, false,
start,
end);
}
int
ptr =
mNextPtr;
// Quick check for short (single-digit) values:
int
charsLeft =
end-
ptr;
if (
charsLeft == 0) {
mValue =
neg ? -
nr :
nr;
return;
}
verifyDigits(
lexical,
start,
end,
ptr);
// Note: charsLeft one less than total length (skipped first digit)
// Can parse more cheaply, if it's really just an int...
if (
charsLeft <= 8) { // no overflow
int
i =
parseInt(
nr,
lexical,
ptr,
ptr+
charsLeft);
mValue =
neg ? -
i :
i;
return;
}
// Otherwise, may have overflow
// Max 10 digits for a legal int
if (
charsLeft == 9 &&
nr < 3) { // min/max is ~2 billion (+/-)
long
base =
L_BILLION;
if (
nr == 2) {
base +=
L_BILLION;
}
int
i =
parseInt(
lexical,
ptr,
ptr+
charsLeft);
long
l =
base + (long)
i;
if (
neg) {
l = -
l;
if (
l >=
L_MIN_INT) {
mValue = (int)
l;
return;
}
} else {
if (
l <=
L_MAX_INT) {
mValue = (int)
l;
return;
}
}
}
throw new
IllegalArgumentException("value \""+
lexicalDesc(
lexical,
start,
end)+"\" not a valid 32-bit integer: overflow.");
}
}
public final static class
LongDecoder
extends
DecoderBase
{
protected long
mValue;
public
LongDecoder() { }
@
Override
public
String getType() { return "long"; }
public long
getValue() { return
mValue; }
@
Override
public void
decode(
String lexical)
throws
IllegalArgumentException
{
final int
end =
lexical.
length();
char
ch =
lexical.
charAt(0);
boolean
neg = (
ch == '-');
int
nr;
if (
neg || (
ch == '+')) {
nr =
skipSignAndZeroes(
lexical,
ch, true,
end);
} else {
nr =
skipSignAndZeroes(
lexical,
ch, false,
end);
}
int
ptr =
mNextPtr;
// Quick check for short (single-digit) values:
int
charsLeft =
end-
ptr;
if (
charsLeft == 0) {
mValue = (long) (
neg ? -
nr :
nr);
return;
}
verifyDigits(
lexical,
ptr,
end);
// Note: charsLeft one less than total length (skipped first digit)
// Can parse more cheaply, if it's really just an int...
if (
charsLeft <= 8) { // no overflow
int
i =
parseInt(
nr,
lexical,
ptr,
ptr+
charsLeft);
mValue = (long) (
neg ? -
i :
i);
return;
}
// At this point, let's just push back the first digit... simpler
--
ptr;
++
charsLeft;
// Still simple long?
if (
charsLeft <= 18) {
long
l =
parseLong(
lexical,
ptr,
ptr+
charsLeft);
mValue =
neg ? -
l :
l;
return;
}
/* Otherwise, let's just fallback to an expensive option,
* BigInteger. While relatively inefficient, it's simple
* to use, reliable etc.
*/
mValue =
parseUsingBD(
lexical.
substring(
ptr,
ptr+
charsLeft),
neg);
}
@
Override
public void
decode(char[]
lexical, final int
start, final int
end)
throws
IllegalArgumentException
{
char
ch =
lexical[
start];
boolean
neg = (
ch == '-');
int
nr;
if (
neg || (
ch == '+')) {
nr =
skipSignAndZeroes(
lexical,
ch, true,
start,
end);
} else {
nr =
skipSignAndZeroes(
lexical,
ch, false,
start,
end);
}
int
ptr =
mNextPtr;
// Quick check for short (single-digit) values:
int
charsLeft =
end-
ptr;
if (
charsLeft == 0) {
mValue = (long) (
neg ? -
nr :
nr);
return;
}
verifyDigits(
lexical,
start,
end,
ptr);
// Note: charsLeft one less than total length (skipped first digit)
// Can parse more cheaply, if it's really just an int...
if (
charsLeft <= 8) { // no overflow
int
i =
parseInt(
nr,
lexical,
ptr,
ptr+
charsLeft);
mValue =
neg ? -
i :
i;
return;
}
// At this point, let's just push back the first digit... simpler
--
ptr;
++
charsLeft;
// Still simple long?
if (
charsLeft <= 18) {
long
l =
parseLong(
lexical,
ptr,
ptr+
charsLeft);
mValue =
neg ? -
l :
l;
return;
}
// Otherwise, let's just fallback to an expensive option
mValue =
parseUsingBD(new
String(
lexical,
ptr,
charsLeft),
neg);
}
private long
parseUsingBD(
String lexical, boolean
neg)
{
BigInteger bi = new
BigInteger(
lexical);
// But we may over/underflow, let's check:
if (
neg) {
bi =
bi.
negate();
if (
bi.
compareTo(
BD_MIN_LONG) >= 0) {
return
bi.
longValue();
}
} else {
if (
bi.
compareTo(
BD_MAX_LONG) <= 0) {
return
bi.
longValue();
}
}
throw new
IllegalArgumentException("value \""+
lexicalDesc(
lexical)+"\" not a valid long: overflow.");
}
}
public final static class
FloatDecoder
extends
DecoderBase
{
protected float
mValue;
public
FloatDecoder() { }
@
Override
public
String getType() { return "float"; }
public float
getValue() { return
mValue; }
@
Override
public void
decode(
String lexical)
throws
IllegalArgumentException
{
/* Then, leading digit; or one of 3 well-known constants
* (INF, -INF, NaN)
*/
int
len =
lexical.
length();
if (
len == 3) {
char
c =
lexical.
charAt(0);
if (
c == 'I') {
if (
lexical.
charAt(1) == 'N' &&
lexical.
charAt(2) == 'F') {
mValue =
Float.
POSITIVE_INFINITY;
return;
}
} else if (
c == 'N') {
if (
lexical.
charAt(1) == 'a' &&
lexical.
charAt(2) == 'N') {
mValue =
Float.
NaN;
return;
}
}
} else if (
len == 4) {
char
c =
lexical.
charAt(0);
if (
c == '-') {
if (
lexical.
charAt(1) == 'I'
&&
lexical.
charAt(2) == 'N'
&&
lexical.
charAt(3) == 'F') {
mValue =
Float.
NEGATIVE_INFINITY;
return;
}
}
}
try {
mValue =
Float.
parseFloat(
lexical);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexical);
}
}
@
Override
public void
decode(char[]
lexical, int
start, int
end)
throws
IllegalArgumentException
{
int
len =
end-
start;
if (
len == 3) {
char
c =
lexical[
start];
if (
c == 'I') {
if (
lexical[
start+1] == 'N' &&
lexical[
start+2] == 'F') {
mValue =
Float.
POSITIVE_INFINITY;
return;
}
} else if (
c == 'N') {
if (
lexical[
start+1] == 'a' &&
lexical[
start+2] == 'N') {
mValue =
Float.
NaN;
return;
}
}
} else if (
len == 4) {
char
c =
lexical[
start];
if (
c == '-') {
if (
lexical[
start+1] == 'I'
&&
lexical[
start+2] == 'N'
&&
lexical[
start+3] == 'F') {
mValue =
Float.
NEGATIVE_INFINITY;
return;
}
}
}
String lexicalStr = new
String(
lexical,
start,
len);
try {
mValue =
Float.
parseFloat(
lexicalStr);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexicalStr);
}
}
}
public final static class
DoubleDecoder
extends
DecoderBase
{
protected double
mValue;
public
DoubleDecoder() { }
@
Override
public
String getType() { return "double"; }
public double
getValue() { return
mValue; }
@
Override
public void
decode(
String lexical)
throws
IllegalArgumentException
{
/* Then, leading digit; or one of 3 well-known constants
* (INF, -INF, NaN)
*/
int
len =
lexical.
length();
if (
len == 3) {
char
c =
lexical.
charAt(0);
if (
c == 'I') {
if (
lexical.
charAt(1) == 'N' &&
lexical.
charAt(2) == 'F') {
mValue =
Double.
POSITIVE_INFINITY;
return;
}
} else if (
c == 'N') {
if (
lexical.
charAt(1) == 'a' &&
lexical.
charAt(2) == 'N') {
mValue =
Double.
NaN;
return;
}
}
} else if (
len == 4) {
char
c =
lexical.
charAt(0);
if (
c == '-') {
if (
lexical.
charAt(1) == 'I'
&&
lexical.
charAt(2) == 'N'
&&
lexical.
charAt(3) == 'F') {
mValue =
Double.
NEGATIVE_INFINITY;
return;
}
}
}
try {
mValue =
Double.
parseDouble(
lexical);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexical);
}
}
@
Override
public void
decode(char[]
lexical, int
start, int
end)
throws
IllegalArgumentException
{
int
len =
end-
start;
if (
len == 3) {
char
c =
lexical[
start];
if (
c == 'I') {
if (
lexical[
start+1] == 'N' &&
lexical[
start+2] == 'F') {
mValue =
Double.
POSITIVE_INFINITY;
return;
}
} else if (
c == 'N') {
if (
lexical[
start+1] == 'a' &&
lexical[
start+2] == 'N') {
mValue =
Double.
NaN;
return;
}
}
} else if (
len == 4) {
char
c =
lexical[
start];
if (
c == '-') {
if (
lexical[
start+1] == 'I'
&&
lexical[
start+2] == 'N'
&&
lexical[
start+3] == 'F') {
mValue =
Double.
NEGATIVE_INFINITY;
return;
}
}
}
String lexicalStr = new
String(
lexical,
start,
len);
try {
mValue =
Double.
parseDouble(
lexicalStr);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexicalStr);
}
}
}
/*
/////////////////////////////////////////////////////
// Decoders, other scalars
/////////////////////////////////////////////////////
*/
public final static class
IntegerDecoder
extends
DecoderBase
{
protected
BigInteger mValue;
public
IntegerDecoder() { }
@
Override
public
String getType() { return "integer"; }
public
BigInteger getValue() { return
mValue; }
@
Override
public void
decode(
String lexical) throws
IllegalArgumentException
{
try {
mValue = new
BigInteger(
lexical);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexical);
}
}
@
Override
public void
decode(char[]
lexical, int
start, int
end) throws
IllegalArgumentException
{
String lexicalStr = new
String(
lexical,
start, (
end-
start));
try {
mValue = new
BigInteger(
lexicalStr);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexicalStr);
}
}
}
public final static class
DecimalDecoder
extends
DecoderBase
{
protected
BigDecimal mValue;
public
DecimalDecoder() { }
@
Override
public
String getType() { return "decimal"; }
public
BigDecimal getValue() { return
mValue; }
@
Override
public void
decode(
String lexical) throws
IllegalArgumentException
{
try {
mValue = new
BigDecimal(
lexical);
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(
lexical);
}
}
@
Override
public void
decode(char[]
lexical, int
start, int
end) throws
IllegalArgumentException
{
int
len =
end-
start;
try {
/* !!! 21-Nov-2008, TSa: This constructor was added in JDK1.5
* so can't yet be used (As of Woodstox 4.x).
* Need to use the older constructor for now
*/
//mValue = new BigDecimal(lexical, start, len);
mValue = new
BigDecimal(new
String(
lexical,
start,
len));
} catch (
NumberFormatException nex) {
throw
constructInvalidValue(new
String(
lexical,
start,
len));
}
}
}
public final static class
QNameDecoder
extends
DecoderBase
{
final
NamespaceContext mNsCtxt;
protected
QName mValue;
public
QNameDecoder(
NamespaceContext nsc) {
mNsCtxt =
nsc;
}
@
Override
public
String getType() { return "QName"; }
public
QName getValue() { return
mValue; }
@
Override
public void
decode(
String lexical) throws
IllegalArgumentException
{
int
ix =
lexical.
indexOf(':');
if (
ix >= 0) { // qualified name
mValue =
resolveQName(
lexical.
substring(0,
ix),
lexical.
substring(
ix+1));
} else {
mValue =
resolveQName(
lexical);
}
}
@
Override
public void
decode(char[]
lexical, int
start, int
end) throws
IllegalArgumentException
{
int
i =
start;
for (;
i <
end; ++
i) {
if (
lexical[
i] == ':') {
mValue =
resolveQName(new
String(
lexical,
start,
i-
start),
new
String(
lexical,
i+1,
end-
i-1));
return;
}
}
mValue =
resolveQName(new
String(
lexical,
start,
end-
start));
}
protected
QName resolveQName(
String localName) throws
IllegalArgumentException
{
// No prefix -> default namespace ("element rules")
String uri =
mNsCtxt.
getNamespaceURI("");
if (
uri == null) { // some impls may return null
uri = "";
}
return new
QName(
uri,
localName);
}
protected
QName resolveQName(
String prefix,
String localName)
throws
IllegalArgumentException
{
if (
prefix.
length() == 0 ||
localName.
length() == 0) {
// either prefix or local name is empty String, illegal
throw
constructInvalidValue(
prefix+":"+
localName);
}
/* Explicit prefix, must map to a bound namespace; and that
* namespace can not be empty (only "no prefix", i.e. 'default
* namespace' has empty URI)
*/
String uri =
mNsCtxt.
getNamespaceURI(
prefix);
if (
uri == null ||
uri.
length() == 0) {
throw new
IllegalArgumentException("Value \""+
lexicalDesc(
prefix+":"+
localName)+"\" not a valid QName: prefix '"+
prefix+"' not bound to a namespace");
}
return new
QName(
uri,
localName,
prefix);
}
}
/*
/////////////////////////////////////////////////////
// Decoders, array
/////////////////////////////////////////////////////
*/
/**
* Intermediate shared base class for token array decoders.
* The most important additional part is the abstract method
* that can be used to expand storage space; this is needed
* when decoding attribute values when all values must fit
* in the result array.
*/
public abstract static class
BaseArrayDecoder
extends
TypedArrayDecoder
{
/**
* Let's use some modest array size for allocating initial
* result buffer
*/
protected final static int
INITIAL_RESULT_BUFFER_SIZE = 40;
/**
* When expanding 'small' result buffers, we will expand
* size by bigger factor than for larger ones.
*/
protected final static int
SMALL_RESULT_BUFFER_SIZE = 4000;
protected int
mStart;
protected int
mEnd;
protected int
mCount = 0;
protected
BaseArrayDecoder(int
start, int
maxCount)
{
mStart =
start;
// First, sanity check
if (
maxCount < 1) {
throw new
IllegalArgumentException("Number of elements to read can not be less than 1");
}
mEnd =
maxCount;
}
@
Override
public final int
getCount() { return
mCount; }
@
Override
public final boolean
hasRoom() { return
mCount <
mEnd; }
/**
* Method that can be called if the internal result buffer
* fills up (when {@link #hasRoom} returns false) and
* will expand result buffer to hold at least one more value.
*/
public abstract void
expand();
protected int
calcNewSize(int
currSize)
{
if (
currSize <
SMALL_RESULT_BUFFER_SIZE) {
return
currSize << 2; // 4 x current for small bufs
}
return
currSize+
currSize; // 2x for bigger
}
}
public final static class
IntArrayDecoder
extends
BaseArrayDecoder
{
int[]
mResult;
final
IntDecoder mDecoder;
/**
* Constructor used for constructing decoders with fixed pre-allocated
* result buffer.
*/
public
IntArrayDecoder(int[]
result, int
start, int
maxCount,
IntDecoder intDecoder)
{
super(
start,
maxCount);
mResult =
result;
mDecoder =
intDecoder;
}
/**
* Constructor used for constructing decoders with automatically
* adjusting result buffer
*/
public
IntArrayDecoder(
IntDecoder intDecoder)
{
super(0,
INITIAL_RESULT_BUFFER_SIZE);
mResult = new int[
INITIAL_RESULT_BUFFER_SIZE];
mDecoder =
intDecoder;
}
@
Override
public void
expand()
{
int[]
old =
mResult;
int
oldLen =
old.length;
int
newSize =
calcNewSize(
oldLen);
mResult = new int[
newSize];
System.
arraycopy(
old,
mStart,
mResult, 0,
oldLen);
mStart = 0;
mEnd =
newSize;
}
public int[]
getValues()
{
int[]
result = new int[
mCount];
// !!! TBI: with JDK 6, use Arrays.copyOf:
System.
arraycopy(
mResult,
mStart,
result, 0,
mCount);
return
result;
}
@
Override
public boolean
decodeValue(
String input) throws
IllegalArgumentException
{
mDecoder.
decode(
input);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
@
Override
public boolean
decodeValue(char[]
buffer, int
start, int
end) throws
IllegalArgumentException
{
mDecoder.
decode(
buffer,
start,
end);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
}
public final static class
LongArrayDecoder
extends
BaseArrayDecoder
{
long[]
mResult;
final
LongDecoder mDecoder;
public
LongArrayDecoder(long[]
result, int
start, int
maxCount,
LongDecoder longDecoder)
{
super(
start,
maxCount);
mResult =
result;
mDecoder =
longDecoder;
}
public
LongArrayDecoder(
LongDecoder longDecoder)
{
super(0,
INITIAL_RESULT_BUFFER_SIZE);
mResult = new long[
INITIAL_RESULT_BUFFER_SIZE];
mDecoder =
longDecoder;
}
@
Override
public void
expand()
{
long[]
old =
mResult;
int
oldLen =
old.length;
int
newSize =
calcNewSize(
oldLen);
mResult = new long[
newSize];
System.
arraycopy(
old,
mStart,
mResult, 0,
oldLen);
mStart = 0;
mEnd =
newSize;
}
public long[]
getValues()
{
long[]
result = new long[
mCount];
System.
arraycopy(
mResult,
mStart,
result, 0,
mCount);
return
result;
}
@
Override
public boolean
decodeValue(
String input) throws
IllegalArgumentException
{
mDecoder.
decode(
input);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
@
Override
public boolean
decodeValue(char[]
buffer, int
start, int
end) throws
IllegalArgumentException
{
mDecoder.
decode(
buffer,
start,
end);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
}
public final static class
FloatArrayDecoder
extends
BaseArrayDecoder
{
float[]
mResult;
final
FloatDecoder mDecoder;
public
FloatArrayDecoder(float[]
result, int
start, int
maxCount,
FloatDecoder floatDecoder)
{
super(
start,
maxCount);
mResult =
result;
mDecoder =
floatDecoder;
}
public
FloatArrayDecoder(
FloatDecoder floatDecoder)
{
super(0,
INITIAL_RESULT_BUFFER_SIZE);
mResult = new float[
INITIAL_RESULT_BUFFER_SIZE];
mDecoder =
floatDecoder;
}
@
Override
public void
expand()
{
float[]
old =
mResult;
int
oldLen =
old.length;
int
newSize =
calcNewSize(
oldLen);
mResult = new float[
newSize];
System.
arraycopy(
old,
mStart,
mResult, 0,
oldLen);
mStart = 0;
mEnd =
newSize;
}
public float[]
getValues()
{
float[]
result = new float[
mCount];
System.
arraycopy(
mResult,
mStart,
result, 0,
mCount);
return
result;
}
@
Override
public boolean
decodeValue(
String input) throws
IllegalArgumentException
{
mDecoder.
decode(
input);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
@
Override
public boolean
decodeValue(char[]
buffer, int
start, int
end) throws
IllegalArgumentException
{
mDecoder.
decode(
buffer,
start,
end);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
}
public final static class
DoubleArrayDecoder
extends
BaseArrayDecoder
{
double[]
mResult;
final
DoubleDecoder mDecoder;
public
DoubleArrayDecoder(double[]
result, int
start, int
maxCount,
DoubleDecoder doubleDecoder)
{
super(
start,
maxCount);
mResult =
result;
mDecoder =
doubleDecoder;
}
public
DoubleArrayDecoder(
DoubleDecoder doubleDecoder)
{
super(0,
INITIAL_RESULT_BUFFER_SIZE);
mResult = new double[
INITIAL_RESULT_BUFFER_SIZE];
mDecoder =
doubleDecoder;
}
@
Override
public void
expand()
{
double[]
old =
mResult;
int
oldLen =
old.length;
int
newSize =
calcNewSize(
oldLen);
mResult = new double[
newSize];
System.
arraycopy(
old,
mStart,
mResult, 0,
oldLen);
mStart = 0;
mEnd =
newSize;
}
public double[]
getValues()
{
double[]
result = new double[
mCount];
System.
arraycopy(
mResult,
mStart,
result, 0,
mCount);
return
result;
}
@
Override
public boolean
decodeValue(
String input) throws
IllegalArgumentException
{
mDecoder.
decode(
input);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
@
Override
public boolean
decodeValue(char[]
buffer, int
start, int
end) throws
IllegalArgumentException
{
mDecoder.
decode(
buffer,
start,
end);
mResult[
mStart+
mCount] =
mDecoder.
getValue();
return (++
mCount >=
mEnd);
}
}
}