View Javadoc
1   package nom.tam.fits.compression.provider;
2   
3   /*
4    * #%L
5    * nom.tam FITS library
6    * %%
7    * Copyright (C) 1996 - 2024 nom-tam-fits
8    * %%
9    * This is free and unencumbered software released into the public domain.
10   *
11   * Anyone is free to copy, modify, publish, use, compile, sell, or
12   * distribute this software, either in source code form or as a compiled
13   * binary, for any purpose, commercial or non-commercial, and by any
14   * means.
15   *
16   * In jurisdictions that recognize copyright laws, the author or authors
17   * of this software dedicate any and all copyright interest in the
18   * software to the public domain. We make this dedication for the benefit
19   * of the public at large and to the detriment of our heirs and
20   * successors. We intend this dedication to be an overt act of
21   * relinquishment in perpetuity of all present and future rights to this
22   * software under copyright law.
23   *
24   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30   * OTHER DEALINGS IN THE SOFTWARE.
31   * #L%
32   */
33  
34  import java.lang.reflect.Constructor;
35  import java.lang.reflect.InvocationTargetException;
36  import java.nio.Buffer;
37  import java.nio.ByteBuffer;
38  import java.util.ServiceLoader;
39  import java.util.logging.Level;
40  import java.util.logging.Logger;
41  
42  import nom.tam.fits.compression.algorithm.api.ICompressOption;
43  import nom.tam.fits.compression.algorithm.api.ICompressor;
44  import nom.tam.fits.compression.algorithm.api.ICompressorControl;
45  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.ByteGZipCompressor;
46  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.DoubleGZipCompressor;
47  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.FloatGZipCompressor;
48  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.IntGZipCompressor;
49  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.LongGZipCompressor;
50  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.ShortGZipCompressor;
51  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.ByteGZip2Compressor;
52  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.DoubleGZip2Compressor;
53  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.FloatGZip2Compressor;
54  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.IntGZip2Compressor;
55  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.LongGZip2Compressor;
56  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.ShortGZip2Compressor;
57  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.ByteHCompressor;
58  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.DoubleHCompressor;
59  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.FloatHCompressor;
60  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.IntHCompressor;
61  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.ShortHCompressor;
62  import nom.tam.fits.compression.algorithm.hcompress.HCompressorQuantizeOption;
63  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.BytePLIOCompressor;
64  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.IntPLIOCompressor;
65  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.ShortPLIOCompressor;
66  import nom.tam.fits.compression.algorithm.quant.QuantizeOption;
67  import nom.tam.fits.compression.algorithm.quant.QuantizeProcessor.DoubleQuantCompressor;
68  import nom.tam.fits.compression.algorithm.quant.QuantizeProcessor.FloatQuantCompressor;
69  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.ByteRiceCompressor;
70  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.DoubleRiceCompressor;
71  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.FloatRiceCompressor;
72  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.IntRiceCompressor;
73  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.ShortRiceCompressor;
74  import nom.tam.fits.compression.algorithm.rice.RiceQuantizeCompressOption;
75  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.ByteNoCompressCompressor;
76  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.DoubleNoCompressCompressor;
77  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.FloatNoCompressCompressor;
78  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.IntNoCompressCompressor;
79  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.LongNoCompressCompressor;
80  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.ShortNoCompressCompressor;
81  import nom.tam.fits.compression.provider.api.ICompressorProvider;
82  import nom.tam.fits.compression.provider.param.api.ICompressHeaderParameter;
83  import nom.tam.fits.compression.provider.param.api.ICompressParameters;
84  import nom.tam.fits.compression.provider.param.base.CompressParameters;
85  import nom.tam.fits.compression.provider.param.hcompress.HCompressParameters;
86  import nom.tam.fits.compression.provider.param.rice.RiceCompressParameters;
87  
88  /**
89   * (<i>for internal use</i>) Standard implementation of the {@code ICompressorProvider} interface.
90   */
91  @SuppressWarnings({"javadoc", "deprecation"})
92  public class CompressorProvider implements ICompressorProvider {
93  
94      /**
95       * private implementation of the tile compression provider, all is based on the option based constructor of the
96       * compressors.
97       */
98      protected static class TileCompressorControl implements ICompressorControl {
99  
100         private final Constructor<ICompressor<Buffer>>[] constructors;
101 
102         private Class<? extends ICompressOption> optionClass;
103 
104         private Class<?> quantType;
105 
106         @SuppressWarnings("unchecked")
107         protected TileCompressorControl(Class<?> compressorClass) {
108             constructors = (Constructor<ICompressor<Buffer>>[]) compressorClass.getConstructors();
109             for (Constructor<ICompressor<Buffer>> c : constructors) {
110                 if (c.getParameterTypes().length == 1) {
111                     optionClass = (Class<? extends ICompressOption>) c.getParameterTypes()[0];
112                     break;
113                 }
114             }
115         }
116 
117         /**
118          * Sets the floating-point type to quantize to use for this tile compressor.
119          *
120          * @param  floatingPointType Floating-point primitive type to quantize. Must be either <code>double.class</code>
121          *                               or else <code>float.class</code>.
122          *
123          * @return                   itself
124          *
125          * @since                    1.18
126          */
127         protected TileCompressorControl setQuantType(Class<?> floatingPointType) {
128             quantType = floatingPointType;
129             return this;
130         }
131 
132         @Override
133         public boolean compress(Buffer in, ByteBuffer out, ICompressOption option) {
134             try {
135                 return newCompressor(option).compress(in, out);
136             } catch (Exception e) {
137                 LOG.log(Level.FINE, "could not compress using " + constructors[0].getName()
138                         + " must fallback to other compression method", e);
139                 return false;
140             }
141         }
142 
143         @Override
144         public void decompress(ByteBuffer in, Buffer out, ICompressOption option) {
145             try {
146                 newCompressor(option).decompress(in, out);
147             } catch (Exception e) {
148                 throw new IllegalStateException("could not decompress " + constructors[0].getName(), e);
149             }
150         }
151 
152         @Override
153         public ICompressOption option() {
154             ICompressOption option = null;
155             if (optionClass != null) {
156                 try {
157                     option = optionClass.getDeclaredConstructor().newInstance();
158                 } catch (Exception e) {
159                     throw new IllegalStateException("could not instantiate option class for " + constructors[0].getName(),
160                             e);
161                 }
162             }
163 
164             if (option == null) {
165                 option = NULL_OPTION;
166             }
167 
168             if (quantType != null) {
169                 return new QuantizeOption(option);
170             }
171 
172             return option;
173         }
174 
175         @SuppressWarnings({"unchecked", "rawtypes"})
176         private ICompressor<Buffer> newCompressor(ICompressOption option)
177                 throws InstantiationException, IllegalAccessException, InvocationTargetException {
178             ICompressor<Buffer> compressor = null;
179             QuantizeOption quantOption = null;
180 
181             if (option == NULL_OPTION) {
182                 option = null;
183             } else if (option instanceof QuantizeOption) {
184                 quantOption = (QuantizeOption) option;
185                 option = quantOption.getCompressOption();
186             }
187 
188             try {
189                 for (Constructor<ICompressor<Buffer>> c : constructors) {
190                     Class<?>[] parms = c.getParameterTypes();
191 
192                     if (option == null && parms.length == 0) {
193                         compressor = c.newInstance();
194                         break;
195                     }
196 
197                     if (option != null && parms.length == 1) {
198                         Class<? extends ICompressOption> p = (Class<? extends ICompressOption>) parms[0];
199                         if (quantOption != null && p.isAssignableFrom(quantOption.getClass())) {
200                             compressor = c.newInstance(quantOption);
201                             quantOption = null; // Don't wrap in a quantizer below...
202                             break;
203                         }
204                         if (p.isAssignableFrom(option.getClass())) {
205                             compressor = c.newInstance(option);
206                             break;
207                         }
208                     }
209                 }
210 
211                 if (quantOption != null && quantType != null) {
212                     if (quantType.equals(double.class)) {
213                         return (ICompressor) new DoubleQuantCompressor(quantOption, (ICompressor) compressor);
214                     }
215                     if (quantType.equals(float.class)) {
216                         return (ICompressor) new FloatQuantCompressor(quantOption, (ICompressor) compressor);
217                     }
218                 }
219 
220                 return compressor;
221             } catch (Exception e) {
222                 e.printStackTrace();
223             }
224 
225             return null;
226         }
227     }
228 
229     private static final ICompressOption NULL_OPTION = new ICompressOption() {
230 
231         @Override
232         public ICompressOption copy() {
233             return this;
234         }
235 
236         @Override
237         public ICompressParameters getCompressionParameters() {
238             return NULL_PARAMETERS;
239         }
240 
241         @Override
242         public boolean isLossyCompression() {
243             return false;
244         }
245 
246         @Override
247         public void setParameters(ICompressParameters parameters) {
248         }
249 
250         @Override
251         public ICompressOption setTileHeight(int value) {
252             return this;
253         }
254 
255         @Override
256         public ICompressOption setTileWidth(int value) {
257             return this;
258         }
259 
260         @Override
261         public <T> T unwrap(Class<T> clazz) {
262             return clazz.isAssignableFrom(this.getClass()) ? clazz.cast(this) : null;
263         }
264     };
265 
266     private static final ICompressParameters NULL_PARAMETERS = new CompressParameters() {
267 
268         @Override
269         protected ICompressHeaderParameter[] headerParameters() {
270             return new ICompressHeaderParameter[0];
271         }
272 
273         @Override
274         public ICompressParameters copy(ICompressOption option) {
275             return this;
276         }
277     };
278 
279     // @formatter:off
280     private static final Class<?>[][] AVAILABLE_COMPRESSORS = {//
281             {ByteRiceCompressor.class, RiceCompressParameters.class}, //
282             {ShortRiceCompressor.class, RiceCompressParameters.class}, //
283             {IntRiceCompressor.class, RiceCompressParameters.class}, //
284             {FloatRiceCompressor.class, RiceQuantizeCompressOption.class}, //
285             {DoubleRiceCompressor.class, RiceQuantizeCompressOption.class}, //
286             {BytePLIOCompressor.class}, //
287             {ShortPLIOCompressor.class}, //
288             {IntPLIOCompressor.class}, //
289             {ByteHCompressor.class, HCompressParameters.class}, //
290             {ShortHCompressor.class, HCompressParameters.class}, //
291             {IntHCompressor.class, HCompressParameters.class}, //
292             {FloatHCompressor.class, HCompressorQuantizeOption.class}, //
293             {DoubleHCompressor.class, HCompressorQuantizeOption.class}, //
294             {ByteGZip2Compressor.class}, //
295             {ShortGZip2Compressor.class}, //
296             {IntGZip2Compressor.class}, //
297             {FloatGZip2Compressor.class}, //
298             {DoubleGZip2Compressor.class}, //
299             {LongGZip2Compressor.class}, //
300             {ByteGZipCompressor.class}, //
301             {ShortGZipCompressor.class}, //
302             {IntGZipCompressor.class}, //
303             {LongGZipCompressor.class}, //
304             {FloatGZipCompressor.class}, //
305             {DoubleGZipCompressor.class}, //
306             {ByteNoCompressCompressor.class}, //
307             {ShortNoCompressCompressor.class}, //
308             {IntNoCompressCompressor.class}, //
309             {LongNoCompressCompressor.class}, //
310             {FloatNoCompressCompressor.class}, //
311             {DoubleNoCompressCompressor.class}};
312     // @formatter:on
313 
314     private static final CompressorControlNameComputer NAME_COMPUTER = new CompressorControlNameComputer();
315 
316     /**
317      * logger to log to.
318      */
319     private static final Logger LOG = Logger.getLogger(CompressorProvider.class.getName());
320 
321     public static ICompressorControl findCompressorControl(String quantAlgorithm, String compressionAlgorithm,
322             Class<?> baseType) {
323         for (ICompressorProvider iTileCompressorProvider : ServiceLoader.load(ICompressorProvider.class,
324                 Thread.currentThread().getContextClassLoader())) {
325             ICompressorControl result = iTileCompressorProvider.createCompressorControl(quantAlgorithm,
326                     compressionAlgorithm, baseType);
327             if (result != null) {
328                 return result;
329             }
330         }
331         return new CompressorProvider().createCompressorControl(quantAlgorithm, compressionAlgorithm, baseType);
332     }
333 
334     @Override
335     public ICompressorControl createCompressorControl(String quantAlgorithm, String compressionAlgorithm,
336             Class<?> baseType) {
337         Class<?> quantType = null;
338 
339         if (quantAlgorithm != null) {
340             // Standard compression via 32-bit integers...
341             if (baseType.equals(double.class) || baseType.equals(float.class)) {
342                 quantType = baseType;
343                 baseType = int.class;
344                 quantAlgorithm = null;
345             }
346         }
347 
348         String className = NAME_COMPUTER.createCompressorClassName(quantAlgorithm, compressionAlgorithm, baseType);
349 
350         for (Class<?>[] types : AVAILABLE_COMPRESSORS) {
351             Class<?> compressorClass = types[0];
352             if (compressorClass.getSimpleName().equals(className)) {
353                 TileCompressorControl tc = new TileCompressorControl(compressorClass);
354                 tc.setQuantType(quantType);
355                 return tc;
356             }
357         }
358 
359         return null;
360     }
361 }