183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com/*
2a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * Copyright (C) 2007 The Android Open Source Project
383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com *
4a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * Licensed under the Apache License, Version 2.0 (the "License");
5a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * you may not use this file except in compliance with the License.
6a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * You may obtain a copy of the License at
783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com *
8a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com *      http://www.apache.org/licenses/LICENSE-2.0
9a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com *
10a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * Unless required by applicable law or agreed to in writing, software
11a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * distributed under the License is distributed on an "AS IS" BASIS,
12a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * See the License for the specific language governing permissions and
14a7139f6586c9bb8452e4c648ce582f8fbc626740JesusFreke@JesusFreke.com * limitations under the License.
1583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com */
1683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
17128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com/*
18128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com * As per the Apache license requirements, this file has been modified
19128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com * from its original state.
20128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com *
21128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
22128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com * under the original license
23128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com */
24128e8279c3cf44cc1d1c8f263035ba8e4044d5c6JesusFreke@JesusFreke.com
2583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.compackage org.jf.dexlib.Util;
2683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
2783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.comimport java.io.IOException;
2883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.comimport java.io.Writer;
2983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.comimport java.util.ArrayList;
3083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
3183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com/**
3283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com * Implementation of {@link AnnotatedOutput} which stores the written data
3383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com * into a <code>byte[]</code>.
349ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com *
3583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
3683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com * writes all use little-endian order.</p>
3783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com */
3883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.compublic final class ByteArrayAnnotatedOutput
3983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        implements AnnotatedOutput {
4083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** default size for stretchy instances */
4183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private static final int DEFAULT_SIZE = 1000;
429ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com
4383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
4483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * whether the instance is stretchy, that is, whether its array
4583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * may be resized to increase capacity
4683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
4783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private final boolean stretchy;
4883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
4983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** non-null; the data itself */
5083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private byte[] data;
5183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
5283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** &gt;= 0; current output cursor */
5383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private int cursor;
5483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
5583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** whether annotations are to be verbose */
5683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private boolean verbose;
5783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
5883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
5983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * null-ok; list of annotations, or <code>null</code> if this instance
609ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     * isn't keeping them
6183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
6283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private ArrayList<Annotation> annotations;
6383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
6483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** &gt;= 40 (if used); the desired maximum annotation width */
6583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private int annotationWidth;
6683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
6783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
6883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * &gt;= 8 (if used); the number of bytes of hex output to use
699ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     * in annotations
7083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
7183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private int hexCols;
7283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
7383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private int currentIndent = 0;
7483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private int indentAmount = 2;
7583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
7683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
7783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Constructs an instance with a fixed maximum size. Note that the
7883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * given array is the only one that will be used to store data. In
7983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * particular, no reallocation will occur in order to expand the
8083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * capacity of the resulting instance. Also, the constructed
8183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * instance does not keep annotations by default.
829ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
8383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param data non-null; data array to use for output
8483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
8583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public ByteArrayAnnotatedOutput(byte[] data) {
8683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this(data, false);
8783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
8883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
8983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
9083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Constructs a "stretchy" instance. The underlying array may be
9183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * reallocated. The constructed instance does not keep annotations
9283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * by default.
9383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
9483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public ByteArrayAnnotatedOutput() {
9583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this(new byte[DEFAULT_SIZE], true);
9683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
9783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
9883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
9983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Internal constructor.
1009ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
10183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param data non-null; data array to use for output
10283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param stretchy whether the instance is to be stretchy
10383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
10483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
10583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (data == null) {
10683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new NullPointerException("data == null");
10783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
10883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
10983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.stretchy = stretchy;
11083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.data = data;
11183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.cursor = 0;
11283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.verbose = false;
11383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.annotations = null;
11483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.annotationWidth = 0;
11583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.hexCols = 0;
11683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
11783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
11883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
11983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Gets the underlying <code>byte[]</code> of this instance, which
12083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * may be larger than the number of bytes written
1219ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
12283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @see #toByteArray
1239ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
12483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @return non-null; the <code>byte[]</code>
12583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
12683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public byte[] getArray() {
12783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return data;
12883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
12983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
13083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
13183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Constructs and returns a new <code>byte[]</code> that contains
13283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * the written contents exactly (that is, with no extra unwritten
13383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * bytes at the end).
1349ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
13583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @see #getArray
1369ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
13783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @return non-null; an appropriately-constructed array
13883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
13983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public byte[] toByteArray() {
14083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        byte[] result = new byte[cursor];
14183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        System.arraycopy(data, 0, result, 0, cursor);
14283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return result;
14383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
14483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
14583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
14683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public int getCursor() {
14783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return cursor;
14883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
14983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
15083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
15183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void assertCursor(int expectedCursor) {
15283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (cursor != expectedCursor) {
15383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new ExceptionWithContext("expected cursor " +
15483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    expectedCursor + "; actual value: " + cursor);
15583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
15683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
15783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
15883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
15983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeByte(int value) {
16083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
16183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + 1;
16283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
16383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
16483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
16583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
16683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
16783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
16883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
16983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
17083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt] = (byte) value;
17183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
17283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
17383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
17483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
17583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeShort(int value) {
17683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
17783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + 2;
17883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
17983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
18083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
18183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
18283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
18383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
18483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
18583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
18683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt] = (byte) value;
18783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 1] = (byte) (value >> 8);
18883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
18983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
19083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
19183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
19283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeInt(int value) {
19383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
19483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + 4;
19583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
19683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
19783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
19883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
19983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
20083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
20183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
20283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
20383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt] = (byte) value;
20483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 1] = (byte) (value >> 8);
20583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 2] = (byte) (value >> 16);
20683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 3] = (byte) (value >> 24);
20783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
20883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
20983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
21083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
21183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeLong(long value) {
21283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
21383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + 8;
21483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
21583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
21683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
21783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
21883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
21983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
22083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
22183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
22283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int half = (int) value;
22383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt] = (byte) half;
22483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 1] = (byte) (half >> 8);
22583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 2] = (byte) (half >> 16);
22683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 3] = (byte) (half >> 24);
22783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
22883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        half = (int) (value >> 32);
22983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 4] = (byte) half;
23083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 5] = (byte) (half >> 8);
23183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 6] = (byte) (half >> 16);
23283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        data[writeAt + 7] = (byte) (half >> 24);
23383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
23483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
23583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
23683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
23783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
23883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public int writeUnsignedLeb128(int value) {
23983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        long remaining = (value & 0xFFFFFFFFL) >> 7;
24083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        long lValue = value;
24183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int count = 0;
24283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
24383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        while (remaining != 0) {
24483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            writeByte((int)(lValue & 0x7f) | 0x80);
24583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            lValue = remaining;
24683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            remaining >>= 7;
24783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            count++;
24883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
24983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
25083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        writeByte((int)(lValue & 0x7f));
25183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return count + 1;
25283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
25383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
25483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
25583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public int writeSignedLeb128(int value) {
25683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int remaining = value >> 7;
25783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int count = 0;
25883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        boolean hasMore = true;
25983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
26083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
26183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        while (hasMore) {
26283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            hasMore = (remaining != end)
26383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                || ((remaining & 1) != ((value >> 6) & 1));
26483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
26583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
26683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            value = remaining;
26783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            remaining >>= 7;
26883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            count++;
26983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
27083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
27183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return count;
27283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
2739ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com
27483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
27583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void write(ByteArray bytes) {
27683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int blen = bytes.size();
27783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
27883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + blen;
27983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
28083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
28183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
28283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
28383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
28483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
28583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
28683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
28783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        bytes.getBytes(data, writeAt);
28883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
28983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
29083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
29183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
29283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void write(byte[] bytes, int offset, int length) {
29383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int writeAt = cursor;
29483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = writeAt + length;
29583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int bytesEnd = offset + length;
29683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
29783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
29883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
29983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new IndexOutOfBoundsException("bytes.length " +
3009ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com                                                bytes.length + "; " +
30183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                                                offset + "..!" + end);
30283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
30383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
30483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
30583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
30683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
30783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
30883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
30983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
31083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
31183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        System.arraycopy(bytes, offset, data, writeAt, length);
31283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
31383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
31483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
31583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
31683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void write(byte[] bytes) {
31783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        write(bytes, 0, bytes.length);
31883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
31983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
32083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
32183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeZeroes(int count) {
32283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (count < 0) {
32383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new IllegalArgumentException("count < 0");
32483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
32583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
32683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = cursor + count;
32783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
32883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
32983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
33083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
33183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
33283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
33383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
33483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
33583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /*
33683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * There is no need to actually write zeroes, since the array is
33783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * already preinitialized with zeroes.
33883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
33983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
34083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
34183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
34283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
34383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
34483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void alignTo(int alignment) {
34583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int mask = alignment - 1;
34683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
34783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if ((alignment < 0) || ((mask & alignment) != 0)) {
34883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new IllegalArgumentException("bogus alignment");
34983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
35083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
35183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int end = (cursor + mask) & ~mask;
35283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
35383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (stretchy) {
35483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            ensureCapacity(end);
35583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (end > data.length) {
35683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throwBounds();
35783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
35883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
35983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
36083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /*
36183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * There is no need to actually write zeroes, since the array is
36283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * already preinitialized with zeroes.
36383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
36483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
36583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        cursor = end;
36683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
36783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
36883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
36983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public boolean annotates() {
37083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return (annotations != null);
37183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
37283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
37383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
37483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public boolean isVerbose() {
37583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return verbose;
37683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
37783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
37883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
37983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void annotate(String msg) {
38083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (annotations == null) {
38183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
38283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
38383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
38483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        endAnnotation();
38583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        annotations.add(new Annotation(cursor, msg, currentIndent));
38683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
38783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
38883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void indent() {
38983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        currentIndent++;
39083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
39183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
39283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void deindent() {
39383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        currentIndent--;
39483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (currentIndent < 0) {
39583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            currentIndent = 0;
39683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
39783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
39883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
39983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void setIndentAmount(int indentAmount) {
40083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.indentAmount = indentAmount;
40183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
40283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
40383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
40483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void annotate(int amt, String msg) {
40583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (annotations == null) {
40683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
40783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
40883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
40983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        endAnnotation();
41083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
41183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int asz = annotations.size();
41283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
41383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int startAt;
41483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
41583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (lastEnd <= cursor) {
41683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            startAt = cursor;
41783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else {
41883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            startAt = lastEnd;
41983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
42083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
42183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent));
42283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
42383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
42483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
42583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void endAnnotation() {
42683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (annotations == null) {
42783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return;
42883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
42983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
43083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int sz = annotations.size();
43183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
43283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (sz != 0) {
43383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            annotations.get(sz - 1).setEndIfUnset(cursor);
43483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
43583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
43683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
43783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /** {@inheritDoc} */
43883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public int getAnnotationWidth() {
43983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
44083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
44183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        return annotationWidth - leftWidth;
44283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
44383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
44483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
44583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Indicates that this instance should keep annotations. This method may
44683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * be called only once per instance, and only before any data has been
44783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * written to the it.
4489ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
44983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param annotationWidth &gt;= 40; the desired maximum annotation width
45083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param verbose whether or not to indicate verbose annotations
45183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
45283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void enableAnnotations(int annotationWidth, boolean verbose) {
45383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if ((annotations != null) || (cursor != 0)) {
45483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new RuntimeException("cannot enable annotations");
45583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
45683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
45783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (annotationWidth < 40) {
45883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            throw new IllegalArgumentException("annotationWidth < 40");
45983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
46083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
46183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
46283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (hexCols < 6) {
46383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            hexCols = 6;
46483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        } else if (hexCols > 10) {
46583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            hexCols = 10;
46683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
46783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
46883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.annotations = new ArrayList<Annotation>(1000);
46983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.annotationWidth = annotationWidth;
47083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.hexCols = hexCols;
47183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        this.verbose = verbose;
47283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
47383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
47483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
47583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Finishes up annotation processing. This closes off any open
47683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * annotations and removes annotations that don't refer to written
47783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * data.
47883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
47983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void finishAnnotating() {
48083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        // Close off the final annotation, if any.
48183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        endAnnotation();
48283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
48383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        // Remove annotations that refer to unwritten data.
48483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (annotations != null) {
48583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            int asz = annotations.size();
48683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            while (asz > 0) {
48783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                Annotation last = annotations.get(asz - 1);
48883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                if (last.getStart() > cursor) {
48983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    annotations.remove(asz - 1);
49083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    asz--;
49183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                } else if (last.getEnd() > cursor) {
49283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    last.setEnd(cursor);
49383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    break;
49483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                } else {
49583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                    break;
49683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                }
49783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            }
49883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
49983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
50083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
50183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
50283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Writes the annotated content of this instance to the given writer.
5039ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
50483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param out non-null; where to write to
50583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
50683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    public void writeAnnotationsTo(Writer out) throws IOException {
50783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int width2 = getAnnotationWidth();
50883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int width1 = annotationWidth - width2 - 1;
50983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
51083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        StringBuilder padding = new StringBuilder();
51183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        for (int i=0; i<1000; i++) {
51283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            padding.append(' ');
51383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
51483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
51583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
51683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        Writer left = twoc.getLeft();
51783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        Writer right = twoc.getRight();
51883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int leftAt = 0; // left-hand byte output cursor
51983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int rightAt = 0; // right-hand annotation index
52083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        int rightSz = annotations.size();
52183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
52283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        while ((leftAt < cursor) && (rightAt < rightSz)) {
52383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            Annotation a = annotations.get(rightAt);
52483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            int start = a.getStart();
52583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            int end;
52683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            String text;
52783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
52883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            if (leftAt < start) {
52983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                // This is an area with no annotation.
53083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                end = start;
53183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                start = leftAt;
53283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                text = "";
53383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            } else {
53483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                // This is an area with an annotation.
53583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                end = a.getEnd();
53683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText();
53783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                rightAt++;
53883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            }
53983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
54083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
54183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            right.write(text);
54283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            twoc.flush();
54383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            leftAt = end;
54483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
54583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
54683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (leftAt < cursor) {
54783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            // There is unannotated output at the end.
54883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
54983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                                hexCols, 6));
55083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
55183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
55283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        while (rightAt < rightSz) {
55383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            // There are zero-byte annotations at the end.
55483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            right.write(annotations.get(rightAt).getText());
55583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            rightAt++;
55683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
55783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
55883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        twoc.flush();
55983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
56083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
56183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
56283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Throws the excpetion for when an attempt is made to write past the
56383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * end of the instance.
56483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
56583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private static void throwBounds() {
56683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        throw new IndexOutOfBoundsException("attempt to write past the end");
56783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
56883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
56983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
57083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Reallocates the underlying array if necessary. Calls to this method
57183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * should be guarded by a test of {@link #stretchy}.
5729ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com     *
57383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * @param desiredSize &gt;= 0; the desired minimum total size of the array
57483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
57583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private void ensureCapacity(int desiredSize) {
57683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        if (data.length < desiredSize) {
57783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            byte[] newData = new byte[desiredSize * 2 + 1000];
57883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            System.arraycopy(data, 0, newData, 0, cursor);
57983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            data = newData;
58083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
58183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
58283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
58383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    /**
58483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     * Annotation on output.
58583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com     */
58683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    private static class Annotation {
58783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /** &gt;= 0; start of annotated range (inclusive) */
58883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        private final int start;
58983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
59083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
59183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * &gt;= 0; end of annotated range (exclusive);
5929ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         * <code>Integer.MAX_VALUE</code> if unclosed
59383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
59483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        private int end;
59583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
59683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /** non-null; annotation text */
59783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        private final String text;
59883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
59983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        private int indent;
60083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
60183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
60283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Constructs an instance.
6039ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
60483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param start &gt;= 0; start of annotated range
60583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param end &gt;= start; end of annotated range (exclusive) or
60683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * <code>Integer.MAX_VALUE</code> if unclosed
60783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param text non-null; annotation text
60883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
60983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public Annotation(int start, int end, String text, int indent) {
61083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this.start = start;
61183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this.end = end;
61283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this.text = text;
61383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this.indent = indent;
61483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
61583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
61683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
61783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Constructs an instance. It is initally unclosed.
6189ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
61983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param start &gt;= 0; start of annotated range
62083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param text non-null; annotation text
62183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
62283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public Annotation(int start, String text, int indent) {
62383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this(start, Integer.MAX_VALUE, text, indent);
62483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
62583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
62683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
62783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Sets the end as given, but only if the instance is unclosed;
62883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * otherwise, do nothing.
6299ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
63083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param end &gt;= start; the end
63183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
63283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public void setEndIfUnset(int end) {
63383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            if (this.end == Integer.MAX_VALUE) {
63483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com                this.end = end;
63583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            }
63683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
63783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
63883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
63983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Sets the end as given.
6409ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
64183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @param end &gt;= start; the end
64283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
64383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public void setEnd(int end) {
64483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            this.end = end;
64583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
64683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
64783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
64883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Gets the start.
6499ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
65083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @return the start
65183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
65283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public int getStart() {
65383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return start;
65483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
65583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
65683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
65783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Gets the end.
6589ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
65983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @return the end
66083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
66183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public int getEnd() {
66283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return end;
66383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
66483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
66583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        /**
66683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * Gets the text.
6679ab2b45ec8531658e3acf0b96b11a214ce8d3b60JesusFreke@JesusFreke.com         *
66883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         * @return non-null; the text
66983b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com         */
67083b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public String getText() {
67183b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return text;
67283b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
67383b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com
67483b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        public int getIndent() {
67583b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com            return indent;
67683b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com        }
67783b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com    }
67883b80f81d311b233188c281059aad4a9f5e8b4e6JesusFreke@JesusFreke.com}
679