/*
 * Copyright (c) 2016 Jean Niklas L'orange. All rights reserved.
 *
 * The use and distribution terms for this software are covered by the
 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 * which can be found in the file LICENSE at the root of this distribution.
 *
 * By using this software in any fashion, you are agreeing to be bound by
 * the terms of this license.
 *
 * You must not remove this notice, or any other, from this software.
 */
package com.hypirion.bencode;

import java.io.*;
import java.util.*;

/**
 * A BencodeWriter writes Bencoded values into an {@link OutputStream}.
 *
 * @since 0.1.0
 */
public final class BencodeWriter implements Closeable {
    private OutputStream out;

    /**
     * Creates a new <code>BencodeWriter</code> out of <code>out</code>.
     *
     * @since 0.1.0
     * @param out the <code>OutputStream</code> to write to
     */
    public BencodeWriter(OutputStream out) {
        this.out = out;
    }

    /**
     * Closes the underlying <code>OutputStream</code>.
     *
     * @exception IOException if an IO exception occurs when closing
     */
    public void close() throws IOException {
        out.close();
    }

    /**
     * Writes a bencoded string to the <code>OutputStream</code>.
     *
     * @exception NullPointerException if <code>s</code> is <code>null</code>
     * @exception IOException if an IO exception occurs when writing
     */
    public void writeString(String s) throws IOException {
        byte[] bs = s.getBytes("UTF-8");
        byte[] bsLen = new Integer(bs.length).toString().getBytes("UTF-8");
        out.write(bsLen);
        out.write(':');
        out.write(bs);
    }

    /**
     * Writes a bencoded long to the <code>OutputStream</code>.
     *
     * @exception IOException if an IO exception occurs when writing
     */
    public void writeLong(long l) throws IOException {
        byte[] bs = new Long(l).toString().getBytes("UTF-8");
        out.write('i');
        out.write(bs);
        out.write('e');
    }

    /**
     * Writes a bencoded list to the <code>OutputStream</code>. The values in
     * the list must themselves be bencodeable.
     *
     * @exception IllegalArgumentException if an underlying value is not bencodable
     * @exception NullPointerException if <code>list</code> is <code>null</code>
     * @exception IOException if an IO exception occurs when writing
     */
    public void writeList(List<Object> list) throws IOException {
        out.write('l');
        for (Object elem : list) {
            write(elem);
        }
        out.write('e');
    }

    /**
     * Writes a bencoded map to the <code>OutputStream</code>. The values in
     * <code>map</code> must be bencodeable.
     *
     * @exception IllegalArgumentException if an underlying value is not bencodable
     * @exception NullPointerException if <code>map</code> is <code>null</code>,
     * or if any of the underlying key/values are null
     * @exception IOException if an IO exception occurs when writing
     */
    public void writeDict(Map<String, Object> map) throws IOException {
        out.write('d');
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            writeString(entry.getKey());
            write(entry.getValue());
        }
        out.write('e');
    }

    /**
     * Writes a bencoded value to the <code>OutputStream</code>. The value must
     * be bencodable.
     *
     * @exception IllegalArgumentException if <code>o</code> is not bencodable
     * @exception NullPointerException if <code>o</code> or any underlying
     * values in <code>o</code> are null
     * @exception IOException if an IO exception occurs when writing
     */
    public void write(Object o) throws IOException {
        if (o instanceof Long) {
            writeLong((Long) o);
        }
        else if (o instanceof Integer) {
            writeLong(((Integer) o).longValue());
        }
        else if (o instanceof String) {
            writeString((String) o);
        }
        // Do not support smaller types, at least not for now
        else if (o instanceof List) {
            writeList((List<Object>) o);
        }
        else if (o instanceof Map) {
            writeDict((Map<String,Object>) o);
        }
        else {
            String msg = String.format("Value must either be integer, string, list or map, was %s",
                                       o.getClass().getName());
            throw new IllegalArgumentException(msg);
        }
    }
}

@@ -696,16 +696,27 @@
 loseable
+, Flushable
  %7B%0A    p
@@ -1206,32 +1206,255 @@
 close();%0A    %7D%0A%0A
+    /**%0A     * Flushes the underlying %3Ccode%3EOutputStream%3C/code%3E.%0A     *%0A     * @exception IOException if an IO exception occurs when flushing%0A     */%0A    public void flush() throws IOException %7B%0A        out.flush();%0A    %7D%0A%0A
     /**%0A     * W

Add flushing for bencode writer