Util.java

// <editor-fold defaultstate="collapsed" desc="license">
/*
 * Copyright (c) 2009, Karl H. Beckers
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of the <ORGANIZATION> nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 **/
// </editor-fold>
package net.jarre_de_the.griffin;

import java.math.BigInteger;
import java.nio.charset.Charset;
import net.jarre_de_the.griffin.exception.ValueTooLargeException;
import net.jarre_de_the.griffin.types.data.DWordData;
import net.jarre_de_the.griffin.types.field.AbstractField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Various static utility methods.
 *
 * @todo implement the rest of the file types and writing them, too (special
 * implementations of GFF files)
 *
 * @todo equals on files is semantically, now ... consider making that more
 * strict and adding a method to normalize file objects (incl. recalculating
 * offsets etc.)
 * <p>
 * @author charly4711
 */
public class Util {

    private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);

    public static final Charset CHARSET_US_ASCII = Charset.forName("US-ASCII");

    /**
     * @see getNullTerminatedString(byte[], Charset) This version defaults to
     * US-ASCII
     *
     * @param buf
     * @return
     */
    public static String getNullTerminatedString(byte[] buf) {
        return getNullTerminatedString(buf, Util.CHARSET_US_ASCII);
    }

    /**
     * Returns a String from a byte array that's supposed to be character data
     * terminated by a zero-byte.(as e. g. in field labels)
     * <p>
     * @param buf The byte array with character data
     * @param charSet character set to use
     * @return A string that has all the bytes buf has up to the first zero-byte
     * read into characters. (or null if buff is null)
     */
    public static String getNullTerminatedString(byte[] buf, Charset charSet) {
        if (buf != null) {
            String l = new String(buf, charSet);
            int pos = l.indexOf(0);
            if (pos >= 0) {
                return l.substring(0, pos);
            } else {
                return l;
            }
        } else {
            return null;
        }
    }

    /**
     * Determines the number of bytes used for the type of number in question.
     *
     * @param clazz
     * @return
     */
    public static int getByteLength(Class<? extends Number> clazz) {
        int length = 0;
        if (null != clazz) {
            if (BigInteger.class.isAssignableFrom(clazz)) {
                length = Long.BYTES + 1;
            } else if (Long.class.isAssignableFrom(clazz)) {
                length = Long.BYTES;
            } else if (Double.class.isAssignableFrom(clazz)) {
                length = Double.BYTES;
            } else if (Float.class.isAssignableFrom(clazz)) {
                length = Float.BYTES;
            } else if (Integer.class.isAssignableFrom(clazz)) {
                length = Integer.BYTES;
            } else if (Short.class.isAssignableFrom(clazz)) {
                length = Short.BYTES;
            } else if (Byte.class.isAssignableFrom(clazz)) {
                // Byte
                length = Byte.BYTES;
            }
        }
        return length;
    }

    public static <T extends Number> int getUnsignedInt(T source) {
        if (null != source) {
            if (Float.class.isAssignableFrom(source.getClass())) {
                return Float.floatToIntBits((Float) source);
            } else if (Integer.class.isAssignableFrom(source.getClass())) {
                Integer intSource = (Integer) source;
                return intSource;
            } else if (Short.class.isAssignableFrom(source.getClass())) {
                int i = 0;
                i |= ((Short) source);
                return i;
            } else if (Byte.class.isAssignableFrom(source.getClass())) {
                int i = 0;
                i |= ((Byte) source);
                return i;
            } else {
                throw new ValueTooLargeException(4, 8);
            }
        } else {
            return 0;
        }
    }

    /**
     * Converts a String into its binary DWordData representation.
     *
     * @param s
     * @return
     */
    public static int stringToBitField(String s) {
        int result = 0;

        if (null != s) {
            if (s.length() > DWordData.LENGTH) {
                throw new ValueTooLargeException(DWordData.LENGTH, s.length());
            }
            char[] cBuf = s.toCharArray();
            for (int i = 24, n = cBuf.length - 1; n >= 0; n--, i -= 8) {
                byte b = (byte) (cBuf[n] & 0xFF);
                result |= (b << i);
            }
        }

        return result;
    }

    /**
     * Creates a byte array as used for a label from a String. Labels are always
     * treated as 7-bit ASCII.
     *
     * @param labelString the String to convert
     * @return a byte[] array of AbstractField.LABEL_LENGTH length
     */
    public static byte[] stringToLabelArray(String labelString) {
        byte[] result = new byte[AbstractField.LABEL_LENGTH];

        if (null != labelString) {
            byte[] stringToBytes = labelString.getBytes(Util.CHARSET_US_ASCII);

            if (stringToBytes.length > AbstractField.LABEL_LENGTH) {
                throw new ValueTooLargeException(AbstractField.LABEL_LENGTH,
                                                 stringToBytes.length);
            }

            System.arraycopy(stringToBytes, 0, result, 0, stringToBytes.length);
        }

        return result;
    }
}