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.lang.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    final class Util {
074
075        private Util() {
076        }
077
078        /**
079         * Returns a new {@code ByteSource} instance representing the specified byte array.
080         *
081         * @param bytes the bytes to represent as a {@code ByteSource} instance.
082         * @return a new {@code ByteSource} instance representing the specified byte array.
083         */
084        public static ByteSource bytes(byte[] bytes) {
085            return new SimpleByteSource(bytes);
086        }
087
088        /**
089         * Returns a new {@code ByteSource} instance representing the specified character array's bytes.  The byte
090         * array is obtained assuming {@code UTF-8} encoding.
091         *
092         * @param chars the character array to represent as a {@code ByteSource} instance.
093         * @return a new {@code ByteSource} instance representing the specified character array's bytes.
094         */
095        public static ByteSource bytes(char[] chars) {
096            return new SimpleByteSource(chars);
097        }
098
099        /**
100         * Returns a new {@code ByteSource} instance representing the specified string's bytes.  The byte
101         * array is obtained assuming {@code UTF-8} encoding.
102         *
103         * @param string the string to represent as a {@code ByteSource} instance.
104         * @return a new {@code ByteSource} instance representing the specified string's bytes.
105         */
106        public static ByteSource bytes(String string) {
107            return new SimpleByteSource(string);
108        }
109
110        /**
111         * Returns a new {@code ByteSource} instance representing the specified ByteSource.
112         *
113         * @param source the ByteSource to represent as a new {@code ByteSource} instance.
114         * @return a new {@code ByteSource} instance representing the specified ByteSource.
115         */
116        public static ByteSource bytes(ByteSource source) {
117            return new SimpleByteSource(source);
118        }
119
120        /**
121         * Returns a new {@code ByteSource} instance representing the specified File's bytes.
122         *
123         * @param file the file to represent as a {@code ByteSource} instance.
124         * @return a new {@code ByteSource} instance representing the specified File's bytes.
125         */
126        public static ByteSource bytes(File file) {
127            return new SimpleByteSource(file);
128        }
129
130        /**
131         * Returns a new {@code ByteSource} instance representing the specified InputStream's bytes.
132         *
133         * @param stream the InputStream to represent as a {@code ByteSource} instance.
134         * @return a new {@code ByteSource} instance representing the specified InputStream's bytes.
135         */
136        public static ByteSource bytes(InputStream stream) {
137            return new SimpleByteSource(stream);
138        }
139
140        /**
141         * Returns {@code true} if the specified object can be easily represented as a {@code ByteSource} using
142         * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise.
143         * <p/>
144         * This implementation merely returns {@link SimpleByteSource}
145         * .{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}.
146         *
147         * @param source the object to test to see if it can be easily converted to ByteSource instances using default
148         *               heuristics.
149         * @return {@code true} if the specified object can be easily represented as a {@code ByteSource} using
150         * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise.
151         */
152        public static boolean isCompatible(Object source) {
153            return SimpleByteSource.isCompatible(source);
154        }
155
156        /**
157         * Returns a {@code ByteSource} instance representing the specified byte source argument.  If the argument
158         * <em>cannot</em> be easily converted to bytes (as is indicated by the {@link #isCompatible(Object)} JavaDoc),
159         * this method will throw an {@link IllegalArgumentException}.
160         *
161         * @param source the byte-backed instance that should be represented as a {@code ByteSource} instance.
162         * @return a {@code ByteSource} instance representing the specified byte source argument.
163         * @throws IllegalArgumentException if the argument <em>cannot</em> be easily converted to bytes
164         *                                  (as indicated by the {@link #isCompatible(Object)} JavaDoc)
165         */
166        public static ByteSource bytes(Object source) throws IllegalArgumentException {
167            if (source == null) {
168                return null;
169            }
170            if (!isCompatible(source)) {
171                String msg = "Unable to heuristically acquire bytes for object of type ["
172                        + source.getClass().getName() + "].  If this type is indeed a byte-backed data type, you might "
173                        + "want to write your own ByteSource implementation to extract its bytes explicitly.";
174                throw new IllegalArgumentException(msg);
175            }
176            if (source instanceof byte[]) {
177                return bytes((byte[]) source);
178            } else if (source instanceof ByteSource) {
179                return (ByteSource) source;
180            } else if (source instanceof char[]) {
181                return bytes((char[]) source);
182            } else if (source instanceof String) {
183                return bytes((String) source);
184            } else if (source instanceof File) {
185                return bytes((File) source);
186            } else if (source instanceof InputStream) {
187                return bytes((InputStream) source);
188            } else {
189                throw new IllegalStateException("Encountered unexpected byte source.  This is a bug - please notify "
190                        + "the Shiro developer list asap (the isCompatible implementation does not reflect this "
191                        + "method's implementation).");
192            }
193        }
194    }
195}