Package com.peterhi.io

Source Code of com.peterhi.io.PmInputStream

package com.peterhi.io;

import static com.peterhi.Util.bitsToBytes;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UTFDataFormatException;
import java.lang.reflect.Array;
import java.text.MessageFormat;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.peterhi.Integer24;
import com.peterhi.IntegerX;
import com.peterhi.Util;
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 PmInputStream extends DataInputStream
                   implements PmIoConstants{

  public PmInputStream(InputStream in) {
    super(in);
  }
 
  @SuppressWarnings("unchecked")
  public <T extends PropModel> T readModel() throws IOException,
    PropException, ClassNotFoundException, IllegalAccessException,
    InstantiationException {
    int size = readSize(false);
    byte[] buffer = new byte[size];
    readFully(buffer);
    String string = new String(buffer, "ascii");
    TypeExpr expr = new TypeExpr(string);
    int[] counts = new int[expr.size()];
   
    for (int i = 0; i < counts.length; i++) {
      counts[i] = readSize(false);
    }
   
    PmLookup lookup = new PmLookup(expr, counts);
   
    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 = readRecursively(p, pt, lookup);
        p.set(m, pv);
      }
    }
   
    return (T )lookup.getRootModel();
  }
 
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private Object readRecursively(PropDescriptor desc, Class<?> type,
    PmLookup lookup) throws IOException, ClassNotFoundException,
    InstantiationException, IllegalAccessException {
    if (type == boolean.class) {
      return readBoolean();
    } else if (type == byte.class) {
      return readByte();
    } else if (type == char.class) {
      return readChar();
    } else if (type == short.class) {
      return readShort();
    } else if (type == int.class) {
      return readInt();
    } else if (type == long.class) {
      return readLong();
    } else if (type == float.class) {
      return readFloat();
    } else if (type == double.class) {
      return readDouble();
    } else if (type == int24.class) {
      return new int24(this);
    } else if (type == intx.class) {
      return new intx(this);
    } else if (type == uintx.class) {
      return new uintx(this);
    } else if (type == Boolean.class) {
      int b = readByte();
      return (b == -1) ? null : (b != 0);
    } else if (type == boolean[].class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }

      boolean[] booleans = new boolean[size];
      byte[] buffer = new byte[bitsToBytes(size)];
      readFully(buffer);
     
      for (int i = 0; i < size; i++) {
        int index = i / Byte.SIZE;
        int shift = i % Byte.SIZE;
        int bits = (buffer[index] >>> shift) & 0x01;
        booleans[i] = (bits != 0);
      }
     
      return booleans;
    } else if (type == byte[].class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      byte[] buffer = new byte[size];
      readFully(buffer);
      return buffer;
    } else if (type == Class.class) {
      Class<?>[] classes = (Class<?>[] )readRecursively(desc,
        Class[].class, lookup);
      return (classes == null) ? null : classes[0];
    } else if (type == Class[].class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      byte[] buffer = new byte[size];
      readFully(buffer);
      String string = new String(buffer, "ascii");
      TypeExpr expr = new TypeExpr(string);
      return expr.getTypes();
    } else if (type == String.class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      byte[] buffer = new byte[size];
      readFully(buffer);
      StringBuilder sb = new StringBuilder();
      int processed = 0;
     
      while (processed < size) {
        int ch0 = buffer[processed];
        int width = utfCharWidth(ch0);
       
        if (width == 0) {
          utfCorrupt(processed);
        }
       
        processed++;
       
        if (width == 1) {
          char ch = 0;
          ch |= ch0;
          sb.append(ch);
        } else if (width == 2) {
          int ch1 = buffer[processed];
         
          if ((ch1 & 0xc0) != 0x80) {
            utfCorrupt(processed);
          }
         
          processed++;
         
          char ch = 0;
          ch |= (ch0 & 0x1f) << 6;
          ch |= (ch1 & 0x3f) << 0;
          sb.append(ch);
        } else if (width == 3) {
          int ch1 = buffer[processed];
         
          if ((ch1 & 0xc0) != 0x80) {
            utfCorrupt(processed);
          }
         
          processed++;
         
          int ch2 = buffer[processed];
         
          if ((ch2 & 0xc0) != 0x80) {
            utfCorrupt(processed);
          }
         
          processed++;
         
          char ch = 0;
          ch |= (ch0 & 0x0f) << 16;
          ch |= (ch1 & 0x3f) <<  6;
          ch |= (ch2 & 0x3f) <<  0;
          sb.append(ch);
        }
      }
     
      return sb.toString();
    } else if (PropModel.class.isAssignableFrom(type)) {
      return fromIndex(lookup);
    } else if (type.isArray()) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      Class<?> ctype = type.getComponentType();
      Object a = Array.newInstance(ctype, size);
     
      for (int i = 0; i < size; i++) {
        Object e = readRecursively(desc, ctype, lookup);
        Array.set(a, i, e);
      }
     
      return a;
    } else if (type == HashSet.class || type == ArrayList.class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      Collection<Object> c = (Collection<Object> )type.newInstance();
     
      for (int i = 0; i < size; i++) {
        Object e = readRecursively(desc, Object.class, lookup);
        c.add(e);
      }
     
      return c;
    } else if (type == HashMap.class) {
      int size = readSize(true);
     
      if (size < 0) {
        return null;
      }
     
      Map<Object, Object> m = new HashMap<Object, Object>();
     
      for (int i = 0; i < size; i++) {
        Object k = readRecursively(desc, Object.class, lookup);
        Object v = readRecursively(desc, Object.class, lookup);
        m.put(k, v);
      }
     
      return m;
    } else if (Collection.class.isAssignableFrom(type)) {
      int size = readSize(false);
      Object[] a = new Object[size];
     
      for (int i = 0; i < size; i++) {
        a[i] = readRecursively(desc, Object.class, lookup);
      }
     
      return a;
    } else if (Map.class.isAssignableFrom(type)) {
      int size = readSize(false);
      Map.Entry<?, ?>[] a = new Map.Entry<?, ?>[size];
     
      for (int i = 0; i < size; i++) {
        Object k = readRecursively(desc, Object.class, lookup);
        Object v = readRecursively(desc, Object.class, lookup);
        a[i] = new AbstractMap.SimpleEntry(k, v);
      }
     
      return a;
    } else if (type == Object.class) {
      int rt = readSize(false);
      Class<?> rtype = runtimeType(rt);
      Object value = null;
     
      if (rtype == Array.class) {
        int crt = readSize(false);
        Class<?> ctype = runtimeType(crt);
        int dimension = readSize(false);
        Class<?> mtype = Util.multiDimArray(ctype, dimension);
        value = initBlankElements(mtype);
        fillElements(desc, value, lookup);
      } else if (rtype != null) {
        value = readRecursively(desc, rtype, lookup);
      }

      return value;
    } else {
      boolean notNull = readBoolean();
     
      if (!notNull) {
        return null;
      }
     
      if (type == Byte.class) {
        return readRecursively(desc, byte.class, lookup);
      } else if (type == Character.class) {
        return readRecursively(desc, char.class, lookup);
      } else if (type == Short.class) {
        return readRecursively(desc, short.class, lookup);
      } else if (type == Integer.class) {
        return readRecursively(desc, int.class, lookup);
      } else if (type == Long.class) {
        return readRecursively(desc, long.class, lookup);
      } else if (type == Float.class) {
        return readRecursively(desc, float.class, lookup);
      } else if (type == Double.class) {
        return readRecursively(desc, double.class, lookup);
      } else if (type == Integer24.class) {
        return readRecursively(desc, int24.class, lookup);
      } else if (type == IntegerX.Signed.class) {
        return readRecursively(desc, intx.class, lookup);
      } else if (type == IntegerX.Unsigned.class) {
        return readRecursively(desc, uintx.class, lookup);
      } else {
        throw new IllegalArgumentException(MessageFormat.format(
          "Unsupported type \"{0}\".", type));
      }
    }
  }
 
  private int utfCharWidth(int ch) {
    int high = ch >> 4;
         
    if (high >= 0 && high <= 7) {
      return 1;
    } else if (high == 12 && high == 13) {
      return 2;
    } else if (high == 14) {
      return 3;
    }
   
    return 0;
  }
 
  private Object initBlankElements(Class<?> type) throws IOException {
    Class<?> ctype = type.getComponentType();
   
    if (ctype == null) {
      return null;
    }

    int size = readSize(true);
   
    if (size < 0) {
      return null;
    }
   
    Object array = Array.newInstance(ctype, size);
   
    for (int i = 0; i < size; i++) {
      Object element = initBlankElements(ctype);
      Array.set(array, i, element);
    }
   
    return array;
  }
 
  private void fillElements(PropDescriptor desc, Object array,
    PmLookup lookup) throws IOException, IllegalAccessException,
    InstantiationException, ClassNotFoundException {
    Class<?> type = array.getClass();
    Class<?> ctype = type.getComponentType();
    int length = Array.getLength(array);
   
    if (!ctype.isArray()) {
      for (int i = 0; i < length; i++) {
        Object element = readRecursively(desc, ctype, lookup);
        Array.set(array, i, element);
      }
    } else for (int i = 0; i < length; i++) {
      Object element = Array.get(array, i);
      fillElements(desc, element, lookup);
    }
  }
 
  private int readSize(boolean nullable) throws IOException {
    uintx u = new uintx(this);
    return u.intValue() - (nullable ? 1 : 0);
  }
 
  private PropModel fromIndex(PmLookup lookup) throws IOException {
    return lookup.get(readSize(true));
  }
 
  private void utfCorrupt(int index) throws IOException {
    throw new UTFDataFormatException(MessageFormat.format(
      "Corrupted utf data at indeex \"{0}\".", index));
  }
 
  private Class<?> runtimeType(int rt) {
    if (rt == RT_NULL) {
      return null;
    } else if (rt == RT_BOOLEAN) {
      return Boolean.class;
    } else if (rt == RT_BYTE) {
      return Byte.class;
    } else if (rt == RT_CHARACTER) {
      return Character.class;
    } else if (rt == RT_SHORT) {
      return Short.class;
    } else if (rt == RT_INTEGER) {
      return Integer.class;
    } else if (rt == RT_LONG) {
      return Long.class;
    } else if (rt == RT_FLOAT) {
      return Float.class;
    } else if (rt == RT_DOUBLE) {
      return Double.class;
    } else if (rt == RT_INTEGER24) {
      return Integer24.class;
    } else if (rt == RT_INTEGERX_SIGNED) {
      return IntegerX.Signed.class;
    } else if (rt == RT_INTEGERX_UNSIGNED) {
      return IntegerX.Unsigned.class;
    } else if (rt == RT_BOOLEANS) {
      return boolean[].class;
    } else if (rt == RT_BYTES) {
      return byte[].class;
    } else if (rt == RT_CHARS) {
      return char[].class;
    } else if (rt == RT_SHORTS) {
      return short[].class;
    } else if (rt == RT_INTS) {
      return int[].class;
    } else if (rt == RT_LONGS) {
      return long[].class;
    } else if (rt == RT_FLOATS) {
      return float[].class;
    } else if (rt == RT_DOUBLES) {
      return double[].class;
    } else if (rt == RT_INT24S) {
      return int24[].class;
    } else if (rt == RT_INTXS) {
      return intx[].class;
    } else if (rt == RT_UINTXS) {
      return uintx[].class;
    } else if (rt == RT_CLASS) {
      return Class.class;
    } else if (rt == RT_CLASSES) {
      return Class[].class;
    } else if (rt == RT_STRING) {
      return String.class;
    } else if (rt == RT_PROPMODEL) {
      return PropModel.class;
    } else if (rt == RT_HASHSET) {
      return HashSet.class;
    } else if (rt == RT_ARRAYLIST) {
      return ArrayList.class;
    } else if (rt == RT_HASHMAP) {
      return HashMap.class;
    } else if (rt == RT_ARRAY) {
      return Array.class;
    } else {
      throw new IllegalArgumentException(MessageFormat.format(
        "Unsupported type \"{0\"}.", rt));
    }
  }
}
TOP

Related Classes of com.peterhi.io.PmInputStream

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.