Package com.peterhi.io

Source Code of com.peterhi.io.PmOutputStream

package com.peterhi.io;

import static com.peterhi.Util.*;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.peterhi.Integer24;
import com.peterhi.IntegerX;
import com.peterhi.int24;
import com.peterhi.intx;
import com.peterhi.uintx;
import com.peterhi.property.PropDescriptor;
import com.peterhi.property.PropException;
import com.peterhi.property.PropModel;
import com.peterhi.property.PropUtil;

public final class PmOutputStream extends DataOutputStream
                  implements PmIoConstants {
 
  public PmOutputStream(OutputStream out) {
    super(out);
  }
 
  public void writeModel(PropModel model) throws IOException, PropException {
    if (model == null) {
      throw new IllegalArgumentException("Null model.");
    }
   
    PmLookup lookup = new PmLookup(model);
    Class<?> mtype = model.getClass();
    Class<?>[] types = lookup.getTypes();
    TypeExpr expr = new TypeExpr().types(types).mainType(mtype);
    String string = expr.toString();
    writeSize(string.length(), false);
    writeBytes(string);
   
    for (List<PropModel> list : lookup.values()) {
      writeSize(list.size(), false);
    }

    for (int i = 0; i < lookup.count(); i++) {
      PropModel m = lookup.get(i);
      Class<?> t = m.getClass();
      Set<PropDescriptor> ps = PropUtil.getDescriptors(t);
     
      for (PropDescriptor p : ps) {
        Class<?> pt = p.getType();
        Object pv = p.get(m);
        writeRecursively(p, pt, pv, lookup);
      }
    }
  }
 
  private void writeRecursively(PropDescriptor desc, Class<?> type,
    Object value, PmLookup lookup) throws IOException {
    if (type == boolean.class) {
      writeBoolean((Boolean )value);
    } else if (type == byte.class) {
      writeByte((Byte )value);
    } else if (type == char.class) {
      writeChar((Character )value);
    } else if (type == short.class) {
      writeShort((Short )value);
    } else if (type == int.class) {
      writeInt((Integer )value);
    } else if (type == long.class) {
      writeLong((Long )value);
    } else if (type == float.class) {
      writeFloat((Float )value);
    } else if (type == double.class) {
      writeDouble((Double )value);
    } else if (type == int24.class) {
      write(((int24 )value).toByteArray());
    } else if (type == intx.class) {
      write(((intx )value).toByteArray());
    } else if (type == uintx.class) {
      write(((uintx )value).toByteArray());
    } else if (type == Boolean.class) {
      Boolean b = (Boolean )value;
      write(b == null ? -1 : (b ? 1 : 0));
    } else if (type == boolean[].class) {
      boolean[] booleans = (boolean[] )value;
      int size = sizeof(booleans);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      byte[] bytes = new byte[bitsToBytes(size)];
     
      for (int i = 0; i < size; i++) {
        int index = i / Byte.SIZE;
        int shift = i % Byte.SIZE;
        int bits = booleans[i] ? 1 : 0;
        bytes[index] |= bits << shift;
      }
     
      write(bytes);
    } else if (type == byte[].class) {
      byte[] bytes = (byte[] )value;
      int size = sizeof(bytes);
     
      if (!writeSize(size, true)) {
        return;
      }

      write(bytes);
    } else if (type == Class.class) {
      Class<?> clazz = (Class<?> )value;
      Class<?>[] classes = (value == null) ? null :
        new Class<?>[] { clazz };
      writeRecursively(desc, Class[].class, classes, lookup);
    } else if (type == Class[].class) {
      TypeExpr expr = (value == null) ? null :
        new TypeExpr().types((Class<?>[] )value);
      String string = (expr == null) ? null : expr.toString();
      int size = sizeof(string);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      writeBytes(string);
    } else if (type == String.class) {
      String string = (String )value;
      int size = sizeofUtf(string);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      for (int i = 0; i < string.length(); i++) {
        char ch = string.charAt(i);
       
        if (ch >= 0x0001 && ch <= 0x007f) {
          write(ch);
        } else if (ch > 0x07ff) {
          write(0x30 | ((ch >> 12) & 0x0f));
          write(0x80 | ((ch >>  6) & 0x3f));
          write(0x80 | ((ch >>  0) & 0x3f));
        } else {
          write(0xc0 | ((ch >>  6) & 0x1f));
          write(0x80 | ((ch >>  0) & 0x3f));
        }
      }
    } else if (PropModel.class.isAssignableFrom(type)) {
      PropModel model = (PropModel )value;
      writeIndex(model, lookup);
    } else if (type.isArray()) {
      int size = sizeof(value);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      Class<?> ctype = type.getComponentType();
     
      for (int i = 0; i < size; i++) {
        Object e = at(value, i);
        writeRecursively(desc, ctype, e, lookup);
      }
    } else if (type == HashSet.class || type == ArrayList.class) {
      int size = sizeof(value);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      for (int i = 0; i < size; i++) {
        Object e = at(value, i);
        writeRecursively(desc, Object.class, e, lookup);
      }
    } else if (type == HashMap.class) {
      int size = sizeof(value);
     
      if (!writeSize(size, true)) {
        return;
      }
     
      for (int i = 0; i < size; i++) {
        Map.Entry<?, ?> e = (Map.Entry<?, ?> )at(value, i);
        Object k = e.getKey();
        Object v = e.getValue();
        writeRecursively(desc, Object.class, k, lookup);
        writeRecursively(desc, Object.class, v, lookup);
      }
    } else if (Collection.class.isAssignableFrom(type)) {
      int size = sizeof(value);
     
      for (int i = 0; i < size; i++) {
        Object e = at(value, i);
        writeRecursively(desc, Object.class, e, lookup);
      }
    } else if (Map.class.isAssignableFrom(type)) {
      int size = sizeof(value);
     
      for (int i = 0; i < size; i++) {
        Map.Entry<?, ?> e = (Map.Entry<?, ?> )at(value, i);
        Object k = e.getKey();
        Object v = e.getValue();
        writeRecursively(desc, Object.class, k, lookup);
        writeRecursively(desc, Object.class, v, lookup);
      }
    } else if (type == Object.class) {
      int rt = runtimeType(value);
     
      if (!writeRuntimeType(rt)) {
        return;
      }
     
      Class<?> rtype = value.getClass();
     
      if (rt == RT_ARRAY) {
        Class<?> ctype = componentType(rtype);
        int crt = runtimeType(ctype);
        int dimension = dimension(rtype);
        writeRuntimeType(crt);
        writeSize(dimension, false);
        writeSizes(value, dimension);
        writeElements(desc, ctype, value, dimension, lookup);
      } else if (rt != RT_NULL) {
        writeRecursively(desc, rtype, value, lookup);
      }
    } else {
      boolean notNull = (value != null);
      writeBoolean(notNull);
     
      if (!notNull) {
        return;
      }
     
      if (type == Byte.class) {
        writeRecursively(desc, byte.class, value, lookup);
      } else if (type == Character.class) {
        writeRecursively(desc, char.class, value, lookup);
      } else if (type == Short.class) {
        writeRecursively(desc, short.class, value, lookup);
      } else if (type == Integer.class) {
        writeRecursively(desc, int.class, value, lookup);
      } else if (type == Long.class) {
        writeRecursively(desc, long.class, value, lookup);
      } else if (type == Float.class) {
        writeRecursively(desc, float.class, value, lookup);
      } else if (type == Double.class) {
        writeRecursively(desc, double.class, value, lookup);
      } else if (type == Integer24.class) {
        writeRecursively(desc, int24.class,
          new int24((Integer24 )value), lookup);
      } else if (type == IntegerX.Signed.class) {
        writeRecursively(desc, intx.class,
          new intx((IntegerX.Signed )value), lookup);
      } else if (type == IntegerX.Unsigned.class) {
        writeRecursively(desc, uintx.class,
          new uintx((IntegerX.Unsigned )value), lookup);
      } else {
        throw new IllegalArgumentException(MessageFormat.format(
          "Unsupported type \"{0}\".", type));
      }
    }
  }
 
  private void writeSizes(Object value, int dimension)
    throws IOException {
    int size = sizeof(value);
   
    if (!writeSize(size, true)) {
      return;
    }
   
    boolean multidimensional = dimension > 1;
   
    if (!multidimensional) {
      return;
    }
   
    for (int i = 0; i < size; i++) {
      Object e = at(value, i);
      writeSizes(e, dimension - 1);
    }
  }
 
  private void writeElements(PropDescriptor desc, Class<?> ctype,
    Object value, int dimension, PmLookup lookup) throws IOException {
    boolean multidimensional = dimension > 1;
    int size = sizeof(value);
   
    for (int i = 0; i < size; i++) {
      Object e = at(value, i);
     
      if (multidimensional) {
        writeElements(desc, ctype, e, dimension - 1, lookup);
      } else {
        writeRecursively(desc, ctype, e, lookup);
      }
    }
  }
 
  private int dimension(Class<?> type) {
    if (type == null) {
      throw new IllegalArgumentException("Null type.");
    }
   
    if (!type.isArray()) {
      return 0;
    }
   
    return dimension(type.getComponentType()) + 1;
  }
 
  private Class<?> componentType(Class<?> type) {
    if (type == null) {
      throw new IllegalArgumentException("Null type.");
    }
   
    if (!type.isArray()) {
      return type;
    }
   
    return componentType(type.getComponentType());
  }
 
  private int runtimeType(Object value) {
    return runtimeType(value == null ? null : value.getClass());
  }
 
  private int runtimeType(Class<?> type) {
    if (type == null) {
      return RT_NULL;
    } else if (type == Boolean.class) {
      return RT_BOOLEAN;
    } else if (type == Byte.class) {
      return RT_BYTE;
    } else if (type == Character.class) {
      return RT_CHARACTER;
    } else if (type == Short.class) {
      return RT_SHORT;
    } else if (type == Integer.class) {
      return RT_INTEGER;
    } else if (type == Long.class) {
      return RT_LONG;
    } else if (type == Float.class) {
      return RT_FLOAT;
    } else if (type == Double.class) {
      return RT_DOUBLE;
    } else if (type == Integer24.class) {
      return RT_INTEGER24;
    } else if (type == IntegerX.Signed.class) {
      return RT_INTEGERX_SIGNED;
    } else if (type == IntegerX.Unsigned.class) {
      return RT_INTEGERX_UNSIGNED;
    } else if (type == boolean[].class) {
      return RT_BOOLEANS;
    } else if (type == byte[].class) {
      return RT_BYTES;
    } else if (type == char[].class) {
      return RT_CHARS;
    } else if (type == short[].class) {
      return RT_SHORTS;
    } else if (type == int[].class) {
      return RT_INTS;
    } else if (type == long[].class) {
      return RT_LONGS;
    } else if (type == float[].class) {
      return RT_FLOATS;
    } else if (type == double[].class) {
      return RT_DOUBLES;
    } else if (type == int24[].class) {
      return RT_INT24S;
    } else if (type == intx[].class) {
      return RT_INTXS;
    } else if (type == uintx[].class) {
      return RT_UINTXS;
    } else if (type == Class.class) {
      return RT_CLASS;
    } else if (type == Class[].class) {
      return RT_CLASSES;
    } else if (type == String.class) {
      return RT_STRING;
    } else if (PropModel.class.isAssignableFrom(type)) {
      return RT_PROPMODEL;
    } else if (type == HashSet.class) {
      return RT_HASHSET;
    } else if (type == ArrayList.class) {
      return RT_ARRAYLIST;
    } else if (type == HashMap.class) {
      return RT_HASHMAP;
    } else if (type.isArray()) {
      runtimeType(componentType(type));
      return RT_ARRAY;
    } else {
      throw new IllegalArgumentException(MessageFormat.format(
        "Unsupported type \"{0}\".", type));
    }
  }

  private boolean writeSize(int size, boolean nullable) throws IOException {
    uintx u = new uintx(size + (nullable ? 1 : 0));
    write(u.toByteArray());
    return nullable ? (size != -1) : true;
   
  }
 
  private void writeIndex(PropModel model, PmLookup lookup)
    throws IOException {
    int index = lookup.indexOf(model);
    writeSize(index, true);
  }
 
  private boolean writeRuntimeType(int rt) throws IOException {
    writeSize(rt, false);
    return (rt != RT_NULL);
  }
 
  private int sizeofUtf(String string) {
    if (string == null) {
      return -1;
    }
   
    int size = 0;
   
    for (int i = 0; i < string.length(); i++) {
      char ch = string.charAt(i);
     
      if (ch >= 0x0001 && ch <= 0x007f) {
        size += 1;
      } else if (ch > 0x07ff) {
        size += 3;
      } else {
        size += 2;
      }
    }
   
    return size;
  }
}
TOP

Related Classes of com.peterhi.io.PmOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.