001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import java.io.File; 005import java.io.IOException; 006import java.io.InputStream; 007import java.io.OutputStream; 008import java.nio.charset.StandardCharsets; 009import java.nio.file.Files; 010import java.nio.file.InvalidPathException; 011import java.util.zip.GZIPInputStream; 012import java.util.zip.GZIPOutputStream; 013import java.util.zip.ZipEntry; 014import java.util.zip.ZipInputStream; 015import java.util.zip.ZipOutputStream; 016 017import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 018import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; 019import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; 020import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; 021import org.openstreetmap.josm.tools.Logging; 022import org.openstreetmap.josm.tools.Utils; 023 024/** 025 * An enum representing the compression type of a resource. 026 */ 027public enum Compression { 028 /** 029 * no compression 030 */ 031 NONE, 032 /** 033 * bzip2 compression 034 */ 035 BZIP2, 036 /** 037 * gzip compression 038 */ 039 GZIP, 040 /** 041 * zip compression 042 */ 043 ZIP, 044 /** 045 * xz compression 046 */ 047 XZ; 048 049 /** 050 * Determines the compression type depending on the suffix of {@code name}. 051 * @param name File name including extension 052 * @return the compression type 053 */ 054 public static Compression byExtension(String name) { 055 return name != null && name.endsWith(".gz") 056 ? GZIP 057 : name != null && (name.endsWith(".bz2") || name.endsWith(".bz")) 058 ? BZIP2 059 : name != null && name.endsWith(".zip") 060 ? ZIP 061 : name != null && name.endsWith(".xz") 062 ? XZ 063 : NONE; 064 } 065 066 /** 067 * Determines the compression type based on the content type (MIME type). 068 * @param contentType the content type 069 * @return the compression type 070 */ 071 public static Compression forContentType(String contentType) { 072 switch (contentType) { 073 case "application/zip": 074 return ZIP; 075 case "application/x-gzip": 076 return GZIP; 077 case "application/x-bzip2": 078 return BZIP2; 079 case "application/x-xz": 080 return XZ; 081 default: 082 return NONE; 083 } 084 } 085 086 /** 087 * Returns an un-compressing {@link InputStream} for {@code in}. 088 * @param in raw input stream 089 * @return un-compressing input stream 090 * 091 * @throws IOException if any I/O error occurs 092 */ 093 public InputStream getUncompressedInputStream(InputStream in) throws IOException { 094 switch (this) { 095 case BZIP2: 096 return getBZip2InputStream(in); 097 case GZIP: 098 return getGZipInputStream(in); 099 case ZIP: 100 return getZipInputStream(in); 101 case XZ: 102 return getXZInputStream(in); 103 case NONE: 104 default: 105 return in; 106 } 107 } 108 109 /** 110 * Returns a XZ input stream wrapping given input stream. 111 * @param in The raw input stream 112 * @return a XZ input stream wrapping given input stream, or {@code null} if {@code in} is {@code null} 113 * @throws IOException if the given input stream does not contain valid BZ2 header 114 * @since 13350 115 */ 116 public static XZCompressorInputStream getXZInputStream(InputStream in) throws IOException { 117 if (in == null) { 118 return null; 119 } 120 return new XZCompressorInputStream(in, true); 121 } 122 123 /** 124 * Returns a Bzip2 input stream wrapping given input stream. 125 * @param in The raw input stream 126 * @return a Bzip2 input stream wrapping given input stream, or {@code null} if {@code in} is {@code null} 127 * @throws IOException if the given input stream does not contain valid BZ2 header 128 * @since 12772 (moved from {@link Utils}, there since 7867) 129 */ 130 public static BZip2CompressorInputStream getBZip2InputStream(InputStream in) throws IOException { 131 if (in == null) { 132 return null; 133 } 134 return new BZip2CompressorInputStream(in, /* see #9537 */ true); 135 } 136 137 /** 138 * Returns a Gzip input stream wrapping given input stream. 139 * @param in The raw input stream 140 * @return a Gzip input stream wrapping given input stream, or {@code null} if {@code in} is {@code null} 141 * @throws IOException if an I/O error has occurred 142 * @since 12772 (moved from {@link Utils}, there since 7119) 143 */ 144 public static GZIPInputStream getGZipInputStream(InputStream in) throws IOException { 145 if (in == null) { 146 return null; 147 } 148 return new GZIPInputStream(in); 149 } 150 151 /** 152 * Returns a Zip input stream wrapping given input stream. 153 * @param in The raw input stream 154 * @return a Zip input stream wrapping given input stream, or {@code null} if {@code in} is {@code null} 155 * @throws IOException if an I/O error has occurred 156 * @since 12772 (moved from {@link Utils}, there since 7119) 157 */ 158 public static ZipInputStream getZipInputStream(InputStream in) throws IOException { 159 if (in == null) { 160 return null; 161 } 162 ZipInputStream zis = new ZipInputStream(in, StandardCharsets.UTF_8); 163 // Positions the stream at the beginning of first entry 164 ZipEntry ze = zis.getNextEntry(); 165 if (ze != null && Logging.isDebugEnabled()) { 166 Logging.debug("Zip entry: {0}", ze.getName()); 167 } 168 return zis; 169 } 170 171 /** 172 * Returns an un-compressing {@link InputStream} for the {@link File} {@code file}. 173 * @param file file 174 * @return un-compressing input stream 175 * @throws IOException if any I/O error occurs 176 */ 177 public static InputStream getUncompressedFileInputStream(File file) throws IOException { 178 try { 179 InputStream in = Files.newInputStream(file.toPath()); // NOPMD 180 try { 181 return byExtension(file.getName()).getUncompressedInputStream(in); 182 } catch (IOException e) { 183 Utils.close(in); 184 throw e; 185 } 186 } catch (InvalidPathException e) { 187 throw new IOException(e); 188 } 189 } 190 191 /** 192 * Returns a compressing {@link OutputStream} for {@code out}. 193 * @param out raw output stream 194 * @return compressing output stream 195 * 196 * @throws IOException if any I/O error occurs 197 */ 198 public OutputStream getCompressedOutputStream(OutputStream out) throws IOException { 199 switch (this) { 200 case BZIP2: 201 return new BZip2CompressorOutputStream(out); 202 case GZIP: 203 return new GZIPOutputStream(out); 204 case ZIP: 205 return new ZipOutputStream(out, StandardCharsets.UTF_8); 206 case XZ: 207 return new XZCompressorOutputStream(out); 208 case NONE: 209 default: 210 return out; 211 } 212 } 213 214 /** 215 * Returns a compressing {@link OutputStream} for the {@link File} {@code file}. 216 * @param file file 217 * @return compressing output stream 218 * 219 * @throws IOException if any I/O error occurs 220 * @throws InvalidPathException if a Path object cannot be constructed from the abstract path 221 */ 222 public static OutputStream getCompressedFileOutputStream(File file) throws IOException { 223 OutputStream out = Files.newOutputStream(file.toPath()); // NOPMD 224 try { 225 return byExtension(file.getName()).getCompressedOutputStream(out); 226 } catch (IOException e) { 227 Utils.close(out); 228 throw e; 229 } 230 } 231}