/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.mina.util;

import java.nio.ByteBuffer;

/**
 * Utility class for smart dumping {@link ByteBuffer}
 * 
 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
 */
public class ByteBufferDumper {
    /** Hex chars */
    private static final byte[] HEX_CHAR = new byte[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
            'C', 'D', 'E', 'F' };

    /**
     * Dump the content of the given ByteBuffer, up to a number of bytes. If the toAscii flag is set to
     * <code>true</code>, this method will try to convert the bytes to a String
     * 
     * @param buffer The buffer to dump
     * @param nbBytes The number of bytes to dump (-1 for all of them)
     * @param toAscii If we want to write the message as a String
     * @return A dump of this ByteBuffer
     */
    public static String dump(ByteBuffer buffer, int nbBytes, boolean toAscii) {
        byte data[];
        int start;
        int size = Math.min(buffer.remaining(), nbBytes >= 0 ? nbBytes : Integer.MAX_VALUE);
        int length = buffer.remaining();

        if (buffer.hasArray() && !buffer.isReadOnly()) {
            start = buffer.position();
            data = buffer.array();
        } else {
            data = new byte[size];

            int oldpos = buffer.position();
            buffer.get(data);
            buffer.position(oldpos);

            start = 0;
            length = data.length;
        }

        // is not ASCII printable ?
        boolean binaryContent = false;

        if (toAscii) {
            for (int i = start; i < start + size; i++) {
                byte b = data[i];

                if (((b < 32) || (b > 126)) && (b != 13) && (b != 10)) {
                    binaryContent = true;
                    break;
                }
            }
        }

        if (!toAscii || binaryContent) {
            StringBuilder out = new StringBuilder(size * 3 + 30);
            out.append("ByteBuffer[len=").append(length).append(",bytes='");

            // fill the first
            int byteValue = data[start] & 0xFF;
            boolean isFirst = true;

            // and the others, too
            for (int i = start; i < start + size; i++) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    out.append(' ');
                }

                byteValue = data[i] & 0xFF;
                out.append(new String(new byte[] { '0', 'x', HEX_CHAR[(byteValue & 0x00F0) >> 4],
                        HEX_CHAR[byteValue & 0x000F] }));
            }

            out.append("']");

            return out.toString();

        } else {
            StringBuilder sb = new StringBuilder(size);
            sb.append("ByteBuffer[len=").append(length).append(",str='").append(new String(data, start, size))
                    .append("']");

            return sb.toString();
        }
    }

    /**
     * Dumps the given buffer. If the buffer contains only ascii, it will write the buffer content as a String.
     * 
     * @param buffer The buffer to dump
     * @return A string representing the buffer content
     */
    public static String dump(ByteBuffer buffer) {
        return dump(buffer, -1, true);
    }

    /**
     * Dump a byte buffer remaining bytes as a hex-decimal string. The current buffer position will remain unchanged.
     * 
     * @param buffer the buffer to dump
     * @return a hex string
     */
    public static String toHex(ByteBuffer buffer) {
        StringBuilder out = new StringBuilder(buffer.remaining() * 2);
        int pos = buffer.position();
        while (buffer.hasRemaining()) {
            int byteValue = buffer.get() & 0xFF;
            out.append((char) (HEX_CHAR[(byteValue & 0x00F0) >> 4]))
                    .append((char) ((HEX_CHAR[byteValue & 0x000F]) - 0));
        }
        buffer.position(pos);
        return out.toString();
    }

    public static ByteBuffer fromHexString(String hex) {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException("the hexa-decimal string length cannot be odd");
        }
        int size = hex.length() / 2;
        ByteBuffer res = ByteBuffer.allocate(size);

        for (int i = 0; i < size; i++) {
            int b = Integer.parseInt(hex.substring(i, i + 2), 16);
            if (Integer.highestOneBit(b) == 128) {
              b = b - 256;
            }
            res.put((byte) b);
        }

        res.flip();
        return res;
    }
}

@@ -5108,19 +5108,23 @@
 string(i
+*2
 , i
+*2
  + 2), 1

Fix for DIRMINA-1008: fix broken algorithm