001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.io;
018    
019    import java.io.Serializable;
020    
021    /**
022     * Byte Order Mark (BOM) representation -
023     * see {@link org.apache.commons.io.input.BOMInputStream}.
024     *
025     * @see org.apache.commons.io.input.BOMInputStream
026     * @see <a href="http://en.wikipedia.org/wiki/Byte_order_mark">Wikipedia - Byte Order Mark</a>
027     * @version $Id: ByteOrderMark.java 1005099 2010-10-06 16:13:01Z niallp $
028     * @since Commons IO 2.0
029     */
030    public class ByteOrderMark implements Serializable {
031    
032        private static final long serialVersionUID = 1L;
033    
034        /** UTF-8 BOM */
035        public static final ByteOrderMark UTF_8    = new ByteOrderMark("UTF-8",    0xEF, 0xBB, 0xBF);
036        /** UTF-16BE BOM (Big Endian) */
037        public static final ByteOrderMark UTF_16BE = new ByteOrderMark("UTF-16BE", 0xFE, 0xFF);
038        /** UTF-16LE BOM (Little Endian) */
039        public static final ByteOrderMark UTF_16LE = new ByteOrderMark("UTF-16LE", 0xFF, 0xFE);
040    
041        private final String charsetName;
042        private final int[] bytes;
043    
044        /**
045         * Construct a new BOM.
046         *
047         * @param charsetName The name of the charset the BOM represents
048         * @param bytes The BOM's bytes
049         * @throws IllegalArgumentException if the charsetName is null or
050         * zero length
051         * @throws IllegalArgumentException if the bytes are null or zero
052         * length
053         */
054        public ByteOrderMark(String charsetName, int... bytes) {
055            if (charsetName == null || charsetName.length() == 0) {
056                throw new IllegalArgumentException("No charsetName specified");
057            }
058            if (bytes == null || bytes.length == 0) {
059                throw new IllegalArgumentException("No bytes specified");
060            }
061            this.charsetName = charsetName;
062            this.bytes = new int[bytes.length];
063            System.arraycopy(bytes, 0, this.bytes, 0, bytes.length);
064        }
065    
066        /**
067         * Return the name of the {@link java.nio.charset.Charset} the BOM represents.
068         *
069         * @return the character set name
070         */
071        public String getCharsetName() {
072            return charsetName;
073        }
074    
075        /**
076         * Return the length of the BOM's bytes.
077         *
078         * @return the length of the BOM's bytes
079         */
080        public int length() {
081            return bytes.length;
082        }
083    
084        /**
085         * The byte at the specified position.
086         *
087         * @param pos The position
088         * @return The specified byte
089         */
090        public int get(int pos) {
091            return bytes[pos];
092        }
093    
094        /**
095         * Return a copy of the BOM's bytes.
096         *
097         * @return a copy of the BOM's bytes
098         */
099        public byte[] getBytes() {
100            byte[] copy = new byte[bytes.length];
101            for (int i = 0; i < bytes.length; i++) {
102                copy[i] = (byte)bytes[i];
103            }
104            return copy;
105        }
106    
107        /**
108         * Indicates if this BOM's bytes equals another.
109         *
110         * @param obj The object to compare to
111         * @return true if the bom's bytes are equal, otherwise
112         * false
113         */
114        @Override
115        public boolean equals(Object obj) {
116            if (!(obj instanceof ByteOrderMark)) {
117                return false;
118            }
119            ByteOrderMark bom = (ByteOrderMark)obj;
120            if (bytes.length != bom.length()) {
121                return false;
122            }
123            for (int i = 0; i < bytes.length; i++) {
124                if (bytes[i] != bom.get(i)) {
125                    return false;
126                }
127            }
128            return true;
129        }
130    
131        /**
132         * Return the hashcode for this BOM.
133         *
134         * @return the hashcode for this BOM.
135         * @see java.lang.Object#hashCode()
136         */
137        @Override
138        public int hashCode() {
139            int hashCode = getClass().hashCode();
140            for (int b : bytes) {
141                hashCode += b;
142            }
143            return hashCode;
144        }
145    
146        /**
147         * Provide a String representation of the BOM.
148         *
149         * @return the length of the BOM's bytes
150         */
151        @Override
152        public String toString() {
153            StringBuilder builder = new StringBuilder();
154            builder.append(getClass().getSimpleName());
155            builder.append('[');
156            builder.append(charsetName);
157            builder.append(": ");
158            for (int i = 0; i < bytes.length; i++) {
159                if (i > 0) {
160                    builder.append(",");
161                }
162                builder.append("0x");
163                builder.append(Integer.toHexString(0xFF & bytes[i]).toUpperCase());
164            }
165            builder.append(']');
166            return builder.toString();
167        }
168    
169    }