// Copyright 2001, FreeHEP.
package org.freehep.util.io;
import java.io.IOException;
import java.io.OutputStream;
/**
* Class to write Tagged blocks to a Stream. The tagged blocks (Tags) contain a
* tagID and a Length, so that known and unknown tags (read with the
* TaggedInputStream) can again be written. The stream also allows to write
* Actions, which again come with a actionCode and a length.
*
* A concrete implementation of this stream should encode/write the TagHeader.
* All Concrete tags should be inherited from the Tag class and implement their
* write methods.
*
* @author Mark Donszelmann
* @author Charles Loomis
* @version $Id: TaggedOutputStream.java 10195 2006-12-15 20:32:24Z duns $
*/
public abstract class TaggedOutputStream extends ByteCountOutputStream
implements TaggedOutput {
/**
* Set of tags that can be used by this Stream
*/
protected TagSet tagSet;
/**
* Set of actions that can be used by this Stream
*/
protected ActionSet actionSet;
/**
* Create a Tagged Output stream.
*
* @param out stream to write
* @param tagSet allowable tag set
* @param actionSet allowable action set
*/
public TaggedOutputStream(OutputStream out, TagSet tagSet,
ActionSet actionSet) {
this(out, tagSet, actionSet, false);
}
/**
* Create a Tagged Output stream.
*
* @param out stream to write
* @param tagSet allowable tag set
* @param actionSet allowable action set
* @param littleEndian true if stream is little endian
*/
public TaggedOutputStream(OutputStream out, TagSet tagSet,
ActionSet actionSet, boolean littleEndian) {
super(out, littleEndian);
this.tagSet = tagSet;
this.actionSet = actionSet;
}
/**
* Writes the TagHeader, which includes a TagID and a length.
*
* @param header TagHeader to write
* @throws IOException if write fails
*/
protected abstract void writeTagHeader(TagHeader header) throws IOException;
/**
* Specifies tag alignment: 1 byte, 2 short, 4 int and 8 long.
*
* @return tag alignment
*/
protected int getTagAlignment() {
return 1;
}
/*
* Write a tag.
*/
public void writeTag(Tag tag) throws IOException {
int tagID = tag.getTag();
if (!tagSet.exists(tagID))
throw new UndefinedTagException(tagID);
pushBuffer();
tag.write(tagID, this);
int align = getTagAlignment();
int pad = (align - (getBufferLength() % align)) % align;
for (int i = 0; i < pad; i++) {
write(0);
}
int len = popBuffer();
TagHeader header = createTagHeader(tag, len);
writeTagHeader(header);
append();
}
/**
* Returns newly created TagHeader. The default implementation
* creates a tagHeader from tagID and length. This method is
* called "after" the tag information is written, but the
* tag header is inserted before the tag info into the stream.
* Its called after since it needs the length of the tag info.
*/
protected TagHeader createTagHeader(Tag tag, long len) {
return new TagHeader(tag.getTag(), len);
}
/**
* Writes the ActionHeader, which includes an actionCode and a length.
*
* @param header ActionHeader to write
* @throws IOException if write fails
*/
protected abstract void writeActionHeader(ActionHeader header)
throws IOException;
/**
* Write action.
*
* @param action action to write
* @throws IOException if write fails
*/
public void writeAction(Action action) throws IOException {
// handle end of action stream
if (action == null) {
writeByte(0);
return;
}
int actionCode = action.getCode();
if (!actionSet.exists(actionCode))
throw new UndefinedTagException(actionCode);
pushBuffer();
action.write(actionCode, this);
int len = popBuffer();
ActionHeader header = new ActionHeader(actionCode, len);
writeActionHeader(header);
append();
}
}