001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.io.support;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.InputStreamReader;
022import java.io.Reader;
023import java.nio.charset.Charset;
024
025import org.springframework.core.io.InputStreamSource;
026import org.springframework.core.io.Resource;
027import org.springframework.util.Assert;
028import org.springframework.util.ObjectUtils;
029
030/**
031 * Holder that combines a {@link Resource} descriptor with a specific encoding
032 * or {@code Charset} to be used for reading from the resource.
033 *
034 * <p>Used as an argument for operations that support reading content with
035 * a specific encoding, typically via a {@code java.io.Reader}.
036 *
037 * @author Juergen Hoeller
038 * @author Sam Brannen
039 * @since 1.2.6
040 * @see Resource#getInputStream()
041 * @see java.io.Reader
042 * @see java.nio.charset.Charset
043 */
044public class EncodedResource implements InputStreamSource {
045
046        private final Resource resource;
047
048        private final String encoding;
049
050        private final Charset charset;
051
052
053        /**
054         * Create a new {@code EncodedResource} for the given {@code Resource},
055         * not specifying an explicit encoding or {@code Charset}.
056         * @param resource the {@code Resource} to hold (never {@code null})
057         */
058        public EncodedResource(Resource resource) {
059                this(resource, null, null);
060        }
061
062        /**
063         * Create a new {@code EncodedResource} for the given {@code Resource},
064         * using the specified {@code encoding}.
065         * @param resource the {@code Resource} to hold (never {@code null})
066         * @param encoding the encoding to use for reading from the resource
067         */
068        public EncodedResource(Resource resource, String encoding) {
069                this(resource, encoding, null);
070        }
071
072        /**
073         * Create a new {@code EncodedResource} for the given {@code Resource},
074         * using the specified {@code Charset}.
075         * @param resource the {@code Resource} to hold (never {@code null})
076         * @param charset the {@code Charset} to use for reading from the resource
077         */
078        public EncodedResource(Resource resource, Charset charset) {
079                this(resource, null, charset);
080        }
081
082        private EncodedResource(Resource resource, String encoding, Charset charset) {
083                super();
084                Assert.notNull(resource, "Resource must not be null");
085                this.resource = resource;
086                this.encoding = encoding;
087                this.charset = charset;
088        }
089
090
091        /**
092         * Return the {@code Resource} held by this {@code EncodedResource}.
093         */
094        public final Resource getResource() {
095                return this.resource;
096        }
097
098        /**
099         * Return the encoding to use for reading from the {@linkplain #getResource() resource},
100         * or {@code null} if none specified.
101         */
102        public final String getEncoding() {
103                return this.encoding;
104        }
105
106        /**
107         * Return the {@code Charset} to use for reading from the {@linkplain #getResource() resource},
108         * or {@code null} if none specified.
109         */
110        public final Charset getCharset() {
111                return this.charset;
112        }
113
114        /**
115         * Determine whether a {@link Reader} is required as opposed to an {@link InputStream},
116         * i.e. whether an {@linkplain #getEncoding() encoding} or a {@link #getCharset() Charset}
117         * has been specified.
118         * @see #getReader()
119         * @see #getInputStream()
120         */
121        public boolean requiresReader() {
122                return (this.encoding != null || this.charset != null);
123        }
124
125        /**
126         * Open a {@code java.io.Reader} for the specified resource, using the specified
127         * {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}
128         * (if any).
129         * @throws IOException if opening the Reader failed
130         * @see #requiresReader()
131         * @see #getInputStream()
132         */
133        public Reader getReader() throws IOException {
134                if (this.charset != null) {
135                        return new InputStreamReader(this.resource.getInputStream(), this.charset);
136                }
137                else if (this.encoding != null) {
138                        return new InputStreamReader(this.resource.getInputStream(), this.encoding);
139                }
140                else {
141                        return new InputStreamReader(this.resource.getInputStream());
142                }
143        }
144
145        /**
146         * Open an {@code InputStream} for the specified resource, ignoring any specified
147         * {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}.
148         * @throws IOException if opening the InputStream failed
149         * @see #requiresReader()
150         * @see #getReader()
151         */
152        @Override
153        public InputStream getInputStream() throws IOException {
154                return this.resource.getInputStream();
155        }
156
157
158        @Override
159        public boolean equals(Object other) {
160                if (this == other) {
161                        return true;
162                }
163                if (!(other instanceof EncodedResource)) {
164                        return false;
165                }
166                EncodedResource otherResource = (EncodedResource) other;
167                return (this.resource.equals(otherResource.resource) &&
168                                ObjectUtils.nullSafeEquals(this.charset, otherResource.charset) &&
169                                ObjectUtils.nullSafeEquals(this.encoding, otherResource.encoding));
170        }
171
172        @Override
173        public int hashCode() {
174                return this.resource.hashCode();
175        }
176
177        @Override
178        public String toString() {
179                return this.resource.toString();
180        }
181
182}