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

package foam.lib.json;

import foam.lib.parse.*;
import java.util.Date;

/**
 * Create a second date parser specifically for Properties of type Object that
 * get set to a Date. We use a "long form" structure like so:
 *
 *   {
 *     class: '__Timestamp__',
 *     value: 1533135788921
 *   }
 *
 * Instead of simply sending the timestamp:
 *
 *   1533135788921
 *
 * or date string:
 *
 *   "2018-08-01T15:04:31.467Z"
 *
 * which would be much more efficient. The reason we can't do that here is
 * because if you're the server trying to parse the value of an Object property,
 * you don't know if a long is a timestamp or just a long, or if a string is a
 * date string or just a string. It's ambiguous. Therefore we have to sacrifice
 * performance to avoid ambiguity.
 *
 * The reason we can use the more efficient formats in other places is that the
 * server knows that those properties are of type Date and therefore it's not
 * ambiguous.
 *
 * Now that we've proven the need for an unambiguous date parser, why wouldn't
 * we just replace the original date parser with this one? Why have two? We do
 * so in the name of performance. The original date parser uses less space,
 * meaning less bandwidth is used and less time is spent serializing and
 * deserializing.
 */
public class ObjectDateParser
  extends ProxyParser
{

  public ObjectDateParser() {
    super(new Seq0(
      Whitespace.instance(),
      Literal.create("{"),
      Whitespace.instance(),
      new KeyParser("class"),
      Whitespace.instance(),
      Literal.create(":"),
      Whitespace.instance(),
      Literal.create("\"__Timestamp__\""),
      Whitespace.instance(),
      Literal.create(","),
      Whitespace.instance(),
      new KeyParser("value"),
      Whitespace.instance(),
      Literal.create(":"),
      Whitespace.instance(),
      new Parser() {
       private Parser delegate = new LongParser();
       public PStream parse(PStream ps, ParserContext x) {
         ps = ps.apply(delegate, x);
         if ( ps != null ) x.set("value", ps.value());
         return ps;
       }
      },
      Whitespace.instance(),
      Literal.create("}")));
  }

  public PStream parse(PStream ps, ParserContext x) {
    try {
      if ((ps = super.parse(ps, x)) == null) return null;
      long timestamp = (long) x.get("value");
      return ps.setValue(new Date(timestamp));
    } catch ( Throwable t ) {
      return null;
    }
  }
}

@@ -1451,25 +1451,151 @@
 arser%0A%7B%0A
-%0A  public
+  private final static Parser instance__ = new ObjectDateParser();%0A%0A  public static Parser instance() %7B return instance__; %7D%0A%0A  private
  ObjectD
@@ -1629,10 +1629,13 @@
  Seq
-0(
+1(15,
 %0A   
@@ -2084,595 +2084,90 @@
     
-new Parser() %7B%0A       private Parser delegate = new LongParser();%0A       public PStream parse(PStream ps, ParserContext x) %7B%0A         ps = ps.apply(delegate, x);%0A         if ( ps != null ) x.set(%22value%22, ps.value());%0A         return ps;%0A       %7D%0A      %7D,%0A      Whitespace.instance(),%0A      Literal.create(%22%7D%22)));%0A  %7D%0A%0A  public PStream parse(PStream ps, ParserContext x) %7B%0A    try %7B%0A      if ((ps = super.parse(ps, x)) == null) return null;%0A      long timestamp = (long) x.get(%22value%22);%0A      return ps.setValue(new Date(timestamp));%0A    %7D catch ( Throwable t ) %7B%0A      return null;%0A    %7D
+  DateParser.instance(),%0A      Whitespace.instance(),%0A      Literal.create(%22%7D%22)));
 %0A  %7D

Make ObjectDateParser a Singleton. Add ability to parse long-form Dates also.