package org.rascalmpl.uri; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.compress.compressors.CompressorException; import org.apache.commons.compress.compressors.CompressorStreamFactory; import org.rascalmpl.value.ISourceLocation; public class CompressedStreamResolver implements ISourceLocationInputOutput { private final URIResolverRegistry registry; public CompressedStreamResolver(URIResolverRegistry registry) { this.registry = registry; } @Override public String scheme() { return "compressed"; } private ISourceLocation getActualURI(ISourceLocation wrappedURI) throws IOException { String scheme = wrappedURI.getScheme(); if (scheme.length() <= "compressed+".length()) { throw new IOException("Invalid scheme: \"" + scheme +"\""); } String actualScheme = scheme.substring("compressed+".length()); try { return URIUtil.changeScheme(wrappedURI, actualScheme); } catch (URISyntaxException e) { throw new IOException("Invalid URI: \"" + wrappedURI +"\"", e); } } @Override public InputStream getInputStream(ISourceLocation uri) throws IOException { InputStream result = registry.getInputStream(getActualURI(uri)); if (result != null) { try { String detectedCompression = detectCompression(uri); if (detectedCompression == null) { // let's use the automatic detection of the Compressor Stream library factory return new CompressorStreamFactory().createCompressorInputStream(new BufferedInputStream(result)); } return new CompressorStreamFactory().createCompressorInputStream(detectedCompression, result); } catch (CompressorException e) { result.close(); throw new IOException("We cannot decompress this file.", e); } } return null; } @Override public OutputStream getOutputStream(ISourceLocation uri, boolean append) throws IOException { OutputStream result = registry.getOutputStream(getActualURI(uri), append); if (result != null) { String detectedCompression = detectCompression(uri); if (detectedCompression == null) { throw new IOException("We could not detect the compression based on the extension."); } try { return new BufferedOutputStream(new CompressorStreamFactory().createCompressorOutputStream(detectedCompression, result)); } catch (CompressorException e) { result.close(); throw new IOException("We cannot compress this kind of file. (Only gz,xz,bz2 have write support)",e); } } return null; } private final Pattern getExtension = Pattern.compile("\\.([a-zA-Z0-9\\-]*)$"); private String detectCompression(ISourceLocation uri) throws IOException { Matcher m = getExtension.matcher(uri.getPath()); if (m.find()) { switch (m.group(1)) { case "gz": return CompressorStreamFactory.GZIP; case "bz": return CompressorStreamFactory.BZIP2; case "bz2": return CompressorStreamFactory.BZIP2; case "lzma" : return CompressorStreamFactory.LZMA; case "Z" : return CompressorStreamFactory.Z; case "xz": return CompressorStreamFactory.XZ; case "7z": case "zip": case "rar": case "tar": case "jar": throw new IOException("We only support compression formats. The extension " + m.group(1) + " is an archive format, which could decompress to more than one file."); } } return null; } @Override public boolean exists(ISourceLocation uri) { try { return registry.exists(getActualURI(uri)); } catch (IOException e) { return false; } } @Override public Charset getCharset(ISourceLocation uri) throws IOException { return registry.getCharset(getActualURI(uri)); } @Override public boolean isDirectory(ISourceLocation uri) { try { return registry.isDirectory(getActualURI(uri)); } catch (IOException e) { return false; } } @Override public boolean isFile(ISourceLocation uri) { try { return registry.isFile(getActualURI(uri)); } catch (IOException e) { return false; } } @Override public long lastModified(ISourceLocation uri) throws IOException { return registry.lastModified(getActualURI(uri)); } @Override public String[] list(ISourceLocation uri) throws IOException { return registry.listEntries(getActualURI(uri)); } @Override public void mkDirectory(ISourceLocation uri) throws IOException { registry.mkDirectory(getActualURI(uri)); } @Override public void remove(ISourceLocation uri) throws IOException { registry.remove(getActualURI(uri)); } @Override public boolean supportsHost() { return true; } }