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 java.io.File;
022import 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 */
030public 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}