001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.util;
020    
021    import org.apache.shiro.codec.Base64;
022    import org.apache.shiro.codec.CodecSupport;
023    import org.apache.shiro.codec.Hex;
024    
025    import java.io.File;
026    import java.io.InputStream;
027    import java.util.Arrays;
028    
029    /**
030     * Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the
031     * {@link Hex Hex} and {@link Base64 Base64} codec classes to support the
032     * {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations.
033     * <p/>
034     * The constructors on this class accept the following implicit byte-backed data types and will convert them to
035     * a byte-array automatically:
036     * <ul>
037     * <li>byte[]</li>
038     * <li>char[]</li>
039     * <li>String</li>
040     * <li>{@link ByteSource ByteSource}</li>
041     * <li>{@link File File}</li>
042     * <li>{@link InputStream InputStream}</li>
043     * </ul>
044     *
045     * @since 1.0
046     */
047    public class SimpleByteSource implements ByteSource {
048    
049        private final byte[] bytes;
050        private String cachedHex;
051        private String cachedBase64;
052    
053        public SimpleByteSource(byte[] bytes) {
054            this.bytes = bytes;
055        }
056    
057        /**
058         * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding).
059         *
060         * @param chars the source characters to use to create the underlying byte array.
061         * @since 1.1
062         */
063        public SimpleByteSource(char[] chars) {
064            this.bytes = CodecSupport.toBytes(chars);
065        }
066    
067        /**
068         * Creates an instance by converting the String to a byte array (assumes UTF-8 encoding).
069         *
070         * @param string the source string to convert to a byte array (assumes UTF-8 encoding).
071         * @since 1.1
072         */
073        public SimpleByteSource(String string) {
074            this.bytes = CodecSupport.toBytes(string);
075        }
076    
077        /**
078         * Creates an instance using the sources bytes directly - it does not create a copy of the
079         * argument's byte array.
080         *
081         * @param source the source to use to populate the underlying byte array.
082         * @since 1.1
083         */
084        public SimpleByteSource(ByteSource source) {
085            this.bytes = source.getBytes();
086        }
087    
088        /**
089         * Creates an instance by converting the file to a byte array.
090         *
091         * @param file the file from which to acquire bytes.
092         * @since 1.1
093         */
094        public SimpleByteSource(File file) {
095            this.bytes = new BytesHelper().getBytes(file);
096        }
097    
098        /**
099         * Creates an instance by converting the stream to a byte array.
100         *
101         * @param stream the stream from which to acquire bytes.
102         * @since 1.1
103         */
104        public SimpleByteSource(InputStream stream) {
105            this.bytes = new BytesHelper().getBytes(stream);
106        }
107    
108        /**
109         * Returns {@code true} if the specified object is a recognized data type that can be easily converted to
110         * bytes by instances of this class, {@code false} otherwise.
111         * <p/>
112         * This implementation returns {@code true} IFF the specified object is an instance of one of the following
113         * types:
114         * <ul>
115         * <li>{@code byte[]}</li>
116         * <li>{@code char[]}</li>
117         * <li>{@link ByteSource}</li>
118         * <li>{@link String}</li>
119         * <li>{@link File}</li>
120         * </li>{@link InputStream}</li>
121         * </ul>
122         *
123         * @param o the object to test to see if it can be easily converted to bytes by instances of this class.
124         * @return {@code true} if the specified object can be easily converted to bytes by instances of this class,
125         *         {@code false} otherwise.
126         * @since 1.2
127         */
128        public static boolean isCompatible(Object o) {
129            return o instanceof byte[] || o instanceof char[] || o instanceof String ||
130                    o instanceof ByteSource || o instanceof File || o instanceof InputStream;
131        }
132    
133        public byte[] getBytes() {
134            return this.bytes;
135        }
136    
137        public boolean isEmpty() {
138            return this.bytes == null || this.bytes.length == 0;
139        }
140    
141        public String toHex() {
142            if ( this.cachedHex == null ) {
143                this.cachedHex = Hex.encodeToString(getBytes());
144            }
145            return this.cachedHex;
146        }
147    
148        public String toBase64() {
149            if ( this.cachedBase64 == null ) {
150                this.cachedBase64 = Base64.encodeToString(getBytes());
151            }
152            return this.cachedBase64;
153        }
154    
155        public String toString() {
156            return toBase64();
157        }
158    
159        public int hashCode() {
160            if (this.bytes == null || this.bytes.length == 0) {
161                return 0;
162            }
163            return Arrays.hashCode(this.bytes);
164        }
165    
166        public boolean equals(Object o) {
167            if (o == this) {
168                return true;
169            }
170            if (o instanceof ByteSource) {
171                ByteSource bs = (ByteSource) o;
172                return Arrays.equals(getBytes(), bs.getBytes());
173            }
174            return false;
175        }
176    
177        //will probably be removed in Shiro 2.0.  See SHIRO-203:
178        //https://issues.apache.org/jira/browse/SHIRO-203
179        private static final class BytesHelper extends CodecSupport {
180            public byte[] getBytes(File file) {
181                return toBytes(file);
182            }
183    
184            public byte[] getBytes(InputStream stream) {
185                return toBytes(stream);
186            }
187        }
188    }