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