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 */
019package org.apache.shiro.util;
020
021import org.apache.shiro.codec.Base64;
022import org.apache.shiro.codec.CodecSupport;
023import org.apache.shiro.codec.Hex;
024
025import java.io.File;
026import java.io.InputStream;
027import 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 */
047public 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}