blob: cee858d694d5bc9994c9d8b24ae08bde3e2db845 [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.etch.bindings.java.transport.fmt.binary;
import java.io.IOException;
import java.util.Map;
import org.apache.etch.bindings.java.msg.Field;
import org.apache.etch.bindings.java.msg.Message;
import org.apache.etch.bindings.java.msg.StructValue;
import org.apache.etch.bindings.java.msg.Type;
import org.apache.etch.bindings.java.msg.Validator;
import org.apache.etch.bindings.java.msg.ValueFactory;
import org.apache.etch.bindings.java.msg.Validator.Level;
import org.apache.etch.bindings.java.support.Validator_int;
import org.apache.etch.bindings.java.support.Validator_string;
import org.apache.etch.bindings.java.transport.ArrayValue;
import org.apache.etch.bindings.java.transport.TaggedDataOutput;
import org.apache.etch.bindings.java.transport.fmt.TypeCode;
import org.apache.etch.util.FlexBuffer;
import org.apache.etch.util.URL;
/**
* An implementation of TaggedDataOutput which uses a binary encoding.
*/
final public class BinaryTaggedDataOutput extends BinaryTaggedData
implements TaggedDataOutput
{
/**
* Name of uri parameter which controls whether we write ints or strings
* for types and fields.
*/
public static final String STRING_TYPE_AND_FIELD =
"BinaryTaggedDataOutput.stringTypeAndField";
/**
* Constructs the BinaryTaggedDataOutput.
*
* @param vf the value factory for the service.
* @param uri the uri used to construct the transport stack.
*/
public BinaryTaggedDataOutput( ValueFactory vf, String uri )
{
super( vf );
level = vf.getLevel();
URL u = new URL( uri );
stringTypeAndField = u.getBooleanTerm( STRING_TYPE_AND_FIELD, false );
}
private final Level level;
private final boolean stringTypeAndField;
//////////////////////////////
// TaggedDataOutput methods //
//////////////////////////////
public void writeMessage( Message msg, FlexBuffer buf ) throws IOException
{
try
{
this.buf = buf;
startMessage( msg );
writeKeysAndValues( msg );
endMessage( msg );
}
finally
{
this.buf = null;
}
}
private FlexBuffer buf;
private void writeStruct( StructValue sv ) throws IOException
{
startStruct( sv );
writeKeysAndValues( sv );
endStruct( sv );
}
private void writeArray( ArrayValue av, Validator v ) throws IOException
{
startArray( av );
writeValues( av, v );
endArray( av );
}
private void writeKeysAndValues( StructValue sv ) throws IOException
{
Type t = sv.type();
for (Map.Entry<Field, Object> me: sv)
{
Field f = me.getKey();
writeField( f );
writeValue( t.getValidator( f ), me.getValue() );
}
}
private void writeValues( ArrayValue av, Validator v )
throws IOException
{
Validator ev = v.elementValidator();
for (Object value: av)
writeValue( ev, value );
}
/////////////////////////
// Main output methods //
/////////////////////////
private void startMessage( Message msg ) throws IOException
{
// since message is top-level, no type code is written to
// indicate a message is starting. we do write a version
// number to indicate this version of the binary tagged
// data output format.
buf.putByte( VERSION );
startStruct( msg );
}
private void endMessage( Message msg ) throws IOException
{
endStruct( msg );
}
private void startStruct( StructValue struct ) throws IOException
{
// the caller has already written a type code to indicate a
// struct is starting. this code is shared by startMessage().
writeType( struct.type() );
writeIntValue( struct.size() );
}
private void endStruct( StructValue struct ) throws IOException
{
// this code is shared by endMessage.
writeNoneValue();
}
private void startArray( ArrayValue array ) throws IOException
{
// the caller has already written a type code to indicate an
// array is starting.
byte type = array.typeCode();
buf.putByte( type );
if (type == TypeCode.CUSTOM)
writeType( array.customStructType() );
writeIntValue( array.dim() );
writeIntValue( array.size() );
}
private void endArray( ArrayValue array ) throws IOException
{
writeNoneValue();
}
private void writeType( Type type ) throws IOException
{
if (stringTypeAndField)
writeStringValue( type.getName() );
else
writeIntValue( type.getId() );
}
private void writeField( Field field ) throws IOException
{
if (stringTypeAndField)
writeStringValue( field.getName() );
else
writeIntValue( field.getId() );
}
private void writeIntValue( Integer value ) throws IOException
{
writeValue( intValidator, value );
}
private final Validator intValidator = Validator_int.get( 0 );
private void writeStringValue( String value ) throws IOException
{
writeValue( stringValidator, value );
}
private final Validator stringValidator = Validator_string.get( 0 );
private void writeNoneValue() throws IOException
{
writeValue( noneValidator, NONE );
}
private final Validator noneValidator = Validator_none.get();
///////////////////////////
// LOCAL UTILITY METHODS //
///////////////////////////
/**
* @param v the validator for this value, or none if there
* or none if there isn't one.
* @param value
* @throws IOException
*/
private void writeValue( Validator v, Object value ) throws IOException
{
// System.out.printf( "writeValue %s: %s\n", value, v );
byte typeCode = checkValue( value );
if (level != Level.NONE && value != null)
{
if (level == Level.FULL && v == null)
throw new IllegalArgumentException(
"validator missing" );
if (v != null && !v.validate( value ))
throw new IllegalArgumentException( String.format(
"validator %s failed for value %s",
v, value ) );
}
buf.putByte( typeCode );
switch (typeCode)
{
case TypeCode.NULL:
case TypeCode.NONE:
case TypeCode.BOOLEAN_FALSE:
case TypeCode.BOOLEAN_TRUE:
case TypeCode.EMPTY_STRING:
return;
case TypeCode.BYTE:
buf.putByte( ((Number) value).byteValue() );
return;
case TypeCode.SHORT:
buf.putShort( ((Number) value).shortValue() );
return;
case TypeCode.INT:
buf.putInt( ((Number) value).intValue() );
return;
case TypeCode.LONG:
buf.putLong( ((Number) value).longValue() );
return;
case TypeCode.FLOAT:
buf.putFloat( ((Number) value).floatValue() );
return;
case TypeCode.DOUBLE:
buf.putDouble( ((Number) value).doubleValue() );
return;
case TypeCode.BYTES:
writeBytes( (byte[]) value );
return;
// reserved for future use:
// case TypeCode.BOOLS:
// case TypeCode.SHORTS:
// case TypeCode.INTS:
// case TypeCode.LONGS:
// case TypeCode.FLOATS:
// case TypeCode.DOUBLES:
// throw new UnsupportedOperationException( "unsupported type code "+type );
case TypeCode.ARRAY:
writeArray( toArrayValue( value, v ), v );
return;
case TypeCode.STRING:
writeBytes( ((String) value).getBytes( vf.getStringEncoding() ) );
return;
case TypeCode.CUSTOM:
{
StructValue struct = vf.exportCustomValue( value );
if (struct == null)
throw new UnsupportedOperationException( "unsupported type "+value.getClass() );
writeStruct( struct );
return;
}
default:
// type is either "tiny" integer or unused...
if (typeCode >= TypeCode.MIN_TINY_INT && typeCode <= TypeCode.MAX_TINY_INT)
return;
throw new UnsupportedOperationException( "unsupported type code "+typeCode );
}
}
/**
* Writes a value (signed or unsigned) to the output stream.
* @param value the value to write to the output stream.
* @throws IOException if there is a problem with the output stream.
*/
private void writeBytes( byte[] value ) throws IOException
{
int n = value.length;
writeIntValue( n );
buf.put( value, 0, n );
}
}