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.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.lang.reflect.InvocationTargetException;
025import javax.activation.DataSource;
026
027/**
028 * {@link DataSource} default method implementations.
029 *
030 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
031 */
032public interface DataSourceDefaultMethods extends DataSource {
033
034    /**
035     * {@link.rfc 2045} {@value #CONTENT_TYPE}
036     */
037    public static final String CONTENT_TYPE = "Content-Type";
038
039    /**
040     * {@link.rfc 2045} {@value #APPLICATION_OCTET_STREAM}
041     */
042    public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
043
044    /**
045     * {@link.rfc 3023} {@value #APPLICATION_XML}
046     */
047    public static final String APPLICATION_XML = "application/xml";
048
049    /**
050     * {@link.rfc 2045} {@value #TEXT_PLAIN}
051     */
052    public static final String TEXT_PLAIN = "text/plain";
053
054    /**
055     * {@link.rfc 2045} {@value #TEXT_HTML}
056     */
057    public static final String TEXT_HTML = "text/html";
058
059    default void setName(String name) {
060        throw new UnsupportedOperationException();
061    }
062
063    default void setContentType(String type) {
064        throw new UnsupportedOperationException();
065    }
066
067    /**
068     * @throws  UnsupportedOperationException
069     *                          Unless overridden by subclass
070     *                          implementation.
071     */
072    @Override
073    default InputStream getInputStream() throws IOException {
074        throw new UnsupportedOperationException();
075    }
076
077    /**
078     * @throws  UnsupportedOperationException
079     *                          Unless overridden by subclass
080     *                          implementation.
081     */
082    @Override
083    default OutputStream getOutputStream() throws IOException {
084        throw new UnsupportedOperationException();
085    }
086
087    /**
088     * Method to clear the {@link DataSource} and discard any input on any
089     * open {@link #getOutputStream()}.
090     *
091     * @throws  UnsupportedOperationException
092     *                          If {@link #getOutputStream()} throws
093     *                          {@link UnsupportedOperationException}.
094     */
095    default void clear() {
096        try {
097            getOutputStream().close();
098        } catch (IOException exception) {
099            throw new IllegalStateException(exception);
100        }
101    }
102
103    /**
104     * Method to get the number of bytes stored in {@link.this}
105     * {@link DataSource}.  Default implementation returns {@code -1}.
106     *
107     * @return  The number of bytes stored in {@link.this}
108     *          {@link DataSource}; {@code -1} if the count is unknown.
109     */
110    default long length() { return -1; }
111
112    /**
113     * Method to "wrap" the {@link InputStream} returned by
114     * {@link #getInputStream()}into {@link InputStream} instances.
115     *
116     * @param   types           The {@link InputStream} implementation
117     *                          {@link Class}es.
118     *
119     * @return  The "wrapped" {@link InputStream}.
120     *
121     * @throws  IOException     If an I/O error occurs.
122     */
123    default InputStream getInputStream(Class<?>... types) throws IOException {
124        return wrap(getInputStream(), types);
125    }
126
127    /**
128     * Method to "wrap" the {@link OutputStream} returned by
129     * {@link #getOutputStream()}into {@link OutputStream} instances.
130     *
131     * @param   types           The {@link OutputStream} implementation
132     *                          {@link Class}es.
133     *
134     * @return  The "wrapped" {@link OutputStream}.
135     *
136     * @throws  IOException     If an I/O error occurs.
137     */
138    default OutputStream getOutputStream(Class<?>... types) throws IOException {
139        return wrap(getOutputStream(), types);
140    }
141
142    /**
143     * Method to "wrap" an {@link InputStream} into {@link InputStream}
144     * instances.
145     *
146     * @param   in              The {@link InputStream}.
147     * @param   types           The {@link InputStream} implementation
148     *                          {@link Class}es.
149     *
150     * @return  The "wrapped" {@link InputStream}.
151     *
152     * @throws  IOException     If any of the wrapping streams throw an
153     *                          {@link IOException}.
154     */
155    default InputStream wrap(InputStream in, Class<?>... types) throws IOException {
156        try {
157            for (Class<?> type : types) {
158                in =
159                    type.asSubclass(InputStream.class)
160                    .getConstructor(InputStream.class)
161                    .newInstance(in);
162            }
163        } catch (InvocationTargetException exception) {
164            Throwable cause = exception.getCause();
165
166            if (cause instanceof IOException) {
167                throw (IOException) cause;
168            } else if (cause instanceof RuntimeException) {
169                throw (RuntimeException) cause;
170            } else if (cause instanceof Error) {
171                throw (Error) cause;
172            } else {
173                throw new IllegalArgumentException(exception);
174            }
175        } catch (RuntimeException exception) {
176            throw exception;
177        } catch (Exception exception) {
178            throw new IllegalArgumentException(exception);
179        }
180
181        return in;
182    }
183
184    /**
185     * Method to "wrap" an {@link OutputStream} into {@link OutputStream}
186     * instances.
187     *
188     * @param   out             The {@link OutputStream}.
189     * @param   types           The {@link OutputStream} implementation
190     *                          {@link Class}es.
191     *
192     * @return  The "wrapped" {@link OutputStream}.
193     *
194     * @throws  IOException     If any of the wrapping streams throw an
195     *                          {@link IOException}.
196     */
197    default OutputStream wrap(OutputStream out, Class<?>... types) throws IOException {
198        try {
199            for (Class<?> type : types) {
200                out =
201                    type.asSubclass(OutputStream.class)
202                    .getConstructor(OutputStream.class)
203                    .newInstance(out);
204            }
205        } catch (InvocationTargetException exception) {
206            Throwable cause = exception.getCause();
207
208            if (cause instanceof IOException) {
209                throw (IOException) cause;
210            } else if (cause instanceof RuntimeException) {
211                throw (RuntimeException) cause;
212            } else if (cause instanceof Error) {
213                throw (Error) cause;
214            } else {
215                throw new IllegalArgumentException(exception);
216            }
217        } catch (RuntimeException exception) {
218            throw exception;
219        } catch (Exception exception) {
220            throw new IllegalArgumentException(exception);
221        }
222
223        return out;
224    }
225}