Search in sources :

Example 1 with WarningListeners

use of org.apache.sis.util.logging.WarningListeners in project sis by apache.

the class ChannelFactory method prepare.

/**
 * Returns a byte channel factory from the given input or output,
 * or {@code null} if the given input/output is of unknown type.
 * More specifically:
 *
 * <ul>
 *   <li>If the given storage is {@code null}, then this method returns {@code null}.</li>
 *   <li>If the given storage is a {@link ReadableByteChannel} or an {@link InputStream},
 *       then the factory will return that input directly or indirectly as a wrapper.</li>
 *   <li>If the given storage is a {@link WritableByteChannel} or an {@link OutputStream}
 *       and the {@code allowWriteOnly} argument is {@code true},
 *       then the factory will return that output directly or indirectly as a wrapper.</li>
 *   <li>If the given storage if a {@link Path}, {@link File}, {@link URL}, {@link URI}
 *       or {@link CharSequence}, then the factory will open new channels on demand.</li>
 * </ul>
 *
 * The given options are used for opening the channel on a <em>best effort basis</em>.
 * In particular, even if the caller provided the {@code WRITE} option, he still needs
 * to verify if the returned channel implements {@link java.nio.channels.WritableByteChannel}.
 * This is because the channel may be opened by {@link URL#openStream()}, in which case the
 * options are ignored.
 *
 * <p>The following options are illegal for read operations and will cause an exception to be
 * thrown if provided while {@code allowWriteOnly} is {@code false}:
 * {@code APPEND}, {@code TRUNCATE_EXISTING}, {@code DELETE_ON_CLOSE}. We reject those options
 * because this method is primarily designed for readable channels, with optional data edition.
 * Since the write option is not guaranteed to be honored, we have to reject the options that
 * would alter significatively the channel behavior depending on whether we have been able to
 * honor the options or not.</p>
 *
 * @param  storage         the stream or the file to open, or {@code null}.
 * @param  encoding        if the input is an encoded URL, the character encoding (normally {@code "UTF-8"}).
 *                         If the URL is not encoded, then {@code null}. This argument is ignored if the given
 *                         input does not need to be converted from URL to {@code File}.
 * @param  allowWriteOnly  whether to allow wrapping {@link WritableByteChannel} and {@link OutputStream}.
 * @param  options         the options to use for creating a new byte channel. Can be null or empty for read-only.
 * @return the channel factory for the given input, or {@code null} if the given input is of unknown type.
 * @throws IOException if an error occurred while processing the given input.
 */
public static ChannelFactory prepare(Object storage, final String encoding, final boolean allowWriteOnly, OpenOption... options) throws IOException {
    /*
         * Unconditionally verify the options (unless 'allowWriteOnly' is true),
         * even if we may not use them.
         */
    final Set<OpenOption> optionSet;
    if (options == null || options.length == 0) {
        optionSet = Collections.singleton(StandardOpenOption.READ);
    } else {
        optionSet = new HashSet<>(Arrays.asList(options));
        optionSet.add(StandardOpenOption.READ);
        if (!allowWriteOnly && optionSet.removeAll(ILLEGAL_OPTIONS)) {
            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "options", Arrays.toString(options)));
        }
    }
    /*
         * Check for inputs that are already readable channels or input streams.
         * Note that Channels.newChannel(InputStream) checks for instances of FileInputStream in order to delegate
         * to its getChannel() method, but only if the input stream type is exactly FileInputStream, not a subtype.
         * If Apache SIS defines its own FileInputStream subclass someday, we may need to add a special case here.
         */
    if (storage instanceof ReadableByteChannel || (allowWriteOnly && storage instanceof WritableByteChannel)) {
        return new Stream((Channel) storage);
    } else if (storage instanceof InputStream) {
        return new Stream(Channels.newChannel((InputStream) storage));
    } else if (allowWriteOnly && storage instanceof OutputStream) {
        return new Stream(Channels.newChannel((OutputStream) storage));
    }
    /*
         * In the following cases, we will try hard to convert to Path objects before to fallback
         * on File, URL or URI, because only Path instances allow us to use the given OpenOptions.
         */
    if (storage instanceof URL) {
        try {
            storage = IOUtilities.toPath((URL) storage, encoding);
        } catch (IOException e) {
            /*
                 * This is normal if the URL uses HTTP or FTP protocol for instance. Log the exception at FINE
                 * level without stack trace. We will open the channel later using the URL instead than the Path.
                 */
            recoverableException(e);
        }
    } else if (storage instanceof URI) {
        /*
             * If the user gave us a URI, try to convert to a Path before to fallback to URL, in order to be
             * able to use the given OpenOptions.  Note that the conversion to Path is likely to fail if the
             * URL uses HTTP or FTP protocols, because JDK7 does not provide file systems for them by default.
             */
        final URI uri = (URI) storage;
        if (!uri.isAbsolute()) {
            /*
                 * All methods invoked in this block throws IllegalArgumentException if the URI has no scheme,
                 * so we are better to check now and provide a more appropriate exception for this method.
                 */
            throw new IOException(Resources.format(Resources.Keys.MissingSchemeInURI_1, uri));
        } else
            try {
                storage = Paths.get(uri);
            } catch (IllegalArgumentException | FileSystemNotFoundException e) {
                try {
                    storage = uri.toURL();
                } catch (MalformedURLException ioe) {
                    ioe.addSuppressed(e);
                    throw ioe;
                }
                /*
                 * We have been able to convert to URL, but the given OpenOptions may not be used.
                 * Log the exception at FINE level without stack trace, because the exception is
                 * probably a normal behavior in this context.
                 */
                recoverableException(e);
            }
    } else {
        if (storage instanceof CharSequence) {
            // Needs to be before the check for File or URL.
            storage = IOUtilities.toFileOrURL(storage.toString(), encoding);
        }
        /*
             * If the input is a File or a CharSequence that we have been able to convert to a File,
             * try to convert to a Path in order to be able to use the OpenOptions. Only if we fail
             * to convert to a Path (which is unlikely), we will use directly the File.
             */
        if (storage instanceof File) {
            final File file = (File) storage;
            try {
                storage = file.toPath();
            } catch (final InvalidPathException e) {
                /*
                     * Unlikely to happen. But if it happens anyway, try to open the channel in a
                     * way less surprising for the user (closer to the object he has specified).
                     */
                if (file.isFile()) {
                    return new Fallback(file, e);
                }
            }
        }
    }
    /*
         * One last check for URL. The URL may be either the given input if we have not been able
         * to convert it to a Path, or a URI, File or CharSequence input converted to URL. Do not
         * try to convert the URL to a Path, because this has already been tried before this point.
         */
    if (storage instanceof URL) {
        final URL file = (URL) storage;
        return new ChannelFactory() {

            @Override
            public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                return Channels.newChannel(file.openStream());
            }

            @Override
            public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                return Channels.newChannel(file.openConnection().getOutputStream());
            }
        };
    }
    if (storage instanceof Path) {
        final Path path = (Path) storage;
        if (Files.isRegularFile(path)) {
            return new ChannelFactory() {

                @Override
                public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                    return Files.newByteChannel(path, optionSet);
                }

                @Override
                public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                    return Files.newByteChannel(path, optionSet);
                }
            };
        }
    }
    return null;
}
Also used : Path(java.nio.file.Path) ReadableByteChannel(java.nio.channels.ReadableByteChannel) WarningListeners(org.apache.sis.util.logging.WarningListeners) MalformedURLException(java.net.MalformedURLException) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) OutputStream(java.io.OutputStream) FileOutputStream(java.io.FileOutputStream) WritableByteChannel(java.nio.channels.WritableByteChannel) IOException(java.io.IOException) URI(java.net.URI) URL(java.net.URL) InvalidPathException(java.nio.file.InvalidPathException) OpenOption(java.nio.file.OpenOption) StandardOpenOption(java.nio.file.StandardOpenOption) FileSystemNotFoundException(java.nio.file.FileSystemNotFoundException) OutputStream(java.io.OutputStream) FileOutputStream(java.io.FileOutputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) File(java.io.File)

Aggregations

File (java.io.File)1 FileInputStream (java.io.FileInputStream)1 FileOutputStream (java.io.FileOutputStream)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 OutputStream (java.io.OutputStream)1 MalformedURLException (java.net.MalformedURLException)1 URI (java.net.URI)1 URL (java.net.URL)1 ReadableByteChannel (java.nio.channels.ReadableByteChannel)1 WritableByteChannel (java.nio.channels.WritableByteChannel)1 FileSystemNotFoundException (java.nio.file.FileSystemNotFoundException)1 InvalidPathException (java.nio.file.InvalidPathException)1 OpenOption (java.nio.file.OpenOption)1 Path (java.nio.file.Path)1 StandardOpenOption (java.nio.file.StandardOpenOption)1 WarningListeners (org.apache.sis.util.logging.WarningListeners)1