001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| 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+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.collections;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.util.Arrays;
024
025import org.conqat.lib.commons.string.StringUtils;
026
027/**
028 * A wrapper class around a byte array that supports a clean implementation of
029 * {@link #hashCode()} and {@link #equals(Object)}, so byte arrays can be used,
030 * e.g., in a HashMap. The class is immutable. Custom (de)serialization is
031 * provided to make this efficient to use in storage or RMI scenarios.
032 */
033public class ByteArrayWrapper implements Serializable, Comparable<ByteArrayWrapper> {
034
035        /** Version used for serialization. */
036        private static final long serialVersionUID = 1;
037
038        /** The wrapped array. */
039        protected byte[] array;
040
041        /** Constructor. */
042        public ByteArrayWrapper(byte[] array) {
043                this.array = array.clone();
044        }
045
046        /** Returns a copy of the internal byte representation. */
047        public byte[] getBytes() {
048                return array.clone();
049        }
050
051        /** {@inheritDoc} */
052        @Override
053        public int hashCode() {
054                return Arrays.hashCode(array);
055        }
056
057        /** {@inheritDoc} */
058        @Override
059        public boolean equals(Object obj) {
060                if (obj instanceof ByteArrayWrapper) {
061                        return Arrays.equals(array, ((ByteArrayWrapper) obj).array);
062                }
063                if (obj instanceof byte[]) {
064                        return Arrays.equals(array, ((byte[]) obj));
065                }
066                return false;
067        }
068
069        /** {@inheritDoc} */
070        @Override
071        public String toString() {
072                return StringUtils.encodeAsHex(array);
073        }
074
075        /** {@inheritDoc} */
076        @Override
077        public int compareTo(ByteArrayWrapper o) {
078                if (o == null) {
079                        return -1;
080                }
081
082                int lengthDelta = array.length - o.array.length;
083                if (lengthDelta != 0) {
084                        return lengthDelta;
085                }
086
087                for (int i = 0; i < array.length; ++i) {
088                        int delta = array[i] - o.array[i];
089                        if (delta != 0) {
090                                return delta;
091                        }
092                }
093
094                return 0;
095        }
096
097        /** Custom serialization. */
098        private void writeObject(ObjectOutputStream out) throws IOException {
099                out.writeInt(array.length);
100                out.write(array);
101        }
102
103        /** Custom deserialization. */
104        private void readObject(ObjectInputStream in) throws IOException {
105                int size = in.readInt();
106                array = new byte[size];
107                int pos = 0;
108                while (pos < size) {
109                        pos += in.read(array, pos, size - pos);
110                }
111        }
112
113        /** Comparator for {@link ByteArrayWrapper}. */
114        public static class Comparator implements java.util.Comparator<ByteArrayWrapper>, Serializable {
115
116                /** Serial version ID. */
117                private static final long serialVersionUID = 1;
118
119                /** {@inheritDoc} */
120                @Override
121                public int compare(ByteArrayWrapper o1, ByteArrayWrapper o2) {
122                        return o1.compareTo(o2);
123                }
124        }
125}