package com.revolsys.io;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.revolsys.collection.list.Lists;
import com.revolsys.collection.map.Maps;
import com.revolsys.io.channels.ChannelReader;
import com.revolsys.io.file.FileNameExtensionFilter;
import com.revolsys.io.file.Paths;
import com.revolsys.record.Available;
import com.revolsys.record.io.RecordWriterFactory;
import com.revolsys.spring.resource.GzipResource;
import com.revolsys.spring.resource.Resource;
import com.revolsys.spring.resource.UrlResource;
import com.revolsys.util.Exceptions;
import com.revolsys.util.Property;
import com.revolsys.util.Strings;
import com.revolsys.util.UrlUtil;
public interface IoFactory extends Available {
@SuppressWarnings("unchecked")
static <C extends IoFactory> List<C> factories(final Class<C> factoryClass) {
return Lists.<C> toArray((Set<C>)IoFactoryRegistry.factoriesByClass.get(factoryClass));
}
/**
* Get the {@link IoFactory} for the given source. The source can be one of the following
* classes.
*
* <ul>
* <li>{@link PathUtil}</li>
* <li>{@link File}</li>
* <li>{@link Resource}</li>
* </ul>
* @param factoryClass The class or interface to get the factory for.
* @param source The source to create the factory for.
* @return The factory.
* @throws IllegalArgumentException If the source is not a supported class.
*/
static <C extends IoFactory> C factory(final Class<C> factoryClass, final Object source) {
final String fileName = fileName(source);
return factoryByFileName(factoryClass, fileName);
}
@SuppressWarnings("unchecked")
static <F extends IoFactory> F factoryByFileExtension(final Class<F> factoryClass,
String fileExtension) {
fileExtension = fileExtension.toLowerCase();
if (Property.hasValue(fileExtension)) {
return (F)Maps.getMap(IoFactoryRegistry.factoryByClassAndFileExtension, factoryClass,
fileExtension);
}
return null;
}
static <C extends IoFactory> C factoryByFileName(final Class<C> factoryClass,
final String fileName) {
for (final String fileExtension : FileUtil.getFileNameExtensions(fileName)) {
final C factory = factoryByFileExtension(factoryClass, fileExtension);
if (factory != null) {
return factory;
}
}
if (fileName.endsWith(".zip")) {
final C factory = factoryByFileName(factoryClass,
fileName.substring(0, fileName.length() - 4));
if (factory != null && factory.isReadFromZipFileSupported()) {
return factory;
}
}
if (fileName.endsWith(".gz")) {
final C factory = factoryByFileName(factoryClass,
fileName.substring(0, fileName.length() - 3));
if (factory != null && factory.isReadFromZipFileSupported()) {
return factory;
}
}
return null;
}
@SuppressWarnings("unchecked")
public static <F extends IoFactory> F factoryByMediaType(final Class<F> factoryClass,
final String mediaType) {
if (Property.hasValue(mediaType)) {
if (mediaType.contains("/")) {
return (F)Maps.getMap(IoFactoryRegistry.factoryByClassAndMediaType, factoryClass,
mediaType);
} else {
return factoryByFileExtension(factoryClass, mediaType);
}
}
return null;
}
static String fileExtensionByMediaType(final String mediaType) {
final RecordWriterFactory writerFactory = factoryByMediaType(RecordWriterFactory.class,
mediaType);
if (writerFactory == null) {
return null;
} else {
return writerFactory.getFileExtension(mediaType);
}
}
static List<String> fileExtensions(final Class<? extends IoFactory> factoryClass) {
return Lists.toArray(IoFactoryRegistry.fileExtensionsByClass.get(factoryClass));
}
public static String fileName(final Object source) {
String fileName = null;
if (Property.hasValue(source)) {
if (source instanceof Resource) {
fileName = ((Resource)source).getFilename();
} else if (source instanceof Path) {
fileName = Paths.getFileName((Path)source);
} else if (source instanceof File) {
fileName = FileUtil.getFileName((File)source);
} else if (source instanceof URL) {
fileName = UrlUtil.getFileName((URL)source);
} else if (source instanceof URI) {
fileName = UrlUtil.getFileName((URI)source);
} else if (source instanceof String) {
fileName = FileUtil.getFileName((String)source);
} else {
throw new IllegalArgumentException(source.getClass() + " is not supported");
}
}
return fileName;
}
static <C extends IoFactory> boolean hasFactory(final Class<C> factoryClass,
final Object source) {
final C factory = factory(factoryClass, source);
return factory != null;
}
static <F extends IoFactory> boolean isAvailable(final Class<F> factoryClass,
final Object source) {
if (factoryClass != null) {
final List<String> fileExtensions = fileExtensions(factoryClass);
if (Property.hasValue(fileExtensions)) {
try {
final String fileName = fileName(source);
for (final String fileExtension : FileUtil.getFileNameExtensions(fileName)) {
if (Property.hasValue(fileExtension)
&& fileExtensions.contains(fileExtension.toLowerCase())) {
return true;
}
}
} catch (final IllegalArgumentException e) {
}
}
}
return false;
}
static Map<String, String> mediaTypeByFileExtension() {
return new HashMap<>(IoFactoryRegistry.mediaTypeByFileExtension);
}
static <F extends IoFactory> List<String> mediaTypes(final Class<F> factoryClass) {
return Lists.toArray(IoFactoryRegistry.mediaTypesByClass.get(factoryClass));
}
public static ChannelReader newChannelReader(final Resource resource) {
final ReadableByteChannel channel = newReadableByteChannel(resource);
return new ChannelReader(channel);
}
public static FileNameExtensionFilter newFileFilter(final IoFactory factory) {
final List<String> fileExtensions = factory.getFileExtensions();
String description = factory.getName();
description += " (" + Strings.toString(fileExtensions) + ")";
return newFileFilter(description, fileExtensions);
}
public static FileNameExtensionFilter newFileFilter(final String description,
final Collection<String> fileExtensions) {
final String[] array = fileExtensions.toArray(new String[0]);
return new FileNameExtensionFilter(description, array);
}
public static List<FileNameExtensionFilter> newFileFilters(final Set<String> allExtensions,
final Class<? extends IoFactory> factoryClass) {
final List<FileNameExtensionFilter> filters = new ArrayList<>();
final List<? extends IoFactory> factories = IoFactory.factories(factoryClass);
for (final IoFactory factory : factories) {
final FileNameExtensionFilter filter = newFileFilter(factory);
filters.add(filter);
if (allExtensions != null) {
for (final String fileNameExtension : filter.getExtensions()) {
allExtensions.add(fileNameExtension);
}
}
}
sortFilters(filters);
return filters;
}
public static ReadableByteChannel newReadableByteChannel(final Resource resource) {
final String fileExtension = resource.getFileNameExtension();
try {
if (fileExtension.equals("zip")) {
final ZipInputStream in = resource.newBufferedInputStream(ZipInputStream::new);
final String baseName = resource.getBaseName();
for (ZipEntry zipEntry = in.getNextEntry(); zipEntry != null; zipEntry = in
.getNextEntry()) {
if (zipEntry.getName().equals(baseName)) {
return Channels.newChannel(in);
}
}
throw new IllegalArgumentException("Cannot find " + baseName + " in " + resource);
} else if (fileExtension.equals("gz")) {
final InputStream in = resource.newBufferedInputStream();
final GZIPInputStream gzIn = new GZIPInputStream(in);
return Channels.newChannel(gzIn);
} else {
return resource.newReadableByteChannel();
}
} catch (final IOException e) {
throw Exceptions.wrap("Unable to open: " + resource, e);
}
}
public static void sortFilters(final List<FileNameExtensionFilter> filters) {
Collections.sort(filters, new Comparator<FileNameExtensionFilter>() {
@Override
public int compare(final FileNameExtensionFilter filter1,
final FileNameExtensionFilter filter2) {
return filter1.getDescription().compareTo(filter2.getDescription());
}
});
}
default void addFileFilters(final List<FileNameExtensionFilter> filters) {
for (final String fileExtension : getFileExtensions()) {
final String description = getName() + " (" + fileExtension + ")";
final FileNameExtensionFilter filter = new FileNameExtensionFilter(description,
fileExtension);
filters.add(filter);
}
}
default String getFileExtension(final String mediaType) {
return null;
}
default List<String> getFileExtensions() {
return Collections.emptyList();
}
default String getMediaType(final String fileExtension) {
return null;
}
default Set<String> getMediaTypes() {
return Collections.emptySet();
}
String getName();
default Resource getZipResource(final Object source) {
Resource resource = Resource.getResource(source);
if (isReadFromZipFileSupported()) {
final String filename = resource.getFilename();
if (filename.endsWith(".zip")) {
final String baseName = filename.substring(0, filename.length() - 4);
final String url = "jar:" + resource.getUri() + "!/" + baseName;
final UrlResource urlResource = new UrlResource(url);
if (urlResource.exists()) {
resource = urlResource;
} else {
return null;
}
} else if (filename.endsWith(".gz")) {
return new GzipResource(resource);
}
}
return resource;
}
default void init() {
}
default boolean isReadFromZipFileSupported() {
return false;
}
}