001package ball.activation;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: DataSourceDefaultMethods.java 5285 2020-02-05 04:23:21Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/activation/DataSourceDefaultMethods.java $
007 * %%
008 * Copyright (C) 2008 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026import java.lang.reflect.InvocationTargetException;
027import javax.activation.DataSource;
028import org.apache.commons.io.IOUtils;
029
030/**
031 * {@link DataSource} default method implementations.
032 *
033 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
034 * @version $Revision: 5285 $
035 */
036public interface DataSourceDefaultMethods extends DataSource {
037
038    /**
039     * {@link.rfc 2045} {@value #CONTENT_TYPE}
040     */
041    public static final String CONTENT_TYPE = "Content-Type";
042
043    /**
044     * {@link.rfc 2045} {@value #APPLICATION_OCTET_STREAM}
045     */
046    public static final String APPLICATION_OCTET_STREAM =
047        "application/octet-stream";
048
049    /**
050     * {@link.rfc 3023} {@value #APPLICATION_XML}
051     */
052    public static final String APPLICATION_XML = "application/xml";
053
054    /**
055     * {@link.rfc 2045} {@value #TEXT_PLAIN}
056     */
057    public static final String TEXT_PLAIN = "text/plain";
058
059    /**
060     * {@link.rfc 2045} {@value #TEXT_HTML}
061     */
062    public static final String TEXT_HTML = "text/html";
063
064    default void setName(String name) {
065        throw new UnsupportedOperationException();
066    }
067
068    default void setContentType(String type) {
069        throw new UnsupportedOperationException();
070    }
071
072    /**
073     * @throws  UnsupportedOperationException
074     *                          Unless overridden by subclass
075     *                          implementation.
076     */
077    @Override
078    default InputStream getInputStream() throws IOException {
079        throw new UnsupportedOperationException();
080    }
081
082    /**
083     * @throws  UnsupportedOperationException
084     *                          Unless overridden by subclass
085     *                          implementation.
086     */
087    @Override
088    default OutputStream getOutputStream() throws IOException {
089        throw new UnsupportedOperationException();
090    }
091
092    /**
093     * Method to clear the {@link DataSource} and discard any input on any
094     * open {@link #getOutputStream()}.
095     *
096     * @throws  UnsupportedOperationException
097     *                          If {@link #getOutputStream()} throws
098     *                          {@link UnsupportedOperationException}.
099     */
100    default void clear() {
101        try {
102            getOutputStream().close();
103        } catch (IOException exception) {
104            throw new IllegalStateException(exception);
105        }
106    }
107
108    /**
109     * Method to get the number of bytes stored in {@link.this}
110     * {@link DataSource}.  Default implementation returns {@code -1}.
111     *
112     * @return  The number of bytes stored in {@link.this}
113     *          {@link DataSource}; {@code -1} if the count is unknown.
114     */
115    default long length() { return -1; }
116
117    /**
118     * Method to "wrap" the {@link InputStream} returned by
119     * {@link #getInputStream()}into {@link InputStream} instances.
120     *
121     * @param   types           The {@link InputStream} implementation
122     *                          {@link Class}es.
123     *
124     * @return  The "wrapped" {@link InputStream}.
125     *
126     * @throws  IOException     If an I/O error occurs.
127     */
128    default InputStream getInputStream(Class<?>... types) throws IOException {
129        return wrap(getInputStream(), types);
130    }
131
132    /**
133     * Method to "wrap" the {@link OutputStream} returned by
134     * {@link #getOutputStream()}into {@link OutputStream} instances.
135     *
136     * @param   types           The {@link OutputStream} implementation
137     *                          {@link Class}es.
138     *
139     * @return  The "wrapped" {@link OutputStream}.
140     *
141     * @throws  IOException     If an I/O error occurs.
142     */
143    default OutputStream getOutputStream(Class<?>... types) throws IOException {
144        return wrap(getOutputStream(), types);
145    }
146
147    /**
148     * Method to write the contents of this {@link DataSource} to an
149     * {@link OutputStream}.
150     *
151     * @see #getInputStream()
152     *
153     * @param   out             The target {@link OutputStream}.
154     *
155     * @throws  IOException     If a problem is encountered opening or
156     *                          reading the {@link InputStream} or writing
157     *                          to the {@link OutputStream}.
158     */
159    default void writeTo(OutputStream out) throws IOException {
160        try (InputStream in = getInputStream()) {
161            IOUtils.copy(in, out);
162        }
163    }
164
165    /**
166     * Method to "wrap" an {@link InputStream} into {@link InputStream}
167     * instances.
168     *
169     * @param   in              The {@link InputStream}.
170     * @param   types           The {@link InputStream} implementation
171     *                          {@link Class}es.
172     *
173     * @return  The "wrapped" {@link InputStream}.
174     *
175     * @throws  IOException     If any of the wrapping streams throw an
176     *                          {@link IOException}.
177     */
178    default InputStream wrap(InputStream in,
179                             Class<?>... types) throws IOException {
180        try {
181            for (Class<?> type : types) {
182                in =
183                    type.asSubclass(InputStream.class)
184                    .getConstructor(InputStream.class)
185                    .newInstance(in);
186            }
187        } catch (InvocationTargetException exception) {
188            Throwable cause = exception.getCause();
189
190            if (cause instanceof IOException) {
191                throw (IOException) cause;
192            } else if (cause instanceof RuntimeException) {
193                throw (RuntimeException) cause;
194            } else if (cause instanceof Error) {
195                throw (Error) cause;
196            } else {
197                throw new IllegalArgumentException(exception);
198            }
199        } catch (RuntimeException exception) {
200            throw exception;
201        } catch (Exception exception) {
202            throw new IllegalArgumentException(exception);
203        }
204
205        return in;
206    }
207
208    /**
209     * Method to "wrap" an {@link OutputStream} into {@link OutputStream}
210     * instances.
211     *
212     * @param   out             The {@link OutputStream}.
213     * @param   types           The {@link OutputStream} implementation
214     *                          {@link Class}es.
215     *
216     * @return  The "wrapped" {@link OutputStream}.
217     *
218     * @throws  IOException     If any of the wrapping streams throw an
219     *                          {@link IOException}.
220     */
221    default OutputStream wrap(OutputStream out,
222                              Class<?>... types) throws IOException {
223        try {
224            for (Class<?> type : types) {
225                out =
226                    type.asSubclass(OutputStream.class)
227                    .getConstructor(OutputStream.class)
228                    .newInstance(out);
229            }
230        } catch (InvocationTargetException exception) {
231            Throwable cause = exception.getCause();
232
233            if (cause instanceof IOException) {
234                throw (IOException) cause;
235            } else if (cause instanceof RuntimeException) {
236                throw (RuntimeException) cause;
237            } else if (cause instanceof Error) {
238                throw (Error) cause;
239            } else {
240                throw new IllegalArgumentException(exception);
241            }
242        } catch (RuntimeException exception) {
243            throw exception;
244        } catch (Exception exception) {
245            throw new IllegalArgumentException(exception);
246        }
247
248        return out;
249    }
250}