Fixed problems with symbol table, minor numeric parsing improvements, added comments.
diff --git a/src/java/org/codehaus/jackson/JsonGenerator.java b/src/java/org/codehaus/jackson/JsonGenerator.java
index da188df..caa05c4 100644
--- a/src/java/org/codehaus/jackson/JsonGenerator.java
+++ b/src/java/org/codehaus/jackson/JsonGenerator.java
@@ -23,6 +23,15 @@
////////////////////////////////////////////////////
*/
+ /**
+ * Method for setting a custom pretty printer, which is usually
+ * used to add indentation for improved human readability.
+ * By default, generator does not do pretty printing.
+ *<p>
+ * To use the default pretty printer that comes with core
+ * Jackson distribution, call {@link #useDefaultPrettyPrinter}
+ * instead.
+ */
public final void setPrettyPrinter(PrettyPrinter pp) {
mPrettyPrinter = pp;
}
@@ -101,9 +110,25 @@
/**
* Method that will output given chunk of binary data as base64
* encoded, as a complete String value (surrounded by double quotes).
- * Note:
+ *<p>
+ * Note: because JSON Strings can not contain unescaped linefeeds,
+ * if linefeeds are included (as per last argument), they must be
+ * escaped. This adds overhead for decoding without improving
+ * readability.
+ * Alternatively if linefeeds are not included,
+ * resulting String value may violate the requirement of base64
+ * RFC which mandates line-length of 76 characters and use of
+ * linefeeds. However, all {@link JsonParser} implementations
+ * are required to accept such "long line base64"; as do
+ * typical production-level base64 decoders.
+ *
+ * @param includeLFs If true, will add linefeeds (single character,
+ * "\n") as mandated by
+ * the RFC that specifies canonical base64 encoding. Due to
+ * JSON String value requirements, linefeeds must be escaped.
*/
- public abstract void writeBinary(byte[] data, int offset, int len)
+ public abstract void writeBinary(byte[] data, int offset, int len,
+ boolean includeLFs)
throws IOException, JsonGenerationException;
/*
@@ -127,6 +152,14 @@
public abstract void writeNumber(BigDecimal dec)
throws IOException, JsonGenerationException;
+ /**
+ * Write method that can be used for custom numeric types that can
+ * not be (easily?) converted to "standard" Java number types.
+ * Because numbers are not surrounded by double quotes, regular
+ * {@link #writeString} method can not be used; nor
+ * {@link #writeRaw} because that does not properly handle
+ * value separators needed in Array or Object contexts.
+ */
public abstract void writeNumber(String encodedValue)
throws IOException, JsonGenerationException;
diff --git a/src/java/org/codehaus/jackson/JsonParser.java b/src/java/org/codehaus/jackson/JsonParser.java
index 49383cc..331aff0 100644
--- a/src/java/org/codehaus/jackson/JsonParser.java
+++ b/src/java/org/codehaus/jackson/JsonParser.java
@@ -163,7 +163,7 @@
* If current event is of type
* {@link JsonToken#VALUE_NUMBER_INT} or
* {@link JsonToken#VALUE_NUMBER_FLOAT}, returns
- * one of type constants; otherwise returns null.
+ * one of {@link NumberType} constants; otherwise returns null.
*/
public abstract NumberType getNumberType()
throws IOException, JsonParseException;
@@ -197,4 +197,31 @@
public abstract BigDecimal getDecimalValue()
throws IOException, JsonParseException;
+
+ /*
+ ////////////////////////////////////////////////////
+ // Public API, access to token information, binary
+ ////////////////////////////////////////////////////
+ */
+
+ /**
+ * Method that can be used to read (and consume -- results
+ * may not be accessible using other methods after the call)
+ * base64-encoded binary data
+ * included in the current textual json value. It is equivalent
+ * to getting String value via {@link #getText} and decoding
+ * result, but should be significantly more performant.
+ *<p>
+ * Note that the contents may be consumed by this call, and thus
+ * only first call to method will produce any output. Likewise,
+ * calls to methods like {@link #getText} are not guaranteed
+ * to return anything.
+ *
+ * @param results Output stream used for returning decoded binary
+ * data
+ *
+ * @return Number of bytes decoded and written to <b>results</b>
+ */
+ public abstract int readBinaryValue(OutputStream results)
+ throws IOException, JsonParseException;
}
diff --git a/src/java/org/codehaus/jackson/impl/JsonGeneratorBase.java b/src/java/org/codehaus/jackson/impl/JsonGeneratorBase.java
index 42cc157..488a702 100644
--- a/src/java/org/codehaus/jackson/impl/JsonGeneratorBase.java
+++ b/src/java/org/codehaus/jackson/impl/JsonGeneratorBase.java
@@ -130,20 +130,15 @@
////////////////////////////////////////////////////
*/
- public abstract void writeString(String text)
- throws IOException, JsonGenerationException;
+ //public abstract void writeString(String text) throws IOException, JsonGenerationException;
- public abstract void writeString(char[] text, int offset, int len)
- throws IOException, JsonGenerationException;
+ //public abstract void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException;
- public abstract void writeRaw(String text)
- throws IOException, JsonGenerationException;
+ //public abstract void writeRaw(String text) throws IOException, JsonGenerationException;
- public abstract void writeRaw(char[] text, int offset, int len)
- throws IOException, JsonGenerationException;
+ //public abstract void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException;
- public abstract void writeBinary(byte[] data, int offset, int len)
- throws IOException, JsonGenerationException;
+ //public abstract void writeBinary(byte[] data, int offset, int len, boolean includeLFs) throws IOException, JsonGenerationException;
/*
////////////////////////////////////////////////////
diff --git a/src/java/org/codehaus/jackson/impl/JsonNumericParserBase.java b/src/java/org/codehaus/jackson/impl/JsonNumericParserBase.java
index c2c3086..233de51 100644
--- a/src/java/org/codehaus/jackson/impl/JsonNumericParserBase.java
+++ b/src/java/org/codehaus/jackson/impl/JsonNumericParserBase.java
@@ -24,6 +24,8 @@
* the fastest one that works for given textual representation.
*/
+ final protected static int NR_UNKNOWN = 0;
+
// First, integer types
final protected static int NR_INT = 0x0001;
@@ -84,7 +86,7 @@
* Bitfield that indicates which numeric representations
* have been calculated for the current type
*/
- protected int mNumTypesValid = 0;
+ protected int mNumTypesValid = NR_UNKNOWN;
// First primitives
@@ -145,7 +147,7 @@
mIntLength = intLen;
mFractLength = fractLen;
mExpLength = expLen;
- mNumTypesValid = 0; // to force parsing
+ mNumTypesValid = NR_UNKNOWN; // to force parsing
if (fractLen < 1 && expLen < 1) { // integer
return (mCurrToken = JsonToken.VALUE_NUMBER_INT);
}
@@ -171,8 +173,8 @@
public Number getNumberValue()
throws IOException, JsonParseException
{
- if (mNumTypesValid == 0) {
- parseNumericValue(); // will also check event type
+ if (mNumTypesValid == NR_UNKNOWN) {
+ parseNumericValue(NR_UNKNOWN); // will also check event type
}
// Separate types for int types
if (mCurrToken == JsonToken.VALUE_NUMBER_INT) {
@@ -204,8 +206,8 @@
public NumberType getNumberType()
throws IOException, JsonParseException
{
- if (mNumTypesValid == 0) {
- parseNumericValue(); // will also check event type
+ if (mNumTypesValid == NR_UNKNOWN) {
+ parseNumericValue(NR_UNKNOWN); // will also check event type
}
if (mCurrToken == JsonToken.VALUE_NUMBER_INT) {
if ((mNumTypesValid & NR_INT) != 0) {
@@ -219,7 +221,9 @@
/* And then floating point types. Here optimal type
* needs to be big decimal, to avoid losing any data?
- * However...
+ * However... using BD is slow, so let's allow returning
+ * double as type if no explicit call has been made to access
+ * data as BD?
*/
if ((mNumTypesValid & NR_BIGDECIMAL) != 0) {
return NumberType.BIG_DECIMAL;
@@ -231,8 +235,8 @@
throws IOException, JsonParseException
{
if ((mNumTypesValid & NR_INT) == 0) {
- if (mNumTypesValid == 0) { // not parsed at all
- parseNumericValue(); // will also check event type
+ if (mNumTypesValid == NR_UNKNOWN) { // not parsed at all
+ parseNumericValue(NR_INT); // will also check event type
}
if ((mNumTypesValid & NR_INT) == 0) { // wasn't an int natively?
convertNumberToInt(); // let's make it so, if possible
@@ -245,8 +249,8 @@
throws IOException, JsonParseException
{
if ((mNumTypesValid & NR_LONG) == 0) {
- if (mNumTypesValid == 0) {
- parseNumericValue();
+ if (mNumTypesValid == NR_UNKNOWN) {
+ parseNumericValue(NR_LONG);
}
if ((mNumTypesValid & NR_LONG) == 0) {
convertNumberToLong();
@@ -259,8 +263,8 @@
throws IOException, JsonParseException
{
if ((mNumTypesValid & NR_DOUBLE) == 0) {
- if (mNumTypesValid == 0) {
- parseNumericValue();
+ if (mNumTypesValid == NR_UNKNOWN) {
+ parseNumericValue(NR_DOUBLE);
}
if ((mNumTypesValid & NR_DOUBLE) == 0) {
convertNumberToDouble();
@@ -273,8 +277,8 @@
throws IOException, JsonParseException
{
if ((mNumTypesValid & NR_BIGDECIMAL) == 0) {
- if (mNumTypesValid == 0) {
- parseNumericValue();
+ if (mNumTypesValid == NR_UNKNOWN) {
+ parseNumericValue(NR_BIGDECIMAL);
}
if ((mNumTypesValid & NR_BIGDECIMAL) == 0) {
convertNumberToBigDecimal();
@@ -295,8 +299,11 @@
* valid number value. Type it will parse into depends on whether
* it is a floating point number, as well as its magnitude: smallest
* legal type (of ones available) is used for efficiency.
+ *
+ * @param neededType Numeric type that we will immediately need, if any;
+ * mostly necessary to optimize handling of floating point numbers
*/
- protected final void parseNumericValue()
+ protected final void parseNumericValue(int expType)
throws JsonParseException
{
// First things first: must be a numeric event
@@ -323,21 +330,28 @@
mNumTypesValid = NR_LONG;
return;
}
- // nope, need the heavy guns...
+ // nope, need the heavy guns... (rare case)
BigInteger bi = new BigInteger(mTextBuffer.contentsAsString());
mNumberBigDecimal = new BigDecimal(bi);
mNumTypesValid = NR_BIGDECIMAL;
return;
}
- // Nope: floating point
-
- /* !!! TBI: Use BigDecimal if need be? And/or optimize with
- * faster parsing
+ /* Nope: floating point. Here we need to be careful to get
+ * optimal parsing strategy: choice is between accurate but
+ * slow (BigDecimal) and lossy but fast (Double). For now
+ * let's only use BD when explicitly requested -- it can
+ * still be constructed correctly at any point since we do
+ * retain textual representation
*/
- String value = mTextBuffer.contentsAsString();
- mNumberDouble = Double.parseDouble(value);
- mNumTypesValid = NR_DOUBLE;
+ if (expType == NR_BIGDECIMAL) {
+ mNumberBigDecimal = mTextBuffer.contentsAsDecimal();
+ mNumTypesValid = NR_BIGDECIMAL;
+ } else {
+ // Otherwise double has to do
+ mNumberDouble = mTextBuffer.contentsAsDouble();
+ mNumTypesValid = NR_DOUBLE;
+ }
} catch (NumberFormatException nex) {
// Can this ever occur? Due to overflow, maybe?
wrapError("Malformed numeric value '"+mTextBuffer.contentsAsString()+"'", nex);
@@ -372,7 +386,7 @@
|| BD_MAX_INT.compareTo(mNumberBigDecimal) < 0) {
reportOverflowInt();
}
- mNumberLong = mNumberBigDecimal.longValue();
+ mNumberInt = mNumberBigDecimal.intValue();
} else {
throwInternal(); // should never get here
}
@@ -407,12 +421,18 @@
protected void convertNumberToDouble()
throws IOException, JsonParseException
{
- if ((mNumTypesValid & NR_INT) != 0) {
- mNumberDouble = (double) mNumberInt;
+ /* 05-Aug-2008, tatus: Important note: this MUST start with
+ * more accurate representations, since we don't know which
+ * value is the original one (others get generated when
+ * requested)
+ */
+
+ if ((mNumTypesValid & NR_BIGDECIMAL) != 0) {
+ mNumberDouble = mNumberBigDecimal.doubleValue();
} else if ((mNumTypesValid & NR_LONG) != 0) {
mNumberDouble = (double) mNumberLong;
- } else if ((mNumTypesValid & NR_BIGDECIMAL) != 0) {
- mNumberDouble = mNumberBigDecimal.doubleValue();
+ } else if ((mNumTypesValid & NR_INT) != 0) {
+ mNumberDouble = (double) mNumberInt;
} else {
throwInternal(); // should never get here
}
@@ -423,16 +443,24 @@
protected void convertNumberToBigDecimal()
throws IOException, JsonParseException
{
- if ((mNumTypesValid & NR_INT) != 0) {
- mNumberBigDecimal = BigDecimal.valueOf((long) mNumberInt);
- } else if ((mNumTypesValid & NR_LONG) != 0) {
- mNumberBigDecimal = BigDecimal.valueOf(mNumberLong);
- } else {
- /* Otherwise, let's actually parse from String representation,
+ /* 05-Aug-2008, tatus: Important note: this MUST start with
+ * more accurate representations, since we don't know which
+ * value is the original one (others get generated when
+ * requested)
+ */
+
+ if ((mNumTypesValid & NR_DOUBLE) != 0) {
+ /* Let's actually parse from String representation,
* to avoid rounding errors that non-decimal floating operations
* would incur
*/
mNumberBigDecimal = new BigDecimal(getText());
+ } else if ((mNumTypesValid & NR_LONG) != 0) {
+ mNumberBigDecimal = BigDecimal.valueOf(mNumberLong);
+ } else if ((mNumTypesValid & NR_INT) != 0) {
+ mNumberBigDecimal = BigDecimal.valueOf((long) mNumberInt);
+ } else {
+ throwInternal(); // should never get here
}
mNumTypesValid |= NR_BIGDECIMAL;
}
diff --git a/src/java/org/codehaus/jackson/impl/ReaderBasedParser.java b/src/java/org/codehaus/jackson/impl/ReaderBasedParser.java
index cf7ff02..3a13990 100644
--- a/src/java/org/codehaus/jackson/impl/ReaderBasedParser.java
+++ b/src/java/org/codehaus/jackson/impl/ReaderBasedParser.java
@@ -200,6 +200,20 @@
/*
////////////////////////////////////////////////////
+ // Public API, binary access
+ ////////////////////////////////////////////////////
+ */
+
+ @Override
+ public int readBinaryValue(OutputStream results)
+ throws IOException, JsonParseException
+ {
+ // !!! TBI: implemented base64 decoding
+ return -1;
+ }
+
+ /*
+ ////////////////////////////////////////////////////
// Internal methods, secondary parsing
////////////////////////////////////////////////////
*/
diff --git a/src/java/org/codehaus/jackson/impl/Utf8StreamParser.java b/src/java/org/codehaus/jackson/impl/Utf8StreamParser.java
index 033a23f..1da5ec9 100644
--- a/src/java/org/codehaus/jackson/impl/Utf8StreamParser.java
+++ b/src/java/org/codehaus/jackson/impl/Utf8StreamParser.java
@@ -210,6 +210,20 @@
/*
////////////////////////////////////////////////////
+ // Public API, binary access
+ ////////////////////////////////////////////////////
+ */
+
+ @Override
+ public int readBinaryValue(OutputStream results)
+ throws IOException, JsonParseException
+ {
+ // !!! TBI: implemented base64 decoding
+ return -1;
+ }
+
+ /*
+ ////////////////////////////////////////////////////
// Internal methods, secondary parsing
////////////////////////////////////////////////////
*/
diff --git a/src/java/org/codehaus/jackson/impl/WriterBasedGenerator.java b/src/java/org/codehaus/jackson/impl/WriterBasedGenerator.java
index 9441160..cc61f21 100644
--- a/src/java/org/codehaus/jackson/impl/WriterBasedGenerator.java
+++ b/src/java/org/codehaus/jackson/impl/WriterBasedGenerator.java
@@ -238,12 +238,13 @@
mOutputBuffer[mOutputTail++] = c;
}
- public void writeBinary(byte[] data, int offset, int len)
+ public void writeBinary(byte[] data, int offset, int len,
+ boolean includeLFs)
throws IOException, JsonGenerationException
{
// !!! TBI: base64-based binary output
- throw new RuntimeException("Not yet implemented");
+ throw new UnsupportedOperationException("Not yet implemented");
}
private void writeRawLong(String text)
@@ -300,13 +301,14 @@
mOutputTail = NumberOutput.outputLong(l, mOutputBuffer, mOutputTail);
}
+ /* !!! 05-Aug-2008, tatus: Any ways to optimize these?
+ */
+
public void writeNumber(double d)
throws IOException, JsonGenerationException
{
// What is the max length for doubles? 40 chars?
verifyValueWrite("write number");
-
- // !!! TODO: use a more efficient printing method?
writeRaw(String.valueOf(d));
}
@@ -315,8 +317,6 @@
{
// What is the max length for floats?
verifyValueWrite("write number");
-
- // !!! TODO: use a more efficient printing method?
writeRaw(String.valueOf(f));
}
@@ -325,11 +325,12 @@
{
// Don't really know max length for big decimal, no point checking
verifyValueWrite("write number");
-
- // !!! TODO: use a more efficient printing method?
writeRaw(dec.toString());
}
+ /**
+ *
+ */
public void writeNumber(String encodedValue)
throws IOException, JsonGenerationException
{
diff --git a/src/java/org/codehaus/jackson/io/NumberOutput.java b/src/java/org/codehaus/jackson/io/NumberOutput.java
index 9f89c5e..e70cfcc 100644
--- a/src/java/org/codehaus/jackson/io/NumberOutput.java
+++ b/src/java/org/codehaus/jackson/io/NumberOutput.java
@@ -49,6 +49,12 @@
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
};
+ /*
+ ////////////////////////////////////////////////////
+ // Efficient serialization methods using raw buffers
+ ////////////////////////////////////////////////////
+ */
+
/**
* @return Offset within buffer after outputting int
*/
@@ -170,6 +176,16 @@
return offset;
}
+ /*
+ ////////////////////////////////////////////////////
+ // Secondary convenience serialization methods
+ ////////////////////////////////////////////////////
+ */
+
+ /* !!! 05-Aug-2008, tatus: Any ways to further optimize
+ * these? (or need: only called by diagnostics methods?)
+ */
+
public static String toString(int value)
{
// Lookup table for small values
@@ -182,7 +198,6 @@
return sSmallIntStrs2[v2];
}
}
- // !!! TODO: further optimize?
return Integer.toString(value);
}
@@ -192,13 +207,11 @@
value >= Integer.MIN_VALUE) {
return toString((int) value);
}
- // !!! TODO: further optimize?
return Long.toString(value);
}
public static String toString(double value)
{
- // !!! TODO: optimize?
return Double.toString(value);
}
diff --git a/src/java/org/codehaus/jackson/map/BaseMapper.java b/src/java/org/codehaus/jackson/map/BaseMapper.java
index 1aa58ef..1cacc5f 100644
--- a/src/java/org/codehaus/jackson/map/BaseMapper.java
+++ b/src/java/org/codehaus/jackson/map/BaseMapper.java
@@ -37,6 +37,20 @@
*/
protected DupFields mCfgDupFields = DupFields.ERROR;
+ /**
+ * Defines whether (escaped) linefeeds are included when serializing
+ * binary data into base64 values or not.
+ *<p>
+ * Default setting is <b>false</b> mostly because linefeeds can not
+ * be included natively anyway, and instead encoded/escaped entries
+ * have to be used. Additionally it is unlikely that recipient would
+ * not be able to decode data (since it needs to be json aware and
+ * do fair bit of handling before being able to access data).
+ * Nonetheless, for maximum interoperability it may be desireable
+ * to enable this setting.
+ */
+ protected boolean mCfgBase64LFs = false;
+
/*
////////////////////////////////////////////////////
// Life-cycle (construction, configuration)
@@ -48,6 +62,10 @@
public void setDupFieldHandling(DupFields mode) { mCfgDupFields = mode; }
public DupFields getDupFieldHandling() { return mCfgDupFields; }
+ public void setAddLinefeedsToBase64(boolean state) { mCfgBase64LFs = state; }
+
+ public boolean getAddLinefeedsToBase64() { return mCfgBase64LFs; }
+
/*
////////////////////////////////////////////////////
// Methods for sub-classes
diff --git a/src/java/org/codehaus/jackson/map/JavaTypeMapper.java b/src/java/org/codehaus/jackson/map/JavaTypeMapper.java
index 22bffd5..372f5e0 100644
--- a/src/java/org/codehaus/jackson/map/JavaTypeMapper.java
+++ b/src/java/org/codehaus/jackson/map/JavaTypeMapper.java
@@ -191,7 +191,7 @@
public JsonParser createParserFor(Object data)
throws JsonParseException
{
- // !!! TBI
+ // !!! TBI: parser for reading from Object (array/map, primitives)
return null;
}
@@ -200,10 +200,17 @@
* Java objects as members of the current list, appending
* them at the end of the list.
*/
- public JsonGenerator createParserFor(List<Object> context)
+ public JsonGenerator createGeneratorFor(List<Object> context)
throws JsonParseException
{
- // !!! TBI
+ // !!! TBI: generator for writing (appending) to Json Arrays (Java lists)
+ return null;
+ }
+
+ public JsonGenerator createGeneratorFor(Map<Object,Object> context)
+ throws JsonParseException
+ {
+ // !!! TBI: generator for writing (appending) to Json Objects (Java maps)
return null;
}
@@ -328,7 +335,7 @@
*/
{
byte[] data = (byte[]) value;
- jgen.writeBinary(data, 0, data.length);
+ jgen.writeBinary(data, 0, data.length, mCfgBase64LFs);
}
break;
diff --git a/src/java/org/codehaus/jackson/map/JsonTypeMapper.java b/src/java/org/codehaus/jackson/map/JsonTypeMapper.java
index bc14330..b633f3e 100644
--- a/src/java/org/codehaus/jackson/map/JsonTypeMapper.java
+++ b/src/java/org/codehaus/jackson/map/JsonTypeMapper.java
@@ -71,6 +71,38 @@
/*
////////////////////////////////////////////////////
+ // Public API, exposing JsonNode(s) via stream
+ // parsers/generators
+ ////////////////////////////////////////////////////
+ */
+
+ /**
+ * Method that will take in a Java object that could have
+ * been created by mappers write methods, and construct
+ * a {@link JsonParser} that exposes contents as JSON
+ * tokens
+ */
+ public JsonParser createParserFor(JsonNode node)
+ throws JsonParseException
+ {
+ // !!! TBI: parser for reading from JsonNode (array/map, primitives)
+ return null;
+ }
+
+ /**
+ * Method that will create a JSON generator that will build
+ * Java objects as members of the current list, appending
+ * them at the end of the list.
+ */
+ public JsonGenerator createGeneratorFor(JsonNode context)
+ throws JsonParseException
+ {
+ // !!! TBI: generator for writing (appending) to Objects (array/map, primitives)
+ return null;
+ }
+
+ /*
+ ////////////////////////////////////////////////////
// Factory methods
////////////////////////////////////////////////////
*/
diff --git a/src/java/org/codehaus/jackson/map/impl/JsonTypeMapperBase.java b/src/java/org/codehaus/jackson/map/impl/JsonTypeMapperBase.java
index 18b6356..605f76c 100644
--- a/src/java/org/codehaus/jackson/map/impl/JsonTypeMapperBase.java
+++ b/src/java/org/codehaus/jackson/map/impl/JsonTypeMapperBase.java
@@ -96,9 +96,9 @@
return numberNode(jp.getLongValue());
case VALUE_NUMBER_FLOAT:
- /* !!! Should we try to see if we should use some
- * other representation (BigDecimal)?
- */
+ if (jp.getNumberType() == JsonParser.NumberType.BIG_DECIMAL) {
+ return numberNode(jp.getDecimalValue());
+ }
return numberNode(jp.getDoubleValue());
case VALUE_TRUE:
diff --git a/src/java/org/codehaus/jackson/sym/NameCanonicalizer.java b/src/java/org/codehaus/jackson/sym/NameCanonicalizer.java
index 432ae31..1929cde 100644
--- a/src/java/org/codehaus/jackson/sym/NameCanonicalizer.java
+++ b/src/java/org/codehaus/jackson/sym/NameCanonicalizer.java
@@ -597,6 +597,7 @@
if (mCollListShared) {
unshareCollision(); // also allocates if list was null
}
+
++mCollCount;
int entryValue = mMainHash[ix];
int bucket = entryValue & 0xFF;
@@ -645,7 +646,6 @@
mNeedRehash = false;
// Note: since we'll make copies, no need to unshare, can just mark as such:
mMainNamesShared = false;
- mCollListShared = false;
/* And then we can first deal with the main hash area. Since we
* are expanding linearly (double up), we know there'll be no
@@ -680,6 +680,7 @@
mCollCount = 0;
mCollEnd = 0;
+ mCollListShared = false;
Bucket[] oldBuckets = mCollList;
mCollList = new Bucket[oldBuckets.length];
diff --git a/src/java/org/codehaus/jackson/util/TextBuffer.java b/src/java/org/codehaus/jackson/util/TextBuffer.java
index 7ad1c83..c36da9b 100644
--- a/src/java/org/codehaus/jackson/util/TextBuffer.java
+++ b/src/java/org/codehaus/jackson/util/TextBuffer.java
@@ -1,6 +1,7 @@
package org.codehaus.jackson.util;
import java.io.*;
+import java.math.BigDecimal;
import java.util.ArrayList;
/**
@@ -315,6 +316,7 @@
public int contentsToArray(int srcStart, char[] dst, int dstStart, int len) {
// Easy to copy from shared buffer:
+
if (mInputStart >= 0) {
int amount = mInputLen - srcStart;
@@ -414,6 +416,39 @@
return rlen;
}
+ /**
+ * Convenience method for converting contents of the buffer
+ * into a {@link BigDecimal}.
+ */
+ public BigDecimal contentsAsDecimal()
+ throws NumberFormatException
+ {
+ // Already got a pre-cut array?
+ if (mResultArray != null) {
+ return new BigDecimal(mResultArray);
+ }
+ // Or a shared buffer?
+ if (mInputStart >= 0) {
+ return new BigDecimal(mInputBuffer, mInputStart, mInputLen);
+ }
+ // Or if not, just a single buffer (the usual case)
+ if (mSegmentSize == 0) {
+ return new BigDecimal(mCurrentSegment, 0, mCurrentSize);
+ }
+ // If not, let's just get it aggregated...
+ return new BigDecimal(contentsAsArray());
+ }
+
+ /**
+ * Convenience method for converting contents of the buffer
+ * into a Double value.
+ */
+ public double contentsAsDouble()
+ throws NumberFormatException
+ {
+ return Double.parseDouble(contentsAsString());
+ }
+
/*
//////////////////////////////////////////////
// Public mutators:
diff --git a/src/perf/TestJsonPerf.java b/src/perf/TestJsonPerf.java
index 7afb854..b28aeda 100644
--- a/src/perf/TestJsonPerf.java
+++ b/src/perf/TestJsonPerf.java
@@ -204,7 +204,8 @@
{
int sum = 0;
for (int i = 0; i < reps; ++i) {
- JsonParser jp = mJsonFactory.createJsonParser(new ByteArrayInputStream(mData), fast);
+ // note: fast is not used any more
+ JsonParser jp = mJsonFactory.createJsonParser(new ByteArrayInputStream(mData));
JsonToken t;
while ((t = jp.nextToken()) != null) {
// Field names are always constructed
diff --git a/src/perf/TestReadPerf.java b/src/perf/TestReadPerf.java
index bc4c46b..d28df3c 100644
--- a/src/perf/TestReadPerf.java
+++ b/src/perf/TestReadPerf.java
@@ -52,8 +52,12 @@
JsonParser jp = null;
while (--reps >= 0) {
jp = mJsonFactory.createJsonParser(new ByteArrayInputStream(mData));
- while (jp.nextToken() != null) {
- ;
+ JsonToken t;
+
+ while ((t = jp.nextToken()) != null) {
+ if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+ jp.getDoubleValue();
+ }
}
jp.close();
}
diff --git a/src/test/impl/TestByteBasedSymbols.java b/src/test/impl/TestByteBasedSymbols.java
new file mode 100644
index 0000000..450c094
--- /dev/null
+++ b/src/test/impl/TestByteBasedSymbols.java
@@ -0,0 +1,112 @@
+package impl;
+
+import java.io.*;
+
+import org.codehaus.jackson.*;
+
+import main.BaseTest;
+
+/**
+ * Unit test(s) to verify that handling of byte-based symbol tables
+ * is working. Created to verify fix to [JACKSON-5]
+ */
+public class TestByteBasedSymbols
+ extends BaseTest
+{
+ final static String[] FIELD_NAMES = new String[] {
+ "a", "b", "c", "x", "y", "b13", "abcdefg", "a123",
+ "a0", "b0", "c0", "d0", "e0", "f0", "g0", "h0",
+ "x2", "aa", "ba", "ab", "b31", "___x", "aX", "xxx",
+ "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
+ "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
+ "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
+ };
+
+ /**
+ * This unit test checks that [JACKSON-5] is fixed; if not, a
+ * symbol table corruption should result in odd problems.
+ */
+ public void testSharedSymbols()
+ throws Exception
+ {
+ // MUST share a single json factory
+ JsonFactory jf = new JsonFactory();
+
+ /* First things first: parse a dummy doc to populate
+ * shared symbol table with some stuff
+ */
+ String DOC0 = "{ \"a\" : 1, \"x\" : [ ] }";
+ JsonParser jp0 = createParser(jf, DOC0);
+
+ /* Important: don't close, don't traverse past end.
+ * This is needed to create partial still-in-use symbol
+ * table...
+ */
+ while (jp0.nextToken() != JsonToken.START_ARRAY) { }
+
+ String doc1 = createDoc(FIELD_NAMES, true);
+ String doc2 = createDoc(FIELD_NAMES, false);
+
+ // Let's run it twice... shouldn't matter
+ for (int x = 0; x < 2; ++x) {
+ JsonParser jp1 = createParser(jf, doc1);
+ JsonParser jp2 = createParser(jf, doc2);
+
+ assertToken(JsonToken.START_OBJECT, jp1.nextToken());
+ assertToken(JsonToken.START_OBJECT, jp2.nextToken());
+
+ int len = FIELD_NAMES.length;
+ for (int i = 0; i < len; ++i) {
+ assertToken(JsonToken.FIELD_NAME, jp1.nextToken());
+ assertToken(JsonToken.FIELD_NAME, jp2.nextToken());
+ assertEquals(FIELD_NAMES[i], jp1.getCurrentName());
+ assertEquals(FIELD_NAMES[len-(i+1)], jp2.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp1.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp2.nextToken());
+ assertEquals(i, jp1.getIntValue());
+ assertEquals(i, jp2.getIntValue());
+ }
+
+ assertToken(JsonToken.END_OBJECT, jp1.nextToken());
+ assertToken(JsonToken.END_OBJECT, jp2.nextToken());
+
+ jp1.close();
+ jp2.close();
+ }
+ }
+
+ /*
+ ////////////////////////////////////////////
+ // Helper methods
+ ////////////////////////////////////////////
+ */
+
+ protected JsonParser createParser(JsonFactory jf, String input)
+ throws IOException, JsonParseException
+ {
+ byte[] data = input.getBytes("UTF-8");
+ InputStream is = new ByteArrayInputStream(data);
+ return jf.createJsonParser(is);
+ }
+
+ private String createDoc(String[] fieldNames, boolean add)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{ ");
+
+ int len = fieldNames.length;
+ for (int i = 0; i < len; ++i) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append('"');
+ sb.append(add ? fieldNames[i] : fieldNames[len - (i+1)]);
+ sb.append("\" : ");
+ sb.append(i);
+ }
+ sb.append(" }");
+ return sb.toString();
+ }
+}
+
+