public static Object copyToStandardObject(
      Object o, ObjectInspector oi, ObjectInspectorCopyOption objectInspectorOption) {
    if (o == null) {
      return null;
    }

    Object result = null;
    switch (oi.getCategory()) {
      case PRIMITIVE:
        {
          PrimitiveObjectInspector loi = (PrimitiveObjectInspector) oi;
          if (objectInspectorOption == ObjectInspectorCopyOption.DEFAULT) {
            objectInspectorOption =
                loi.preferWritable()
                    ? ObjectInspectorCopyOption.WRITABLE
                    : ObjectInspectorCopyOption.JAVA;
          }
          switch (objectInspectorOption) {
            case JAVA:
              result = loi.getPrimitiveJavaObject(o);
              if (loi.getPrimitiveCategory()
                  == PrimitiveObjectInspector.PrimitiveCategory.TIMESTAMP) {
                result =
                    PrimitiveObjectInspectorFactory.javaTimestampObjectInspector.copyObject(result);
              }
              break;
            case WRITABLE:
              result = loi.getPrimitiveWritableObject(loi.copyObject(o));
              break;
          }
          break;
        }
      case LIST:
        {
          ListObjectInspector loi = (ListObjectInspector) oi;
          int length = loi.getListLength(o);
          ArrayList<Object> list = new ArrayList<Object>(length);
          for (int i = 0; i < length; i++) {
            list.add(
                copyToStandardObject(
                    loi.getListElement(o, i),
                    loi.getListElementObjectInspector(),
                    objectInspectorOption));
          }
          result = list;
          break;
        }
      case MAP:
        {
          MapObjectInspector moi = (MapObjectInspector) oi;
          HashMap<Object, Object> map = new HashMap<Object, Object>();
          Map<? extends Object, ? extends Object> omap = moi.getMap(o);
          for (Map.Entry<? extends Object, ? extends Object> entry : omap.entrySet()) {
            map.put(
                copyToStandardObject(
                    entry.getKey(), moi.getMapKeyObjectInspector(), objectInspectorOption),
                copyToStandardObject(
                    entry.getValue(), moi.getMapValueObjectInspector(), objectInspectorOption));
          }
          result = map;
          break;
        }
      case STRUCT:
        {
          StructObjectInspector soi = (StructObjectInspector) oi;
          List<? extends StructField> fields = soi.getAllStructFieldRefs();
          ArrayList<Object> struct = new ArrayList<Object>(fields.size());
          for (StructField f : fields) {
            struct.add(
                copyToStandardObject(
                    soi.getStructFieldData(o, f),
                    f.getFieldObjectInspector(),
                    objectInspectorOption));
          }
          result = struct;
          break;
        }
      case UNION:
        {
          UnionObjectInspector uoi = (UnionObjectInspector) oi;
          List<ObjectInspector> objectInspectors = uoi.getObjectInspectors();
          Object object =
              copyToStandardObject(
                  uoi.getField(o), objectInspectors.get(uoi.getTag(o)), objectInspectorOption);
          result = object;
          break;
        }
      default:
        {
          throw new RuntimeException("Unknown ObjectInspector category!");
        }
    }
    return result;
  }