/*
 * Copyright (C) 2016 University of Washington
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.opendatakit.database.queries;

import android.os.Parcel;
import android.os.Parcelable;

import java.io.Serializable;

/**
 * This holds the list of objects that are bind arguments to the SQLite database.
 * The objects are primitive objects (String, Integer, Long, Float, Double, Boolean)
 */
@SuppressWarnings("serial")
public class BindArgs implements Parcelable, Serializable {

  public final Object[] bindArgs;

  public BindArgs() {
    bindArgs = null;
  }

  public BindArgs(Object[] args) {
    bindArgs = args;
    if ( args != null ) {
      for (int i = 0 ; i < args.length ; ++i ) {
        Object o = args[i];
        if ( o == null ) continue;
        if ( o instanceof String ) continue;
        if ( o instanceof Integer ) continue;
        if ( o instanceof Boolean ) continue;
        if ( o instanceof Double ) continue;
        if ( o instanceof Float ) continue;
        if ( o instanceof Long ) continue;
        throw new IllegalArgumentException("bind args must be a primitive type");
      }
    }
  }

  private static void marshallObject(Parcel out, Object toMarshall) {
    if (toMarshall == null) {
      out.writeInt(0);
    } else if ( toMarshall instanceof String ) {
      out.writeInt(1);
      out.writeString((String) toMarshall);
    } else if ( toMarshall instanceof Integer ) {
      out.writeInt(2);
      out.writeInt((Integer) toMarshall);
    } else if ( toMarshall instanceof Boolean ) {
      if ( (Boolean) toMarshall ) {
        out.writeInt(3);
      } else {
        out.writeInt(4);
      }
    } else if ( toMarshall instanceof Double ) {
      out.writeInt(5);
      out.writeDouble((Double) toMarshall);
    } else if ( toMarshall instanceof Float ) {
      out.writeInt(6);
      out.writeFloat((Float) toMarshall);
    } else if ( toMarshall instanceof Long ) {
      out.writeInt(7);
      out.writeLong((Long) toMarshall);
    } else {
      throw new IllegalStateException("should have been prevented in constructor");
    }
  }

  private static Object unmarshallObject(Parcel in) {
    int dataType = in.readInt();
    switch(dataType) {
    case 0:
      return null;
    case 1:
      return in.readString();
    case 2:
      return in.readInt();
    case 3:
      return Boolean.TRUE;
    case 4:
      return Boolean.FALSE;
    case 5:
      return in.readDouble();
    case 6:
      return in.readFloat();
    case 7:
      return in.readLong();
    default:
      throw new IllegalStateException("should have been prevented in constructor");
    }
  }

  protected BindArgs(Parcel in) {
    int dataCount = in.readInt();
    if (dataCount < 0) {
      bindArgs = null;
    } else {
      Object[] result = new Object[dataCount];
      for ( int i = 0 ; i < dataCount ; ++i ) {
        result[i] = unmarshallObject(in);
      }
      bindArgs = result;
    }
  }

  @Override public void writeToParcel(Parcel dest, int flags) {
    if (bindArgs == null) {
      dest.writeInt(-1);
    } else {
      dest.writeInt(bindArgs.length);
      for ( int i = 0 ; i < bindArgs.length ; ++i ) {
        marshallObject(dest, bindArgs[i]);
      }
    }
  }

  @Override public int describeContents() {
    return 0;
  }

  public static final Creator<BindArgs> CREATOR = new Creator<BindArgs>() {
    @Override public BindArgs createFromParcel(Parcel in) {
      return new BindArgs(in);
    }

    @Override public BindArgs[] newArray(int size) {
      return new BindArgs[size];
    }
  };
}

@@ -701,16 +701,204 @@
 elable;%0A
+import com.fasterxml.jackson.core.JsonProcessingException;%0Aimport com.fasterxml.jackson.core.type.TypeReference;%0Aimport org.opendatakit.utilities.ODKFileUtils;%0A%0Aimport java.io.IOException;
 %0Aimport 
@@ -918,16 +918,44 @@
 lizable;
+%0Aimport java.util.ArrayList;
 %0A%0A/**%0A *
@@ -1851,32 +1851,771 @@
    %7D%0A    %7D%0A  %7D%0A%0A
+  public BindArgs(String fromJSON) %7B%0A    if ( fromJSON == null ) %7B%0A      bindArgs = new Object%5B0%5D;%0A      return;%0A    %7D%0A%0A    TypeReference%3CArrayList%3CObject%3E%3E type = new TypeReference%3CArrayList%3CObject%3E%3E() %7B%7D;%0A    try %7B%0A      ArrayList%3CObject%3E values = ODKFileUtils.mapper.readValue(fromJSON, type);%0A      bindArgs = values.toArray(new Object%5Bvalues.size()%5D);%0A    %7D catch (IOException e) %7B%0A      // not expected%0A      throw new IllegalStateException(%22Unable to deserialize bindArgs%22);%0A    %7D%0A  %7D%0A%0A  public String asJSON() %7B%0A    try %7B%0A      return ODKFileUtils.mapper.writeValueAsString(bindArgs);%0A    %7D catch (JsonProcessingException e) %7B%0A      // not expected%0A      throw new IllegalStateException(%22Unable to serialize bindArgs%22);%0A    %7D%0A  %7D%0A%0A
   private static

Add JSON serialization capability to BindArgs. Used in Javascript and Intent APIs to preserve integer, numeric and boolean types for bind parameters.