Support reading/writing values in JSON strings without java.lang.String

Signed-off-by: Lukas Jungmann <lukas.jungmann@oracle.com>
diff --git a/impl/src/main/java/org/eclipse/jsonp/JsonGeneratorImpl.java b/impl/src/main/java/org/eclipse/jsonp/JsonGeneratorImpl.java
index 964d9dd..dd64d2b 100644
--- a/impl/src/main/java/org/eclipse/jsonp/JsonGeneratorImpl.java
+++ b/impl/src/main/java/org/eclipse/jsonp/JsonGeneratorImpl.java
@@ -152,12 +152,7 @@
 
     @Override
     public JsonGenerator write(String name, String fieldValue) {
-        if (currentContext.scope != Scope.IN_OBJECT) {
-            throw new JsonGenerationException(
-                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
-        }
-        writeName(name);
-        writeEscapedString(fieldValue);
+        write(name, (CharSequence) fieldValue);
         return this;
     }
 
@@ -338,7 +333,7 @@
                 break;
             case STRING:
                 JsonString str = (JsonString)value;
-                write(name, str.getString());
+                write(name, str.getChars());
                 break;
             case NUMBER:
                 JsonNumber number = (JsonNumber)value;
@@ -480,6 +475,15 @@
         return this;
     }
 
+    void write(String name, CharSequence fieldValue) {
+      if (currentContext.scope != Scope.IN_OBJECT) {
+          throw new JsonGenerationException(
+                  JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+      }
+      writeName(name);
+      writeEscapedString(fieldValue);
+    }
+
     protected void writeComma() {
         if (isCommaAllowed()) {
             writeChar(',');
@@ -530,7 +534,7 @@
     //    ^           ^                     ^             ^
     //    |           |                     |             |
     //   begin       end                   begin         end
-    void writeEscapedString(String string) {
+    void writeEscapedString(CharSequence string) {
         writeChar('"');
         int len = string.length();
         for(int i = 0; i < len; i++) {
@@ -582,10 +586,15 @@
         writeChar('"');
     }
 
-    void writeString(String str, int begin, int end) {
+    void writeString(CharSequence str, int begin, int end) {
         while (begin < end) {       // source begin and end indexes
             int no = Math.min(buf.length - len, end - begin);
-            str.getChars(begin, begin + no, buf, len);
+            if (str instanceof String) {
+              ((String)str).getChars(begin, begin + no, buf, len);
+            } else {
+              // if passed a non-string, assume this is deliberate
+              getChars(str, begin, begin + no, buf, len);
+            }
             begin += no;            // Increment source index
             len += no;              // Increment dest index
             if (len >= buf.length) {
@@ -594,7 +603,7 @@
         }
     }
 
-    void writeString(String str) {
+    void writeString(CharSequence str) {
         writeString(str, 0, str.length());
     }
 
@@ -669,6 +678,15 @@
                 return i+1;
     }
 
+    void getChars(CharSequence str, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
+      int length = srcEnd - srcBegin;
+      for (int i = 0 ; i < length ; i++) {
+        int srcIdx = srcBegin + i;
+        int dstIdx = dstBegin + i;
+        dst[dstIdx] = str.charAt(srcIdx);
+      }
+    }
+
     /**
      * Places characters representing the integer i into the
      * character array buf. The characters are placed into
diff --git a/impl/src/main/java/org/eclipse/jsonp/JsonParserImpl.java b/impl/src/main/java/org/eclipse/jsonp/JsonParserImpl.java
index 43ec874..a6fe3d5 100644
--- a/impl/src/main/java/org/eclipse/jsonp/JsonParserImpl.java
+++ b/impl/src/main/java/org/eclipse/jsonp/JsonParserImpl.java
@@ -60,7 +60,7 @@
 
     private final Stack stack = new Stack();
     private final JsonTokenizer tokenizer;
-    
+
     public JsonParserImpl(Reader reader, BufferPool bufferPool) {
         this(reader, bufferPool, false);
     }
@@ -173,7 +173,7 @@
                 return getObject(new JsonObjectBuilderImpl(bufferPool));
             case KEY_NAME:
             case VALUE_STRING:
-                return new JsonStringImpl(getString());
+                return new JsonStringImpl(getCharSequence());
             case VALUE_NUMBER:
                 if (isDefinitelyInt()) {
                     return JsonNumberImpl.getJsonNumber(getInt());
@@ -321,6 +321,14 @@
         throw parsingException(JsonToken.EOF, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL, SQUARECLOSE]");
     }
 
+    private CharSequence getCharSequence() {
+      if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING
+              || currentEvent == Event.VALUE_NUMBER) {
+          return tokenizer.getCharSequence();
+      }
+      throw new IllegalStateException(JsonMessages.PARSER_GETSTRING_ERR(currentEvent));
+  }
+
     private JsonObject getObject(JsonObjectBuilder builder) {
         while(hasNext()) {
             JsonParser.Event e = next();
diff --git a/impl/src/main/java/org/eclipse/jsonp/JsonStringImpl.java b/impl/src/main/java/org/eclipse/jsonp/JsonStringImpl.java
index c1181b0..d75ae86 100644
--- a/impl/src/main/java/org/eclipse/jsonp/JsonStringImpl.java
+++ b/impl/src/main/java/org/eclipse/jsonp/JsonStringImpl.java
@@ -25,15 +25,15 @@
  */
 final class JsonStringImpl implements JsonString {
 
-    private final String value;
+    private final CharSequence value;
 
-    JsonStringImpl(String value) {
+    JsonStringImpl(CharSequence value) {
         this.value = value;
     }
 
     @Override
     public String getString() {
-        return value;
+        return value.toString();
     }
 
     @Override
diff --git a/impl/src/main/java/org/eclipse/jsonp/JsonTokenizer.java b/impl/src/main/java/org/eclipse/jsonp/JsonTokenizer.java
index 4f70d13..c1b980d 100644
--- a/impl/src/main/java/org/eclipse/jsonp/JsonTokenizer.java
+++ b/impl/src/main/java/org/eclipse/jsonp/JsonTokenizer.java
@@ -22,7 +22,9 @@
 import jakarta.json.stream.JsonLocation;
 import jakarta.json.stream.JsonParser;
 import jakarta.json.stream.JsonParsingException;
-import java.io.*;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
 import java.math.BigDecimal;
 import java.util.Arrays;
 
@@ -510,6 +512,11 @@
         return new String(buf, storeBegin, storeEnd-storeBegin);
     }
 
+    CharSequence getCharSequence() {
+      int len = storeEnd - storeBegin;
+      return new StringBuilder(len).append(buf, storeBegin, len);
+    }
+
     BigDecimal getBigDecimal() {
         if (bd == null) {
             bd = new BigDecimal(buf, storeBegin, storeEnd-storeBegin);
@@ -531,7 +538,7 @@
             return getBigDecimal().intValue();
         }
     }
-    
+
     long getLong() {
         // no need to create BigDecimal for common integer values (1-18 digits)
         int storeLen = storeEnd-storeBegin;
@@ -553,7 +560,7 @@
         int storeLen = storeEnd-storeBegin;
         return !fracOrExp && (storeLen <= 9 || (minus && storeLen <= 10));
     }
-    
+
     // returns true for common long values (1-18 digits).
     // So there are cases it will return false even though the number is long
     boolean isDefinitelyLong() {
@@ -582,5 +589,5 @@
         return new JsonParsingException(
                 JsonMessages.TOKENIZER_EXPECTED_CHAR(unexpected, location, expected), location);
     }
-    
+
 }