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 java.io.File;
022    import java.io.InputStream;
023    
024    /**
025     * A {@code ByteSource} wraps a byte array and provides additional encoding operations.  Most users will find the
026     * {@link Util} inner class sufficient to construct ByteSource instances.
027     *
028     * @since 1.0
029     */
030    public interface ByteSource {
031    
032        /**
033         * Returns the wrapped byte array.
034         *
035         * @return the wrapped byte array.
036         */
037        byte[] getBytes();
038    
039        /**
040         * Returns the <a href="http://en.wikipedia.org/wiki/Hexadecimal">Hex</a>-formatted String representation of the
041         * underlying wrapped byte array.
042         *
043         * @return the <a href="http://en.wikipedia.org/wiki/Hexadecimal">Hex</a>-formatted String representation of the
044         *         underlying wrapped byte array.
045         */
046        String toHex();
047    
048        /**
049         * Returns the <a href="http://en.wikipedia.org/wiki/Base64">Base 64</a>-formatted String representation of the
050         * underlying wrapped byte array.
051         *
052         * @return the <a href="http://en.wikipedia.org/wiki/Base64">Base 64</a>-formatted String representation of the
053         *         underlying wrapped byte array.
054         */
055        String toBase64();
056    
057        /**
058         * Returns {@code true} if the underlying wrapped byte array is null or empty (zero length), {@code false}
059         * otherwise.
060         *
061         * @return {@code true} if the underlying wrapped byte array is null or empty (zero length), {@code false}
062         *         otherwise.
063         * @since 1.2
064         */
065        boolean isEmpty();
066    
067        /**
068         * Utility class that can construct ByteSource instances.  This is slightly nicer than needing to know the
069         * {@code ByteSource} implementation class to use.
070         *
071         * @since 1.2
072         */
073        public static final class Util {
074    
075            /**
076             * Returns a new {@code ByteSource} instance representing the specified byte array.
077             *
078             * @param bytes the bytes to represent as a {@code ByteSource} instance.
079             * @return a new {@code ByteSource} instance representing the specified byte array.
080             */
081            public static ByteSource bytes(byte[] bytes) {
082                return new SimpleByteSource(bytes);
083            }
084    
085            /**
086             * Returns a new {@code ByteSource} instance representing the specified character array's bytes.  The byte
087             * array is obtained assuming {@code UTF-8} encoding.
088             *
089             * @param chars the character array to represent as a {@code ByteSource} instance.
090             * @return a new {@code ByteSource} instance representing the specified character array's bytes.
091             */
092            public static ByteSource bytes(char[] chars) {
093                return new SimpleByteSource(chars);
094            }
095    
096            /**
097             * Returns a new {@code ByteSource} instance representing the specified string's bytes.  The byte
098             * array is obtained assuming {@code UTF-8} encoding.
099             *
100             * @param string the string to represent as a {@code ByteSource} instance.
101             * @return a new {@code ByteSource} instance representing the specified string's bytes.
102             */
103            public static ByteSource bytes(String string) {
104                return new SimpleByteSource(string);
105            }
106    
107            /**
108             * Returns a new {@code ByteSource} instance representing the specified ByteSource.
109             *
110             * @param source the ByteSource to represent as a new {@code ByteSource} instance.
111             * @return a new {@code ByteSource} instance representing the specified ByteSource.
112             */
113            public static ByteSource bytes(ByteSource source) {
114                return new SimpleByteSource(source);
115            }
116    
117            /**
118             * Returns a new {@code ByteSource} instance representing the specified File's bytes.
119             *
120             * @param file the file to represent as a {@code ByteSource} instance.
121             * @return a new {@code ByteSource} instance representing the specified File's bytes.
122             */
123            public static ByteSource bytes(File file) {
124                return new SimpleByteSource(file);
125            }
126    
127            /**
128             * Returns a new {@code ByteSource} instance representing the specified InputStream's bytes.
129             *
130             * @param stream the InputStream to represent as a {@code ByteSource} instance.
131             * @return a new {@code ByteSource} instance representing the specified InputStream's bytes.
132             */
133            public static ByteSource bytes(InputStream stream) {
134                return new SimpleByteSource(stream);
135            }
136    
137            /**
138             * Returns {@code true} if the specified object can be easily represented as a {@code ByteSource} using
139             * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise.
140             * <p/>
141             * This implementation merely returns {@link SimpleByteSource}.{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}.
142             *
143             * @param source the object to test to see if it can be easily converted to ByteSource instances using default
144             *               heuristics.
145             * @return {@code true} if the specified object can be easily represented as a {@code ByteSource} using
146             *         the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise.
147             */
148            public static boolean isCompatible(Object source) {
149                return SimpleByteSource.isCompatible(source);
150            }
151    
152            /**
153             * Returns a {@code ByteSource} instance representing the specified byte source argument.  If the argument
154             * <em>cannot</em> be easily converted to bytes (as is indicated by the {@link #isCompatible(Object)} JavaDoc),
155             * this method will throw an {@link IllegalArgumentException}.
156             *
157             * @param source the byte-backed instance that should be represented as a {@code ByteSource} instance.
158             * @return a {@code ByteSource} instance representing the specified byte source argument.
159             * @throws IllegalArgumentException if the argument <em>cannot</em> be easily converted to bytes
160             *                                  (as indicated by the {@link #isCompatible(Object)} JavaDoc)
161             */
162            public static ByteSource bytes(Object source) throws IllegalArgumentException {
163                if (source == null) {
164                    return null;
165                }
166                if (!isCompatible(source)) {
167                    String msg = "Unable to heuristically acquire bytes for object of type [" +
168                            source.getClass().getName() + "].  If this type is indeed a byte-backed data type, you might " +
169                            "want to write your own ByteSource implementation to extract its bytes explicitly.";
170                    throw new IllegalArgumentException(msg);
171                }
172                if (source instanceof byte[]) {
173                    return bytes((byte[]) source);
174                } else if (source instanceof ByteSource) {
175                    return (ByteSource) source;
176                } else if (source instanceof char[]) {
177                    return bytes((char[]) source);
178                } else if (source instanceof String) {
179                    return bytes((String) source);
180                } else if (source instanceof File) {
181                    return bytes((File) source);
182                } else if (source instanceof InputStream) {
183                    return bytes((InputStream) source);
184                } else {
185                    throw new IllegalStateException("Encountered unexpected byte source.  This is a bug - please notify " +
186                            "the Shiro developer list asap (the isCompatible implementation does not reflect this " +
187                            "method's implementation).");
188                }
189            }
190        }
191    }