blob: 78a68a99591cdf5db47c27eb03380cb808bae6f2 [file] [log] [blame]
/* $Id$
*
* Copyright 2007-2008 Cisco Systems Inc.
*
* Licensed 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 etch.bindings.java.transport.fmt.xml;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Stack;
import etch.bindings.java.msg.Field;
import etch.bindings.java.msg.Message;
import etch.bindings.java.msg.StructValue;
import etch.bindings.java.msg.Type;
import etch.bindings.java.msg.Validator;
import etch.bindings.java.msg.ValueFactory;
import etch.bindings.java.msg.Validator.Level;
import etch.bindings.java.transport.ArrayValue;
import etch.bindings.java.transport.TaggedDataOutput;
import etch.bindings.java.transport.fmt.TypeCode;
import etch.util.FlexBuffer;
import etch.util.core.xml.XmlParser.DefaultTagElement;
import etch.util.core.xml.XmlParser.TagElement;
/**
* Description of TaggedDataOutputStream.
*/
@SuppressWarnings("all")
final public class XmlTaggedDataOutput extends XmlTaggedData
implements TaggedDataOutput, XmlTags
{
public class FlexBufferOutputStream extends OutputStream
{
public FlexBufferOutputStream( FlexBuffer buf )
{
this.buf = buf;
}
private final FlexBuffer buf;
@Override
public void write( int b ) throws IOException
{
buf.put( b );
}
}
private static final byte VERSION = 1;
/**
* Constructs the XmlTaggedDataOutput.
*
* @param vf the value factory for the service.
* @param uri the uri used to construct the transport stack.
*/
public XmlTaggedDataOutput( ValueFactory vf, String uri )
{
super( vf );
level = vf.getLevel();
}
private final Level level;
private final Stack<TagElement> stack = new Stack<TagElement>();
public void writeMessage( Message msg, FlexBuffer buf ) throws IOException
{
try
{
startMessage( msg );
writeKeysAndValues( msg );
endMessage( msg, buf );
}
finally
{
stack.clear();
}
}
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();
TagElement te = writeValue( t.getValidator( f ), me.getValue() );
setFieldAttr( te, f );
}
}
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 )
{
TagElement te = new DefaultTagElement( MESSAGE_TAG );
te.setAttr( null, VERSION_ATTR, VERSION );
setTypeAttr( te, msg.type() );
setSizeAttr( te, msg.size() );
stack.push( te );
}
private void endMessage( Message msg, FlexBuffer buf ) throws IOException
{
TagElement te = stack.pop();
if (!stack.isEmpty())
throw new IOException( "end of message stack sequence error: "+stack );
te.toString( new OutputStreamWriter( new FlexBufferOutputStream( buf ) ) );
}
private void startStruct( StructValue struct )
{
// the caller has already created an element tag for us.
TagElement te = stack.peek();
setTypeAttr( te, struct.type() );
setSizeAttr( te, struct.size() );
}
private void endStruct( StructValue struct )
{
// the caller will pop this element off the stack
}
private void startArray( ArrayValue array )
{
// the caller has already created an element tag for us.
TagElement te = stack.peek();
byte type = array.typeCode();
setTypeCodeAttr( te, type );
if (type == TypeCode.CUSTOM)
setTypeAttr( te, array.customStructType() );
setDimAttr( te, array.dim() );
setSizeAttr( te, array.size() );
}
private void endArray( ArrayValue array )
{
// the caller will pop this element off the stack
}
private void setTypeAttr( TagElement te, Type type )
{
te.setAttr( null, TYPE_ATTR, type.getName() );
}
private void setSizeAttr( TagElement te, int size )
{
te.setAttr( null, SIZE_ATTR, size );
}
private void setDimAttr( TagElement te, int dim )
{
te.setAttr( null, DIM_ATTR, dim );
}
private void setTypeCodeAttr( TagElement te, byte tc )
{
te.setAttr( null, TYPE_CODE_ATTR, tc );
}
private void setFieldAttr( TagElement te, Field field )
{
te.setAttr( null, FIELD_ATTR, field.getName() );
}
//////////////////////////////////////////////
private TagElement writeValue( Validator validator, Object value )
{
// TODO Auto-generated method stub
return null;
}
private void writeStructElement( Field key, Object value ) throws IOException
{
TagElement te = stack.peek().addTag( ELEMENT_TAG );
te.setAttr( null, KEY_ATTR, key.getId() );
stack.push( te );
writeValue( value );
stack.pop();
}
private void writeArrayElement( Object value ) throws IOException
{
TagElement te = stack.peek().addTag( ELEMENT_TAG );
stack.push( te );
writeValue( value );
stack.pop();
}
@SuppressWarnings("deprecation")
private void writeValue( Object value ) throws IOException
{
// the caller has already created an element for us.
TagElement te = stack.peek();
int type = checkValue( value );
switch (type)
{
case TypeCode.NULL:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, NULL_TYPE );
break;
}
case TypeCode.BOOLEAN_FALSE:
case TypeCode.BOOLEAN_TRUE:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, BOOLEAN_TYPE );
te.addCdata( value.toString() );
break;
}
case TypeCode.BYTE:
case TypeCode.SHORT:
case TypeCode.INT:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, INTEGER_TYPE );
te.addCdata( value.toString() );
break;
}
case TypeCode.LONG:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, LONG_TYPE );
te.addCdata( value.toString() );
break;
}
case TypeCode.FLOAT:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, FLOAT_TYPE );
te.addCdata( value.toString() );
break;
}
case TypeCode.DOUBLE:
{
te.setAttr( null, ELEMENT_TYPE_ATTR, DOUBLE_TYPE );
te.addCdata( value.toString() );
break;
}
case TypeCode.BYTES:
{
byte[] b = (byte[]) value;
te.setAttr( null, ELEMENT_TYPE_ATTR, BYTES_TYPE );
te.addCdata( bytes2string( b ) );
break;
}
case TypeCode.EMPTY_STRING:
case TypeCode.STRING:
{
String s = (String) value;
te.setAttr( null, ELEMENT_TYPE_ATTR, STRING_TYPE );
te.addCdata( s );
break;
}
case TypeCode.ARRAY:
te.setAttr( null, ELEMENT_TYPE_ATTR, ARRAY_TYPE );
if (value instanceof ArrayValue)
writeArray( (ArrayValue) value, null );
else
writeArray( toArrayValue( value, null ), null );
break;
case TypeCode.STRUCT:
case TypeCode.CUSTOM:
{
StructValue struct = vf.exportCustomValue( value );
if (struct == null)
throw new UnsupportedOperationException( "unsupported type "+value.getClass() );
te.setAttr( null, ELEMENT_TYPE_ATTR, CUSTOM_TYPE );
writeStruct( struct );
break;
}
default:
throw new IllegalArgumentException( "unsupported type code "+type );
}
}
private String bytes2string( byte[] buf )
{
StringBuffer sb = new StringBuffer();
for (byte b: buf)
{
if (sb.length() > 0)
sb.append( ' ' );
sb.append( Integer.toString( b, 16 ) );
}
return sb.toString();
}
}