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