/******************************************************************************* * Copyright (c) 2009-2015 Centrum Wiskunde en Informatica (CWI) * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Arnold Lankamp - interfaces and implementation * Anya Helene Bagge - labeled map types * Jurgen Vinju - externa; types *******************************************************************************/ package org.rascalmpl.value.io.binary; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.rascalmpl.value.IAnnotatable; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IDateTime; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IList; import org.rascalmpl.value.IMap; import org.rascalmpl.value.INode; import org.rascalmpl.value.IRational; import org.rascalmpl.value.IReal; import org.rascalmpl.value.ISet; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.ITuple; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IWithKeywordParameters; import org.rascalmpl.value.type.ExternalType; import org.rascalmpl.value.type.ITypeVisitor; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeStore; import org.rascalmpl.value.util.IndexedSet; import org.rascalmpl.value.visitors.IValueVisitor; // TODO Change this thing so it doesn't use recursion. /** * @author Arnold Lankamp */ public class BinaryWriter{ public static final class IdentityValue implements IValue { private final IValue wrapped; public IdentityValue(IValue toWrap) { wrapped = toWrap; } @Override public int hashCode() { return System.identityHashCode(wrapped); } @Override public boolean equals(Object obj) { return obj instanceof IdentityValue && wrapped == ((IdentityValue)obj).wrapped; } @Override public Type getType() { throw new UnsupportedOperationException(); } @Override public <T, E extends Throwable> T accept(IValueVisitor<T, E> v) throws E { throw new UnsupportedOperationException(); } @Override public boolean isEqual(IValue other) { throw new UnsupportedOperationException(); } @Override public boolean isAnnotatable() { throw new UnsupportedOperationException(); } @Override public IAnnotatable<? extends IValue> asAnnotatable() { throw new UnsupportedOperationException(); } @Override public boolean mayHaveKeywordParameters() { throw new UnsupportedOperationException(); } @Override public IWithKeywordParameters<? extends IValue> asWithKeywordParameters() { throw new UnsupportedOperationException(); } } /*package*/ static final String CharEncoding = "UTF8"; private final static int BOOL_HEADER = 0x01; // private final static int INTEGER_HEADER = 0x02; private final static int BIG_INTEGER_HEADER = 0x03; // Special case of INTEGER_HEADER (flags for alternate encoding). private final static int DOUBLE_HEADER = 0x04; private final static int STRING_HEADER = 0x05; private final static int SOURCE_LOCATION_HEADER = 0x06; private final static int DATE_TIME_HEADER = 0x10; private final static int TUPLE_HEADER = 0x07; private final static int NODE_HEADER = 0x08; private final static int ANNOTATED_NODE_HEADER = 0x09; private final static int CONSTRUCTOR_HEADER = 0x0a; private final static int ANNOTATED_CONSTRUCTOR_HEADER = 0x0b; private final static int LIST_HEADER = 0x0c; private final static int SET_HEADER = 0x0d; private final static int MAP_HEADER = 0x0f; private final static int RATIONAL_HEADER = 0x11; private final static int KEYWORDED_NODE_HEADER = 0x12; private final static int KEYWORDED_CONSTRUCTOR_HEADER = 0x13; private final static int VALUE_TYPE_HEADER = 0x01; private final static int VOID_TYPE_HEADER = 0x02; private final static int BOOL_TYPE_HEADER = 0x03; private final static int INTEGER_TYPE_HEADER = 0x04; private final static int DOUBLE_TYPE_HEADER = 0x05; private final static int STRING_TYPE_HEADER = 0x06; private final static int SOURCE_LOCATION_TYPE_HEADER = 0x07; private final static int DATE_TIME_TYPE_HEADER = 0x14; private final static int NODE_TYPE_HEADER = 0x08; private final static int TUPLE_TYPE_HEADER = 0x09; private final static int LIST_TYPE_HEADER = 0x0a; private final static int SET_TYPE_HEADER = 0x0b; private final static int MAP_TYPE_HEADER = 0x0d; private final static int PARAMETER_TYPE_HEADER = 0x0e; private final static int ADT_TYPE_HEADER = 0x0f; private final static int CONSTRUCTOR_TYPE_HEADER = 0x10; private final static int ALIAS_TYPE_HEADER = 0x11; private final static int ANNOTATED_NODE_TYPE_HEADER = 0x12; private final static int ANNOTATED_CONSTRUCTOR_TYPE_HEADER = 0x13; private final static int RATIONAL_TYPE_HEADER = 0x15; private final static int NUM_TYPE_HEADER = 0x16; private final static int SHARED_FLAG = 0x80; private final static int TYPE_SHARED_FLAG = 0x40; private final static int URL_SHARED_FLAG = 0x20; private final static int NAME_SHARED_FLAG = 0x20; private final static int HAS_FIELD_NAMES = 0x20; private final static int DATE_TIME_INDICATOR = 0x01; private final static int DATE_INDICATOR = 0x02; private final static int TIME_INDICATOR = 0x03; private final IndexedSet<IValue> sharedValues; private final IndexedSet<Type> sharedTypes; private final IndexedSet<String> sharedPaths; private final IndexedSet<String> sharedNames; private final IValue value; private final OutputStream out; private final TypeStore typeStore; private final boolean maximalSharing; public BinaryWriter(IValue value, OutputStream outputStream, TypeStore typeStore){ this(value, outputStream, true, typeStore); } public BinaryWriter(IValue value, OutputStream outputStream, boolean maximalSharing, TypeStore typeStore){ super(); this.value = value; this.out = outputStream; this.typeStore = typeStore; this.maximalSharing = maximalSharing; sharedValues = new IndexedSet<>(); sharedTypes = new IndexedSet<>(); sharedPaths = new IndexedSet<>(); sharedNames = new IndexedSet<>(); } public void serialize() throws IOException{ doSerialize(value); } private void doSerialize(IValue value) throws IOException{ // This special cases the hashing logic: if we have a constructor with // at least one location annotation, don't try to hash it boolean tryHashing = true; if (tryHashing && value.getType().isAbstractData()) { IConstructor consValue = (IConstructor)value; if (consValue.isAnnotatable() && consValue.asAnnotatable().hasAnnotations()) { Map<String,IValue> amap = consValue.asAnnotatable().getAnnotations(); for (Entry<String, IValue> aEntry : amap.entrySet()) { Type aType = aEntry.getValue().getType(); if (!aType.isBottom() && aType.isSourceLocation()) { tryHashing = false; break; } } } } boolean alwaysMaximallyShare = value.getType().isString() || value.getType().isNumber() || value.getType().isSourceLocation(); if (tryHashing) { int valueId; if (maximalSharing || alwaysMaximallyShare) { valueId = sharedValues.get(value); } else { valueId = sharedValues.get(new IdentityValue(value)); } if(valueId != -1){ out.write(SHARED_FLAG); printInteger(valueId); return; } } // This sucks and is order dependent :-/. if(value instanceof IBool){ writeBool((IBool) value); }else if(value instanceof IInteger){ writeInteger((IInteger) value); }else if(value instanceof IRational){ writeRational((IRational) value); }else if(value instanceof IReal){ writeDouble((IReal) value); }else if(value instanceof IString){ writeString((IString) value); }else if(value instanceof ISourceLocation){ writeSourceLocation((ISourceLocation) value); }else if(value instanceof IDateTime){ writeDateTime((IDateTime) value); }else if(value instanceof ITuple){ writeTuple((ITuple) value); }else if(value instanceof IConstructor){ IConstructor constructor = (IConstructor) value; if (constructor.mayHaveKeywordParameters() && constructor.asWithKeywordParameters().hasParameters()) { writeKeywordedConstructor(constructor); } else if (!constructor.mayHaveKeywordParameters() && constructor.isAnnotatable() && constructor.asAnnotatable().hasAnnotations()) { writeAnnotatedConstructor(constructor); } else { writeConstructor(constructor); } }else if(value instanceof INode){ INode node = (INode) value; if (node.mayHaveKeywordParameters() && node.asWithKeywordParameters().hasParameters()) { writeKeywordedNode(node); } else if (!node.mayHaveKeywordParameters() && node.asAnnotatable().hasAnnotations()) { writeAnnotatedNode(node); } else { writeNode(node); } }else if(value instanceof IList){ writeList((IList) value); }else if(value instanceof ISet){ writeSet((ISet) value); }else if(value instanceof IMap){ writeMap((IMap) value); } if (tryHashing) { if (maximalSharing || alwaysMaximallyShare) { sharedValues.store(value); } else { sharedValues.store(new IdentityValue(value)); } } } private void doWriteType(Type type) throws IOException{ // This sucks and is order dependent :-/. type.accept(new ITypeVisitor<Type,IOException>() { @Override public Type visitReal(Type type) throws IOException { writeDoubleType(); return type; } @Override public Type visitInteger(Type type) throws IOException { writeIntegerType(); return type; } @Override public Type visitRational(Type type) throws IOException { writeRationalType(); return type; } @Override public Type visitList(Type type) throws IOException { writeListType(type); return type; } @Override public Type visitMap(Type type) throws IOException { writeMapType(type); return type; } @Override public Type visitNumber(Type type) throws IOException { writeNumType(); return type; } @Override public Type visitAlias(Type type) throws IOException { writeAliasType(type); return type; } @Override public Type visitSet(Type type) throws IOException { writeSetType(type); return type; } @Override public Type visitSourceLocation(Type type) throws IOException { writeSourceLocationType(); return type; } @Override public Type visitString(Type type) throws IOException { writeStringType(); return type; } @Override public Type visitNode(Type type) throws IOException { writeNodeType(type); return type; } @Override public Type visitConstructor(Type type) throws IOException { writeConstructorType(type); return type; } @Override public Type visitAbstractData(Type type) throws IOException { writeADTType(type); return type; } @Override public Type visitTuple(Type type) throws IOException { writeTupleType(type); return type; } @Override public Type visitValue(Type type) throws IOException { writeValueType(); return type; } @Override public Type visitVoid(Type type) throws IOException { writeVoidType(); return type; } @Override public Type visitBool(Type type) throws IOException { writeBoolType(); return type; } @Override public Type visitParameter(Type type) throws IOException { writeParameterType(type); return type; } @Override public Type visitExternal(Type type) throws IOException { return visitAbstractData(((ExternalType) type).asAbstractDataType()); } @Override public Type visitDateTime(Type type) throws IOException { writeDateTimeType(type); return type; } }); } private void writeType(Type type) throws IOException{ int typeId = sharedTypes.get(type); if(typeId != -1){ out.write(SHARED_FLAG); printInteger(typeId); return; } doWriteType(type); sharedTypes.store(type); } private void writeBool(IBool bool) throws IOException{ out.write(BOOL_HEADER); if(bool.getValue()){ out.write(1); }else{ out.write(0); } } private void writeInteger(IInteger integer) throws IOException{ byte[] valueData = integer.getTwosComplementRepresentation(); int length = valueData.length; out.write(BIG_INTEGER_HEADER); printInteger(length); out.write(valueData, 0, length); } /** * Format: * header * length of numerator * numerator byte[] * length of denominator * denominator byte[] */ private void writeRational(IRational rational) throws IOException{ out.write(RATIONAL_HEADER); byte[] valueData = rational.numerator().getTwosComplementRepresentation(); int length = valueData.length; printInteger(length); out.write(valueData, 0, length); valueData = rational.denominator().getTwosComplementRepresentation(); length = valueData.length; printInteger(length); out.write(valueData, 0, length); } private void writeDouble(IReal real) throws IOException{ out.write(DOUBLE_HEADER); byte[] valueData = real.unscaled().getTwosComplementRepresentation(); int length = valueData.length; printInteger(length); out.write(valueData, 0, length); printInteger(real.scale()); } private void writeString(IString string) throws IOException{ out.write(STRING_HEADER); String theString = string.getValue(); byte[] stringData = theString.getBytes(CharEncoding); printInteger(stringData.length); out.write(stringData); } private void writeSourceLocation(ISourceLocation sourceLocation) throws IOException{ URI uri = sourceLocation.getURI(); String path = uri.toString(); int id = sharedPaths.store(path); int header = SOURCE_LOCATION_HEADER; if(id == -1){ out.write(header); byte[] pathData = path.getBytes(CharEncoding); printInteger(pathData.length); out.write(pathData); } else{ out.write(header | URL_SHARED_FLAG); printInteger(id); } int beginLine, beginColumn, endLine, endColumn; if (!sourceLocation.hasLineColumn()) { beginLine = -1; endLine = -1; beginColumn = -1; endColumn = -1; } else { beginLine = sourceLocation.getBeginLine(); endLine = sourceLocation.getEndLine(); beginColumn = sourceLocation.getBeginColumn(); endColumn = sourceLocation.getEndColumn(); } int offset, length; if (!sourceLocation.hasOffsetLength()) { offset = -1; length = -1; } else { offset = sourceLocation.getOffset(); length = sourceLocation.getLength(); } printInteger(offset); printInteger(length); printInteger(beginLine); printInteger(endLine); printInteger(beginColumn); printInteger(endColumn); } private void writeDateTime(IDateTime dateTime) throws IOException{ out.write(DATE_TIME_HEADER); if(dateTime.isDateTime()){ out.write(DATE_TIME_INDICATOR); printInteger(dateTime.getYear()); printInteger(dateTime.getMonthOfYear()); printInteger(dateTime.getDayOfMonth()); printInteger(dateTime.getHourOfDay()); printInteger(dateTime.getMinuteOfHour()); printInteger(dateTime.getSecondOfMinute()); printInteger(dateTime.getMillisecondsOfSecond()); printInteger(dateTime.getTimezoneOffsetHours()); printInteger(dateTime.getTimezoneOffsetMinutes()); }else if(dateTime.isDate()){ out.write(DATE_INDICATOR); printInteger(dateTime.getYear()); printInteger(dateTime.getMonthOfYear()); printInteger(dateTime.getDayOfMonth()); }else{ out.write(TIME_INDICATOR); printInteger(dateTime.getHourOfDay()); printInteger(dateTime.getMinuteOfHour()); printInteger(dateTime.getSecondOfMinute()); printInteger(dateTime.getMillisecondsOfSecond()); printInteger(dateTime.getTimezoneOffsetHours()); printInteger(dateTime.getTimezoneOffsetMinutes()); } } private void writeTuple(ITuple tuple) throws IOException{ out.write(TUPLE_HEADER); int arity = tuple.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(tuple.get(i)); } } private void writeNode(INode node) throws IOException{ String nodeName = node.getName(); int nodeNameId = sharedNames.store(nodeName); if(nodeNameId == -1){ out.write(NODE_HEADER); byte[] nodeData = nodeName.getBytes(CharEncoding); printInteger(nodeData.length); out.write(nodeData); }else{ out.write(NODE_HEADER | NAME_SHARED_FLAG); printInteger(nodeNameId); } int arity = node.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(node.get(i)); } } private void writeKeywordedNode(INode node) throws IOException{ String nodeName = node.getName(); int nodeNameId = sharedNames.store(nodeName); if(nodeNameId == -1){ out.write(KEYWORDED_NODE_HEADER); byte[] nodeData = nodeName.getBytes(CharEncoding); printInteger(nodeData.length); out.write(nodeData); }else{ out.write(KEYWORDED_NODE_HEADER | NAME_SHARED_FLAG); printInteger(nodeNameId); } int arity = node.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(node.get(i)); } Map<String, IValue> kwParams = node.asWithKeywordParameters().getParameters(); printInteger(kwParams.size()); for (Map.Entry<String, IValue> param : kwParams.entrySet()) { String label = param.getKey(); byte[] labelData = label.getBytes(CharEncoding); printInteger(labelData.length); out.write(labelData); IValue value = param.getValue(); doSerialize(value); } } private void writeAnnotatedNode(INode node) throws IOException{ String nodeName = node.getName(); int nodeNameId = sharedNames.store(nodeName); if(nodeNameId == -1){ out.write(ANNOTATED_NODE_HEADER); byte[] nodeData = nodeName.getBytes(CharEncoding); printInteger(nodeData.length); out.write(nodeData); }else{ out.write(ANNOTATED_NODE_HEADER | NAME_SHARED_FLAG); printInteger(nodeNameId); } int arity = node.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(node.get(i)); } Map<String, IValue> annotations = node.asAnnotatable().getAnnotations(); printInteger(annotations.size()); Iterator<Map.Entry<String, IValue>> annotationsIterator = annotations.entrySet().iterator(); while(annotationsIterator.hasNext()){ Map.Entry<String, IValue> annotation = annotationsIterator.next(); String label = annotation.getKey(); byte[] labelData = label.getBytes(CharEncoding); printInteger(labelData.length); out.write(labelData); IValue value = annotation.getValue(); doSerialize(value); } } private void writeConstructor(IConstructor constructor) throws IOException{ Type constructorType = constructor.getUninstantiatedConstructorType(); int constructorTypeId = sharedTypes.get(constructorType); if(constructorTypeId == -1){ out.write(CONSTRUCTOR_HEADER); doWriteType(constructorType); sharedTypes.store(constructorType); }else{ out.write(CONSTRUCTOR_HEADER | TYPE_SHARED_FLAG); printInteger(constructorTypeId); } int arity = constructor.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(constructor.get(i)); } } private void writeKeywordedConstructor(IConstructor constructor) throws IOException{ Type constructorType = constructor.getConstructorType(); int constructorTypeId = sharedTypes.get(constructorType); if(constructorTypeId == -1){ out.write(KEYWORDED_CONSTRUCTOR_HEADER); doWriteType(constructorType); sharedTypes.store(constructorType); }else{ out.write(KEYWORDED_CONSTRUCTOR_HEADER | TYPE_SHARED_FLAG); printInteger(constructorTypeId); } int arity = constructor.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(constructor.get(i)); } Map<String, IValue> kwParams = constructor.asWithKeywordParameters().getParameters(); printInteger(kwParams.size()); for (Map.Entry<String, IValue> param: kwParams.entrySet()) { String label = param.getKey(); byte[] labelData = label.getBytes(CharEncoding); printInteger(labelData.length); out.write(labelData); IValue value = param.getValue(); doSerialize(value); } } private void writeAnnotatedConstructor(IConstructor constructor) throws IOException{ Type constructorType = constructor.getConstructorType(); int constructorTypeId = sharedTypes.get(constructorType); if(constructorTypeId == -1){ out.write(ANNOTATED_CONSTRUCTOR_HEADER); doWriteType(constructorType); sharedTypes.store(constructorType); }else{ out.write(ANNOTATED_CONSTRUCTOR_HEADER | TYPE_SHARED_FLAG); printInteger(constructorTypeId); } int arity = constructor.arity(); printInteger(arity); for(int i = 0; i < arity; i++){ doSerialize(constructor.get(i)); } Map<String, IValue> annotations = constructor.asAnnotatable().getAnnotations(); printInteger(annotations.size()); Iterator<Map.Entry<String, IValue>> annotationsIterator = annotations.entrySet().iterator(); while(annotationsIterator.hasNext()){ Map.Entry<String, IValue> annotation = annotationsIterator.next(); String label = annotation.getKey(); byte[] labelData = label.getBytes(CharEncoding); printInteger(labelData.length); out.write(labelData); IValue value = annotation.getValue(); doSerialize(value); } } private void writeList(IList list) throws IOException{ Type elementType = list.getElementType(); int elementTypeId = sharedTypes.get(elementType); if(elementTypeId == -1){ out.write(LIST_HEADER); doWriteType(elementType); sharedTypes.store(elementType); }else{ out.write(LIST_HEADER | TYPE_SHARED_FLAG); printInteger(elementTypeId); } int length = list.length(); printInteger(length); for(int i = 0; i < length; i++){ doSerialize(list.get(i)); } } private void writeSet(ISet set) throws IOException{ Type elementType = set.getElementType(); int elementTypeId = sharedTypes.get(elementType); if(elementTypeId == -1){ out.write(SET_HEADER); doWriteType(elementType); sharedTypes.store(elementType); }else{ out.write(SET_HEADER | TYPE_SHARED_FLAG); printInteger(elementTypeId); } printInteger(set.size()); Iterator<IValue> content = set.iterator(); while(content.hasNext()){ doSerialize(content.next()); } } private void writeMap(IMap map) throws IOException{ Type mapType = map.getType(); int mapTypeId = sharedTypes.get(mapType); if(mapTypeId == -1){ out.write(MAP_HEADER); doWriteType(mapType); sharedTypes.store(mapType); }else{ out.write(MAP_HEADER | TYPE_SHARED_FLAG); printInteger(mapTypeId); } printInteger(map.size()); Iterator<Map.Entry<IValue, IValue>> content = map.entryIterator(); while(content.hasNext()){ Map.Entry<IValue, IValue> entry = content.next(); doSerialize(entry.getKey()); doSerialize(entry.getValue()); } } private void writeValueType() throws IOException{ out.write(VALUE_TYPE_HEADER); } private void writeVoidType() throws IOException{ out.write(VOID_TYPE_HEADER); } private void writeBoolType() throws IOException{ out.write(BOOL_TYPE_HEADER); } private void writeIntegerType() throws IOException{ out.write(INTEGER_TYPE_HEADER); } private void writeNumType() throws IOException { out.write(NUM_TYPE_HEADER); } private void writeRationalType() throws IOException{ out.write(RATIONAL_TYPE_HEADER); } private void writeDoubleType() throws IOException{ out.write(DOUBLE_TYPE_HEADER); } private void writeStringType() throws IOException{ out.write(STRING_TYPE_HEADER); } private void writeSourceLocationType() throws IOException{ out.write(SOURCE_LOCATION_TYPE_HEADER); } private void writeDateTimeType(Type dateTimeType) throws IOException{ out.write(DATE_TIME_TYPE_HEADER); } private void writeNodeType(Type nodeType) throws IOException{ Map<String, Type> declaredAnnotations = typeStore.getAnnotations(nodeType); if(declaredAnnotations.isEmpty()){ out.write(NODE_TYPE_HEADER); }else{ out.write(ANNOTATED_NODE_TYPE_HEADER); // Annotations. int nrOfAnnotations = declaredAnnotations.size(); printInteger(nrOfAnnotations); Iterator<Map.Entry<String, Type>> declaredAnnotationsIterator = declaredAnnotations.entrySet().iterator(); while(declaredAnnotationsIterator.hasNext()){ Map.Entry<String, Type> declaredAnnotation = declaredAnnotationsIterator.next(); String label = declaredAnnotation.getKey(); byte[] labelBytes = label.getBytes(CharEncoding); printInteger(labelBytes.length); out.write(labelBytes); writeType(declaredAnnotation.getValue()); } } } private void writeTupleType(Type tupleType) throws IOException{ boolean hasFieldNames = tupleType.hasFieldNames(); if(hasFieldNames){ out.write(TUPLE_TYPE_HEADER | HAS_FIELD_NAMES); int arity = tupleType.getArity(); printInteger(arity); for(int i = 0; i < arity; i++){ writeType(tupleType.getFieldType(i)); String name = tupleType.getFieldName(i); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); } }else{ out.write(TUPLE_TYPE_HEADER); int arity = tupleType.getArity(); printInteger(arity); for(int i = 0; i < arity; i++){ writeType(tupleType.getFieldType(i)); } } } private void writeListType(Type listType) throws IOException{ out.write(LIST_TYPE_HEADER); writeType(listType.getElementType()); } private void writeSetType(Type setType) throws IOException{ out.write(SET_TYPE_HEADER); writeType(setType.getElementType()); } private void writeMapType(Type mapType) throws IOException{ boolean hasFieldNames = mapType.hasFieldNames(); if(hasFieldNames){ out.write(MAP_TYPE_HEADER | HAS_FIELD_NAMES); String name; byte[] nameData; writeType(mapType.getKeyType()); name = mapType.getKeyLabel(); nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(mapType.getValueType()); name = mapType.getValueLabel(); nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); } else { out.write(MAP_TYPE_HEADER); writeType(mapType.getKeyType()); writeType(mapType.getValueType()); } } private void writeParameterType(Type parameterType) throws IOException{ out.write(PARAMETER_TYPE_HEADER); String name = parameterType.getName(); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(parameterType.getBound()); } private void writeADTType(Type adtType) throws IOException{ out.write(ADT_TYPE_HEADER); String name = adtType.getName(); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(adtType.getTypeParameters()); } private void writeConstructorType(Type constructorType) throws IOException{ Map<String, Type> declaredAnnotations = typeStore.getAnnotations(constructorType); if(declaredAnnotations.isEmpty()){ out.write(CONSTRUCTOR_TYPE_HEADER); String name = constructorType.getName(); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(constructorType.getFieldTypes()); writeType(constructorType.getAbstractDataType()); }else{ out.write(ANNOTATED_CONSTRUCTOR_TYPE_HEADER); String name = constructorType.getName(); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(constructorType.getFieldTypes()); writeType(constructorType.getAbstractDataType()); // Annotations. int nrOfAnnotations = declaredAnnotations.size(); printInteger(nrOfAnnotations); Iterator<Map.Entry<String, Type>> declaredAnnotationsIterator = declaredAnnotations.entrySet().iterator(); while(declaredAnnotationsIterator.hasNext()){ Map.Entry<String, Type> declaredAnnotation = declaredAnnotationsIterator.next(); String label = declaredAnnotation.getKey(); byte[] labelBytes = label.getBytes(CharEncoding); printInteger(labelBytes.length); out.write(labelBytes); writeType(declaredAnnotation.getValue()); } } } private void writeAliasType(Type aliasType) throws IOException{ out.write(ALIAS_TYPE_HEADER); String name = aliasType.getName(); byte[] nameData = name.getBytes(CharEncoding); printInteger(nameData.length); out.write(nameData); writeType(aliasType.getAliased()); writeType(aliasType.getTypeParameters()); } private final static int SEVENBITS = 0x0000007f; private final static int SIGNBIT = 0x00000080; private void printInteger(int value) throws IOException{ int intValue = value; if((intValue & 0xffffff80) == 0){ out.write((byte) (intValue & SEVENBITS)); return; } out.write((byte) ((intValue & SEVENBITS) | SIGNBIT)); if((intValue & 0xffffc000) == 0){ out.write((byte) ((intValue >>> 7) & SEVENBITS)); return; } out.write((byte) (((intValue >>> 7) & SEVENBITS) | SIGNBIT)); if((intValue & 0xffe00000) == 0){ out.write((byte) ((intValue >>> 14) & SEVENBITS)); return; } out.write((byte) (((intValue >>> 14) & SEVENBITS) | SIGNBIT)); if((intValue & 0xf0000000) == 0){ out.write((byte) ((intValue >>> 21) & SEVENBITS)); return; } out.write((byte) (((intValue >>> 21) & SEVENBITS) | SIGNBIT)); out.write((byte) ((intValue >>> 28) & SEVENBITS)); } }