001package ball.activation;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import java.beans.ConstructorProperties;
022import java.io.BufferedReader;
023import java.io.IOException;
024import java.io.InputStreamReader;
025import java.io.OutputStreamWriter;
026import java.io.PrintStream;
027import java.io.PrintWriter;
028import java.io.Reader;
029import java.io.Writer;
030import java.nio.charset.Charset;
031
032import static java.nio.charset.StandardCharsets.UTF_8;
033import static java.util.stream.Collectors.joining;
034import static org.apache.commons.lang3.StringUtils.EMPTY;
035
036/**
037 * {@link javax.activation.DataSource} implementation that provides a
038 * {@link BufferedReader} wrapping the {@link javax.activation.DataSource}
039 * {@link java.io.InputStream} and a {@link PrintWriter} wrapping the
040 * {@link javax.activation.DataSource} {@link java.io.OutputStream}.
041 *
042 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
043 */
044public class ReaderWriterDataSource extends FilterDataSource {
045    protected static final Charset CHARSET = UTF_8;
046
047    private final Charset charset;
048
049    /**
050     * @param   name            Initial {@code "Name"} attribute value.
051     * @param   type            Initial {@code "ContentType"} attribute
052     *                          value.
053     */
054    @ConstructorProperties({ "name", "contentType" })
055    public ReaderWriterDataSource(String name, String type) {
056        this(name, type, null);
057    }
058
059    /**
060     * @param   name            Initial {@code "Name"} attribute value.
061     * @param   type            Initial {@code "ContentType"} attribute
062     *                          value.
063     * @param   charset         The {@link Charset} used to encode the
064     *                          {@link java.io.OutputStream}.
065     */
066    @ConstructorProperties({ "name", "contentType", "charset" })
067    public ReaderWriterDataSource(String name, String type, Charset charset) {
068        this(name, type, charset, null);
069    }
070
071    /**
072     * @param   name            Initial {@code "Name"} attribute value.
073     * @param   type            Initial {@code "ContentType"} attribute
074     *                          value.
075     * @param   charset         The {@link Charset} used to encode the
076     *                          {@link java.io.OutputStream}.
077     * @param   content         The initial content {@link String}.
078     */
079    @ConstructorProperties({ "name", "contentType", "charset", EMPTY })
080    public ReaderWriterDataSource(String name, String type, Charset charset, String content) {
081        super(new ByteArrayDataSource(name, type));
082
083        this.charset = (charset != null) ? charset : CHARSET;
084
085        if (content != null) {
086            try (Writer writer = getWriter()) {
087                writer.write(content);
088            } catch (IOException exception) {
089                throw new ExceptionInInitializerError(exception);
090            }
091        }
092    }
093
094    /**
095     * Private no-argument constructor (for JAXB annotated subclasses).
096     */
097    private ReaderWriterDataSource() { this(null, null); }
098
099    /**
100     * Method to get the {@link Charset} used to create the
101     * {@link BufferedReader} and {@link PrintWriter}.
102     *
103     * @return  The Charset.
104     */
105    public Charset getCharset() { return charset; }
106
107    /**
108     * Method to return a new {@link Reader} to read the underlying
109     * {@link java.io.InputStream}.
110     *
111     * @see #getInputStream()
112     *
113     * @return  A {@link Reader} wrapping the
114     *          {@link javax.activation.DataSource}
115     *          {@link java.io.InputStream}.
116     *
117     * @throws  IOException     If an I/O exception occurs.
118     */
119    public Reader getReader() throws IOException {
120        return new InputStreamReader(getInputStream(), getCharset());
121    }
122
123    /**
124     * Method to return a new {@link Writer} to write to the underlying
125     * {@link java.io.OutputStream}.
126     *
127     * @see #getOutputStream()
128     *
129     * @return  A {@link Writer} wrapping the
130     *          {@link javax.activation.DataSource}
131     *          {@link java.io.OutputStream}.
132     *
133     * @throws  IOException     If an I/O exception occurs.
134     */
135    public Writer getWriter() throws IOException {
136        return new OutputStreamWriter(getOutputStream(), getCharset());
137    }
138
139    /**
140     * Method to return a new {@link BufferedReader} to read the underlying
141     * {@link java.io.InputStream}.
142     *
143     * @see #getInputStream()
144     *
145     * @return  A {@link BufferedReader} wrapping the
146     *          {@link javax.activation.DataSource}
147     *          {@link java.io.InputStream}.
148     *
149     * @throws  IOException     If an I/O exception occurs.
150     */
151    public BufferedReader getBufferedReader() throws IOException {
152        return new BufferedReader(getReader());
153    }
154
155    /**
156     * Method to return a new {@link PrintWriter} to write to the underlying
157     * {@link java.io.OutputStream}.
158     *
159     * @see #getOutputStream()
160     *
161     * @return  A {@link PrintWriter} wrapping the
162     *          {@link javax.activation.DataSource}
163     *          {@link java.io.OutputStream}.
164     *
165     * @throws  IOException     If an I/O exception occurs.
166     */
167    public PrintWriter getPrintWriter() throws IOException {
168        return new PrintWriter(getWriter(), true);
169    }
170
171    /**
172     * Method to return a new {@link PrintStream} to write to the underlying
173     * {@link java.io.OutputStream}.
174     *
175     * @see #getOutputStream()
176     *
177     * @return  A {@link PrintStream} wrapping the
178     *          {@link javax.activation.DataSource}
179     *          {@link java.io.OutputStream}.
180     *
181     * @throws  IOException     If an I/O exception occurs.
182     */
183    public PrintStream getPrintStream() throws IOException {
184        return new PrintStream(getOutputStream(), true, getCharset().name());
185    }
186
187    /**
188     * Method to write the contents of this
189     * {@link javax.activation.DataSource} to a {@link PrintWriter}.
190     *
191     * @see #getBufferedReader()
192     *
193     * @param   writer          The target {@link PrintWriter}.
194     *
195     * @throws  IOException     If a problem is encountered opening or
196     *                          reading the {@link BufferedReader} or
197     *                          writing to the {@link PrintWriter}.
198     */
199    public void writeTo(PrintWriter writer) throws IOException {
200        getBufferedReader().lines().forEach(t -> writer.println(t));
201    }
202
203    @Override
204    public String toString() {
205        String string = null;
206
207        try (BufferedReader reader = getBufferedReader()) {
208            string = reader.lines().collect(joining("\n"));
209        } catch (IOException exception) {
210            string = super.toString();
211        }
212
213        return string;
214    }
215
216    /**
217     * Convenience method to get the name of a {@link Charset}.
218     *
219     * @param   charset         The {@link Charset}.
220     *
221     * @return  The name of the {@link Charset} if non-{@code null};
222     *          {@code null} otherwise.
223     *
224     * @see Charset#name()
225     */
226    protected static String nameOf(Charset charset) {
227        return (charset != null) ? charset.name() : null;
228    }
229}