Skip to content

Custom Tags

Ryandw11 edited this page Aug 19, 2020 · 1 revision

You can create custom tags in ODS. Here is how you do so.

Limitations

  • The id cannot be 0-15. (It is recommended you start at 20 and don't go into the negatives).

Creating the Tag

To create a tag just simply create a new class that implements Tag generic interface. In order to make a custom tag you must have a data type that you are "wrapping". In this example we will use a String and make a basic copy of the StringTag.

public class CustomTag implements Tag<String> {
    @Override
    public String getValue() {
        return null;
    }

    @Override
    public void setValue(String s) {

    }

    @Override
    public String getName() {
        return null;
    }

    @Override
    public void setName(String name) {

    }

    @Override
    public void writeData(DataOutputStream dos) throws IOException {

    }

    @Override
    public Tag<String> createFromData(ByteBuffer value, int length) {
        return null;
    }

    @Override
    public byte getID() {
        return 0;
    }
}

At the top of the class add a name and value variable.

private String name;
private String value;

Then add a constructor that takes in the name and value.
This constructor is required. The constructor can only have two parameters, name and value, in that order. The name must be a string and the value must be the same type that is described when you implement the Tag generic interface. In this example we implement Tag<String> so the type of value should be String.

public CustomTag(String name, String value) {
    this.name = name;
    this.value = value;
}

Editing the Methods

Now it is time to go through and edit the methods. The name and value getters and setters should be simple to implement.

writeData()

The write data method is called when ODS writes the tag to a file. In the write data method is a DataOutputStream parameter.

    @Override
    public void writeData(DataOutputStream dos) throws IOException {
        // Write out the custom tag ID
        dos.write(getID());

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        CountingOutputStream cos = new CountingOutputStream(os);
        DataOutputStream tempDos = new DataOutputStream(cos);

        tempDos.writeShort(name.getBytes(StandardCharsets.UTF_8).length);
        // Write the name.
        tempDos.write(name.getBytes(StandardCharsets.UTF_8));
        // write the value.
        tempDos.write(value.getBytes(StandardCharsets.UTF_8));

        dos.writeInt(cos.getCount());
        dos.write(os.toByteArray());

        // Close all of the temporary streams.
        cos.close();
        os.close();
        tempDos.close();
    }

dos.write(getID()); writes the id of the tag to the output.

In order to get the length of the tag in bytes we create a temporary output stream that counts the number of bytes inside of it.
CountingOutputStream is apart of the Apache Commons Library.

ByteArrayOutputStream os = new ByteArrayOutputStream();
CountingOutputStream cos = new CountingOutputStream(os);
DataOutputStream tempDos = new DataOutputStream(cos);

tempDos.writeShort(name.getBytes(StandardCharsets.UTF_8).length); This writes the length of the name. (It must be a short).
tempDos.write(name.getBytes(StandardCharsets.UTF_8)); Write the actual name of the tag.
tempDos.write(value.getBytes(StandardCharsets.UTF_8)); Write the actual value of the tag.

dos.writeInt(cos.getCount()); Write the number of bytes in the temporary output stream into the main output stream.
dos.write(os.toByteArray()); Write the temporary output stream into the main output stream.

The code below closes the temporary output stream. (Do not close the main output stream).

cos.close();
os.close();
tempDos.close();

Tip: If you want to write existing tags to the file you can by doing tag.writeData(tempDos); where tempDos.write(value.getBytes(StandardCharsets.UTF_8)); is.

createFromData()

This method creates the tag from the provided ByteBuffer.

    @Override
    public Tag<String> createFromData(ByteBuffer value, int length) {
        byte[] stringData = new byte[length];
        value.get(stringData);
        this.value = new String(stringData, StandardCharsets.UTF_8);
        return this;
    }

This method has two parameters (The value (in a ByteBuffer) and the length of the value (# of bytes).
Note: The provided ByteBuffer is the entire file, not just the tag data. Please only read the # of bytes specified by the length parameter. Reading any more data will trigger an exception.

Tip: If your tag is solely made up of pre-existing tags than you can use the ObjectDataStructure.getListData() static method to get a list of tags.

getId()

Finally, set the return value of getID() to whatever you want it to be (within the limitations).

 @Override
 public byte getID() {
     return 20;
 }

Final Example Code

/**
 * This is an example custom tag.
 */
public class CustomTag implements Tag<String> {

    /**
     * The name and value must be stored.
     */
    private String value;
    private String name;

    /**
     * This constructor MUST exist inorder for the custom tag to work.
     * The first parameter must be the name followed by the value. (name must be a string).
     */
    public CustomTag(String name, String value){
        this.name = name;
        this.value = value;
    }

    @Override
    public void setValue(String s) {
        this.value = s;
    }

    @Override
    public String getValue() {
        return value;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void writeData(DataOutputStream dos) throws IOException {
        // Write out the custom tag ID
        // The id is not included in the size of the tag.
        dos.write(getID());
        //Create a new DataOutputStream that way the size of the tag
        // can be counted.
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // This output stream is apart of the Apache Commons Library
        CountingOutputStream cos = new CountingOutputStream(os);
        DataOutputStream tempDos = new DataOutputStream(cos);

        // Write the size of the name (Must be a short)
        tempDos.writeShort(name.getBytes(StandardCharsets.UTF_8).length);
        // Write the name.
        tempDos.write(name.getBytes(StandardCharsets.UTF_8));
        // write the value.
        tempDos.write(value.getBytes(StandardCharsets.UTF_8));

        // write the size to the main output stream.
        dos.writeInt(cos.getCount());
        // write the temporary output stream to the main stream.
        dos.write(os.toByteArray());

        // Close all of the temporary streams. (NOT THE MAIN ONE)
        cos.close();
        os.close();
        tempDos.close();
    }

    @Override
    public Tag<String> createFromData(ByteBuffer value, int length) {
        // Only read [length] number of bytes. Reading any more will cause
        // an exception to occur.
        byte[] stringData = new byte[length];
        value.get(stringData);
        this.value = new String(stringData, StandardCharsets.UTF_8);
        return this;
    }

    @Override
    public byte getID() {
        return 20;
    }
}

Registering your Custom Tag

In order to use a custom tag in ODS you must register it. To do so just simply call the ODS.regiseterCustomTag() static method and pass in an instance of your custom tag.

ODS.registerCustomTag(new CustomTag("", ""));

Note: The instance of the custom tag does not need a name or value.

You can now use your custom tag like any other tag.