gt:                 JSONArray jsonArray = new JSONArray();
context: /*
   * Copyright (C) 2013 The Android Open Source Project
   *
   * 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 com.android.cts.verifier.camera.its;
  
  import android.graphics.Point;
  import android.graphics.Rect;
  import android.hardware.camera2.CameraCharacteristics;
  import android.hardware.camera2.CameraDevice;
  import android.hardware.camera2.CameraMetadata;
  import android.hardware.camera2.CaptureResult;
  import android.hardware.camera2.CaptureRequest;
  import android.hardware.camera2.TotalCaptureResult;
  import android.hardware.camera2.params.BlackLevelPattern;
  import android.hardware.camera2.params.ColorSpaceTransform;
  import android.hardware.camera2.params.Face;
  import android.hardware.camera2.params.LensShadingMap;
  import android.hardware.camera2.params.MeteringRectangle;
  import android.hardware.camera2.params.RggbChannelVector;
  import android.hardware.camera2.params.StreamConfigurationMap;
  import android.hardware.camera2.params.TonemapCurve;
  import android.location.Location;
  import android.util.Log;
  import android.util.Pair;
  import android.util.Rational;
  import android.util.Size;
  import android.util.SizeF;
  import android.util.Range;
  
  import org.json.JSONArray;
  import org.json.JSONObject;
  
  import java.lang.reflect.Array;
  import java.lang.reflect.Field;
  import java.lang.reflect.GenericArrayType;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.Arrays;
  import java.util.LinkedList;
  import java.util.List;
  
  /**
   * Class to deal with serializing and deserializing between JSON and Camera2 objects.
   */
  public class ItsSerializer {
      public static final String TAG = ItsSerializer.class.getSimpleName();
  
      private static class MetadataEntry {
          public MetadataEntry(String k, Object v) {
              key = k;
              value = v;
          }
          public String key;
          public Object value;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeRational(Rational rat) throws org.json.JSONException {
          JSONObject ratObj = new JSONObject();
          ratObj.put("numerator", rat.getNumerator());
          ratObj.put("denominator", rat.getDenominator());
          return ratObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeSize(Size size) throws org.json.JSONException {
          JSONObject sizeObj = new JSONObject();
          sizeObj.put("width", size.getWidth());
          sizeObj.put("height", size.getHeight());
          return sizeObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeSizeF(SizeF size) throws org.json.JSONException {
          JSONObject sizeObj = new JSONObject();
          sizeObj.put("width", size.getWidth());
          sizeObj.put("height", size.getHeight());
          return sizeObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeRect(Rect rect) throws org.json.JSONException {
          JSONObject rectObj = new JSONObject();
          rectObj.put("left", rect.left);
          rectObj.put("right", rect.right);
          rectObj.put("top", rect.top);
          rectObj.put("bottom", rect.bottom);
          return rectObj;
      }
  
      private static Object serializePoint(Point point) throws org.json.JSONException {
          JSONObject pointObj = new JSONObject();
          pointObj.put("x", point.x);
          pointObj.put("y", point.y);
          return pointObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeFace(Face face)
              throws org.json.JSONException {
          JSONObject faceObj = new JSONObject();
          faceObj.put("bounds", serializeRect(face.getBounds()));
          faceObj.put("score", face.getScore());
          faceObj.put("id", face.getId());
          if (face.getLeftEyePosition() != null) {
              faceObj.put("leftEye", serializePoint(face.getLeftEyePosition()));
          }
          if (face.getRightEyePosition() != null) {
              faceObj.put("rightEye", serializePoint(face.getRightEyePosition()));
          }
          if (face.getMouthPosition() != null) {
              faceObj.put("mouth", serializePoint(face.getMouthPosition()));
          }
          return faceObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeStreamConfigurationMap(
              StreamConfigurationMap map)
              throws org.json.JSONException {
          // TODO: Serialize the rest of the StreamConfigurationMap fields.
          JSONObject mapObj = new JSONObject();
          JSONArray cfgArray = new JSONArray();
          int fmts[] = map.getOutputFormats();
          if (fmts != null) {
              for (int fi = 0; fi < Array.getLength(fmts); fi++) {
                  Size sizes[] = map.getOutputSizes(fmts[fi]);
                  if (sizes != null) {
                      for (int si = 0; si < Array.getLength(sizes); si++) {
                          JSONObject obj = new JSONObject();
                          obj.put("format", fmts[fi]);
                          obj.put("width",sizes[si].getWidth());
                          obj.put("height", sizes[si].getHeight());
                          obj.put("input", false);
                          obj.put("minFrameDuration",
                                  map.getOutputMinFrameDuration(fmts[fi],sizes[si]));
                          cfgArray.put(obj);
                      }
                  }
                  sizes = map.getHighResolutionOutputSizes(fmts[fi]);
                  if (sizes != null) {
                      for (int si = 0; si < Array.getLength(sizes); si++) {
                          JSONObject obj = new JSONObject();
                          obj.put("format", fmts[fi]);
                          obj.put("width",sizes[si].getWidth());
                          obj.put("height", sizes[si].getHeight());
                          obj.put("input", false);
                          obj.put("minFrameDuration",
                                  map.getOutputMinFrameDuration(fmts[fi],sizes[si]));
                          cfgArray.put(obj);
                      }
                  }
              }
          }
          mapObj.put("availableStreamConfigurations", cfgArray);
          return mapObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeMeteringRectangle(MeteringRectangle rect)
              throws org.json.JSONException {
          JSONObject rectObj = new JSONObject();
          rectObj.put("x", rect.getX());
          rectObj.put("y", rect.getY());
          rectObj.put("width", rect.getWidth());
          rectObj.put("height", rect.getHeight());
          rectObj.put("weight", rect.getMeteringWeight());
          return rectObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializePair(Pair pair)
              throws org.json.JSONException {
          JSONArray pairObj = new JSONArray();
          pairObj.put(pair.first);
          pairObj.put(pair.second);
          return pairObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeRange(Range range)
              throws org.json.JSONException {
          JSONArray rangeObj = new JSONArray();
          rangeObj.put(range.getLower());
          rangeObj.put(range.getUpper());
          return rangeObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeColorSpaceTransform(ColorSpaceTransform xform)
              throws org.json.JSONException {
          JSONArray xformObj = new JSONArray();
          for (int row = 0; row < 3; row++) {
              for (int col = 0; col < 3; col++) {
                  xformObj.put(serializeRational(xform.getElement(col,row)));
              }
          }
          return xformObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeTonemapCurve(TonemapCurve curve)
              throws org.json.JSONException {
          JSONObject curveObj = new JSONObject();
          String names[] = {"red", "green", "blue"};
          for (int ch = 0; ch < 3; ch++) {
              JSONArray curveArr = new JSONArray();
              int len = curve.getPointCount(ch);
              for (int i = 0; i < len; i++) {
                  curveArr.put(curve.getPoint(ch,i).x);
                  curveArr.put(curve.getPoint(ch,i).y);
              }
              curveObj.put(names[ch], curveArr);
          }
          return curveObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeRggbChannelVector(RggbChannelVector vec)
              throws org.json.JSONException {
          JSONArray vecObj = new JSONArray();
          vecObj.put(vec.getRed());
          vecObj.put(vec.getGreenEven());
          vecObj.put(vec.getGreenOdd());
          vecObj.put(vec.getBlue());
          return vecObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeBlackLevelPattern(BlackLevelPattern pat)
              throws org.json.JSONException {
          int patVals[] = new int[4];
          pat.copyTo(patVals, 0);
          JSONArray patObj = new JSONArray();
          patObj.put(patVals[0]);
          patObj.put(patVals[1]);
          patObj.put(patVals[2]);
          patObj.put(patVals[3]);
          return patObj;
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeLocation(Location loc)
              throws org.json.JSONException {
          return loc.toString();
      }
  
      @SuppressWarnings("unchecked")
      private static Object serializeLensShadingMap(LensShadingMap map)
              throws org.json.JSONException {
          JSONArray mapObj = new JSONArray();
          for (int row = 0; row < map.getRowCount(); row++) {
              for (int col = 0; col < map.getColumnCount(); col++) {
                  for (int ch = 0; ch < 4; ch++) {
                      mapObj.put(map.getGainFactor(ch, col, row));
                  }
              }
          }
          return mapObj;
      }
  
      private static String getKeyName(Object keyObj) throws ItsException {
          if (keyObj.getClass() == CaptureResult.Key.class
                  || keyObj.getClass() == TotalCaptureResult.class) {
              return ((CaptureResult.Key)keyObj).getName();
          } else if (keyObj.getClass() == CaptureRequest.Key.class) {
              return ((CaptureRequest.Key)keyObj).getName();
          } else if (keyObj.getClass() == CameraCharacteristics.Key.class) {
              return ((CameraCharacteristics.Key)keyObj).getName();
          }
          throw new ItsException("Invalid key object");
      }
  
      private static Object getKeyValue(CameraMetadata md, Object keyObj) throws ItsException {
          if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) {
              return ((CaptureResult)md).get((CaptureResult.Key)keyObj);
          } else if (md.getClass() == CaptureRequest.class) {
              return ((CaptureRequest)md).get((CaptureRequest.Key)keyObj);
          } else if (md.getClass() == CameraCharacteristics.class) {
              return ((CameraCharacteristics)md).get((CameraCharacteristics.Key)keyObj);
          }
          throw new ItsException("Invalid key object");
      }
  
      @SuppressWarnings("unchecked")
      private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md)
              throws ItsException {
          String keyName = getKeyName(keyObj);
  
          try {
              Object keyValue = getKeyValue(md, keyObj);
              if (keyValue == null) {
                  return new MetadataEntry(keyName, JSONObject.NULL);
              } else if (keyType == Float.class) {
                  // The JSON serializer doesn't handle floating point NaN or Inf.
                  if (((Float)keyValue).isInfinite() || ((Float)keyValue).isNaN()) {
                      Logt.w(TAG, "Inf/NaN floating point value serialized: " + keyName);
                      return null;
                  }
                  return new MetadataEntry(keyName, keyValue);
              } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class ||
                         keyType == Boolean.class || keyType == String.class) {
                  return new MetadataEntry(keyName, keyValue);
              } else if (keyType == Rational.class) {
                  return new MetadataEntry(keyName, serializeRational((Rational)keyValue));
              } else if (keyType == Size.class) {
                  return new MetadataEntry(keyName, serializeSize((Size)keyValue));
              } else if (keyType == SizeF.class) {
                  return new MetadataEntry(keyName, serializeSizeF((SizeF)keyValue));
              } else if (keyType == Rect.class) {
                  return new MetadataEntry(keyName, serializeRect((Rect)keyValue));
              } else if (keyType == Face.class) {
                  return new MetadataEntry(keyName, serializeFace((Face)keyValue));
              } else if (keyType == StreamConfigurationMap.class) {
                  return new MetadataEntry(keyName,
                          serializeStreamConfigurationMap((StreamConfigurationMap)keyValue));
              } else if (keyType instanceof ParameterizedType &&
                      ((ParameterizedType)keyType).getRawType() == Range.class) {
                  return new MetadataEntry(keyName, serializeRange((Range)keyValue));
              } else if (keyType == ColorSpaceTransform.class) {
                  return new MetadataEntry(keyName,
                          serializeColorSpaceTransform((ColorSpaceTransform)keyValue));
              } else if (keyType == MeteringRectangle.class) {
                  return new MetadataEntry(keyName,
                          serializeMeteringRectangle((MeteringRectangle)keyValue));
              } else if (keyType == Location.class) {
                  return new MetadataEntry(keyName,
                          serializeLocation((Location)keyValue));
              } else if (keyType == RggbChannelVector.class) {
                  return new MetadataEntry(keyName,
                          serializeRggbChannelVector((RggbChannelVector)keyValue));
              } else if (keyType == BlackLevelPattern.class) {
                  return new MetadataEntry(keyName,
                          serializeBlackLevelPattern((BlackLevelPattern)keyValue));
              } else if (keyType == TonemapCurve.class) {
                  return new MetadataEntry(keyName,
                          serializeTonemapCurve((TonemapCurve)keyValue));
              } else if (keyType == Point.class) {
                  return new MetadataEntry(keyName,
                          serializePoint((Point)keyValue));
              } else if (keyType == LensShadingMap.class) {
                  return new MetadataEntry(keyName,
                          serializeLensShadingMap((LensShadingMap)keyValue));
              } else {
                  Logt.w(TAG, String.format("Serializing unsupported key type: " + keyType));
                  return null;
              }
          } catch (org.json.JSONException e) {
              throw new ItsException("JSON error for key: " + keyName + ": ", e);
          }
      }
  
      @SuppressWarnings("unchecked")
      private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)
              throws ItsException {
          String keyName = getKeyName(keyObj);
          try {
              Object keyValue = getKeyValue(md, keyObj);
              if (keyValue == null) {
                  return null;
              }
              int arrayLen = Array.getLength(keyValue);
              Type elmtType = ((GenericArrayType)keyType).getGenericComponentType();
              if (elmtType == int.class  || elmtType == float.class || elmtType == byte.class ||
                  elmtType == long.class || elmtType == double.class || elmtType == boolean.class) {
                  return new MetadataEntry(keyName, new JSONArray(keyValue));
              } else if (elmtType == Rational.class) {
                  JSONArray jsonArray = new JSONArray();
                  for (int i = 0; i < arrayLen; i++) {
                      jsonArray.put(serializeRational((Rational)Array.get(keyValue,i)));
                  }
                  return new MetadataEntry(keyName, jsonArray);
              } else if (elmtType == Size.class) {
                  JSONArray jsonArray = new JSONArray();
                  for (int i = 0; i < arrayLen; i++) {
                      jsonArray.put(serializeSize((Size)Array.get(keyValue,i)));
                  }
                  return new MetadataEntry(keyName, jsonArray);
              } else if (elmtType == Rect.class) {
                  JSONArray jsonArray = new JSONArray();
                  for (int i = 0; i < arrayLen; i++) {
                      jsonArray.put(serializeRect((Rect)Array.get(keyValue,i)));
                  }
                  return new MetadataEntry(keyName, jsonArray);
              } else if (elmtType == Face.class) {
  