001/*
002 * Copyright 2002-2017 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.lang.Nullable;
028import org.springframework.util.Assert;
029import org.springframework.util.ObjectUtils;
030
031/**
032 * Holder that combines a {@link Resource} descriptor with a specific encoding
033 * or {@code Charset} to be used for reading from the resource.
034 *
035 * <p>Used as an argument for operations that support reading content with
036 * a specific encoding, typically via a {@code java.io.Reader}.
037 *
038 * @author Juergen Hoeller
039 * @author Sam Brannen
040 * @since 1.2.6
041 * @see Resource#getInputStream()
042 * @see java.io.Reader
043 * @see java.nio.charset.Charset
044 */
045public class EncodedResource implements InputStreamSource {
046
047        private final Resource resource;
048
049        @Nullable
050        private final String encoding;
051
052        @Nullable
053        private final Charset charset;
054
055
056        /**
057         * Create a new {@code EncodedResource} for the given {@code Resource},
058         * not specifying an explicit encoding or {@code Charset}.
059         * @param resource the {@code Resource} to hold (never {@code null})
060         */
061        public EncodedResource(Resource resource) {
062                this(resource, null, null);
063        }
064
065        /**
066         * Create a new {@code EncodedResource} for the given {@code Resource},
067         * using the specified {@code encoding}.
068         * @param resource the {@code Resource} to hold (never {@code null})
069         * @param encoding the encoding to use for reading from the resource
070         */
071        public EncodedResource(Resource resource, @Nullable String encoding) {
072                this(resource, encoding, null);
073        }
074
075        /**
076         * Create a new {@code EncodedResource} for the given {@code Resource},
077         * using the specified {@code Charset}.
078         * @param resource the {@code Resource} to hold (never {@code null})
079         * @param charset the {@code Charset} to use for reading from the resource
080         */
081        public EncodedResource(Resource resource, @Nullable Charset charset) {
082                this(resource, null, charset);
083        }
084
085        private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
086                super();
087                Assert.notNull(resource, "Resource must not be null");
088                this.resource = resource;
089                this.encoding = encoding;
090                this.charset = charset;
091        }
092
093
094        /**
095         * Return the {@code Resource} held by this {@code EncodedResource}.
096         */
097        public final Resource getResource() {
098                return this.resource;
099        }
100
101        /**
102         * Return the encoding to use for reading from the {@linkplain #getResource() resource},
103         * or {@code null} if none specified.
104         */
105        @Nullable
106        public final String getEncoding() {
107                return this.encoding;
108        }
109
110        /**
111         * Return the {@code Charset} to use for reading from the {@linkplain #getResource() resource},
112         * or {@code null} if none specified.
113         */
114        @Nullable
115        public final Charset getCharset() {
116                return this.charset;
117        }
118
119        /**
120         * Determine whether a {@link Reader} is required as opposed to an {@link InputStream},
121         * i.e. whether an {@linkplain #getEncoding() encoding} or a {@link #getCharset() Charset}
122         * has been specified.
123         * @see #getReader()
124         * @see #getInputStream()
125         */
126        public boolean requiresReader() {
127                return (this.encoding != null || this.charset != null);
128        }
129
130        /**
131         * Open a {@code java.io.Reader} for the specified resource, using the specified
132         * {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}
133         * (if any).
134         * @throws IOException if opening the Reader failed
135         * @see #requiresReader()
136         * @see #getInputStream()
137         */
138        public Reader getReader() throws IOException {
139                if (this.charset != null) {
140                        return new InputStreamReader(this.resource.getInputStream(), this.charset);
141                }
142                else if (this.encoding != null) {
143                        return new InputStreamReader(this.resource.getInputStream(), this.encoding);
144                }
145                else {
146                        return new InputStreamReader(this.resource.getInputStream());
147                }
148        }
149
150        /**
151         * Open an {@code InputStream} for the specified resource, ignoring any specified
152         * {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}.
153         * @throws IOException if opening the InputStream failed
154         * @see #requiresReader()
155         * @see #getReader()
156         */
157        @Override
158        public InputStream getInputStream() throws IOException {
159                return this.resource.getInputStream();
160        }
161
162
163        @Override
164        public boolean equals(@Nullable Object other) {
165                if (this == other) {
166                        return true;
167                }
168                if (!(other instanceof EncodedResource)) {
169                        return false;
170                }
171                EncodedResource otherResource = (EncodedResource) other;
172                return (this.resource.equals(otherResource.resource) &&
173                                ObjectUtils.nullSafeEquals(this.charset, otherResource.charset) &&
174                                ObjectUtils.nullSafeEquals(this.encoding, otherResource.encoding));
175        }
176
177        @Override
178        public int hashCode() {
179                return this.resource.hashCode();
180        }
181
182        @Override
183        public String toString() {
184                return this.resource.toString();
185        }
186
187}