/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package foam.lib.json;

import foam.lib.parse.*;

public class StringParser implements Parser {
  public final static char ESCAPE = '\\';

  public StringParser() {
  }

  public PStream parse(PStream ps, ParserContext x) {
    char delim = ps.head();

    if ( delim != '"' && delim != '\'' ) return null;

    ps = ps.tail();
    char lastc = delim;

    // TODO: use thread-local SB instead to avoid generating garbage
    StringBuilder sb = new StringBuilder();

    while ( ps.valid() ) {
      char c = ps.head();

      if ( c == delim && lastc != ESCAPE ) break;

      PStream tail = ps.tail();

      if ( c == ESCAPE ) {
        char   nextChar        = ps.tail().head();
        Parser escapeSeqParser = null;

        if ( nextChar == 'u' ) {
          // TODO: make a constant
          escapeSeqParser = new UnicodeParser();
        } else if ( nextChar == 'n' ) {
          // TODO: make a constant
          escapeSeqParser = new ASCIIEscapeParser();
        }

        if ( escapeSeqParser != null ) {
          PStream escapePS = ps.apply(escapeSeqParser, x);
          if ( escapePS != null ) {
            sb.append(escapePS.value());
            tail = escapePS;

            c = ((Character) escapePS.value()).charValue();
          }
        }
      } else {
        sb.append(c);
      }

      ps = tail;
      lastc = c;
    }

    return ps.tail().setValue(sb.toString());
  }
}

@@ -167,16 +167,53 @@
 rse.*;%0A%0A
+import java.util.function.Supplier;%0A%0A
 public c
@@ -229,16 +229,18 @@
 ngParser
+%0A 
  impleme
@@ -253,19 +253,22 @@
 rser
- 
+%0A
 %7B%0A  p
-ublic
+rotected
  fin
@@ -296,16 +296,565 @@
  = '%5C%5C';
+%0A  protected final static ThreadLocal%3CParser%3E unicodeParser = ThreadLocal.withInitial(UnicodeParser::new);%0A  protected final static ThreadLocal%3CParser%3E asciiEscapeParser = ThreadLocal.withInitial(ASCIIEscapeParser::new);cd %0A  protected final static ThreadLocal%3CStringBuilder%3E sb = new ThreadLocal%3CStringBuilder%3E() %7B%0A%0A    @Override%0A    protected StringBuilder initialValue() %7B%0A      return new StringBuilder();%0A    %7D%0A%0A    @Override%0A    public StringBuilder get() %7B%0A      StringBuilder b = super.get();%0A      b.setLength(0);%0A      return b;%0A    %7D%0A  %7D;
 %0A%0A  publ
@@ -1069,113 +1069,38 @@
     
-// TODO: use thread-local SB instead to avoid generating garbage%0A    StringBuilder sb = new StringBuilder
+StringBuilder builder = sb.get
 ();%0A
@@ -1354,150 +1354,56 @@
  = n
-ull;%0A%0A        if ( nextChar == 'u' ) %7B%0A          // TODO: make a constant%0A          escapeSeqParser = new UnicodeParser();%0A        %7D else if (
+extChar == 'u' ?%0A          unicodeParser.get() :
  nex
@@ -1415,19 +1415,17 @@
  == 'n' 
-) %7B
+?
 %0A       
@@ -1431,95 +1431,39 @@
     
-// TODO: make a constant%0A          escapeSeqParser = new ASCIIEscapeParser();%0A        %7D
+asciiEscapeParser.get() : null;
 %0A%0A  
@@ -1600,34 +1600,39 @@
 ) %7B%0A            
-s
 b
+uilder
 .append(escapePS
@@ -1688,17 +1688,16 @@
     c = 
-(
 (Charact
@@ -1720,21 +1720,8 @@
 ue()
-).charValue()
 ;%0A  
@@ -1763,18 +1763,23 @@
         
-s
 b
+uilder
 .append(
@@ -1862,18 +1862,23 @@
 etValue(
-s
 b
+uilder
 .toStrin

Address TODO comments in StringParser