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.FitsException;
43  import nom.tam.fits.compression.algorithm.api.ICompressOption;
44  import nom.tam.fits.compression.algorithm.api.ICompressor;
45  import nom.tam.fits.compression.algorithm.api.ICompressorControl;
46  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.ByteGZipCompressor;
47  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.DoubleGZipCompressor;
48  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.FloatGZipCompressor;
49  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.IntGZipCompressor;
50  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.LongGZipCompressor;
51  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor.ShortGZipCompressor;
52  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.ByteGZip2Compressor;
53  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.DoubleGZip2Compressor;
54  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.FloatGZip2Compressor;
55  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.IntGZip2Compressor;
56  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.LongGZip2Compressor;
57  import nom.tam.fits.compression.algorithm.gzip2.GZip2Compressor.ShortGZip2Compressor;
58  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.ByteHCompressor;
59  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.DoubleHCompressor;
60  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.FloatHCompressor;
61  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.IntHCompressor;
62  import nom.tam.fits.compression.algorithm.hcompress.HCompressor.ShortHCompressor;
63  import nom.tam.fits.compression.algorithm.hcompress.HCompressorQuantizeOption;
64  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.BytePLIOCompressor;
65  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.IntPLIOCompressor;
66  import nom.tam.fits.compression.algorithm.plio.PLIOCompress.ShortPLIOCompressor;
67  import nom.tam.fits.compression.algorithm.quant.QuantizeOption;
68  import nom.tam.fits.compression.algorithm.quant.QuantizeProcessor.DoubleQuantCompressor;
69  import nom.tam.fits.compression.algorithm.quant.QuantizeProcessor.FloatQuantCompressor;
70  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.ByteRiceCompressor;
71  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.DoubleRiceCompressor;
72  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.FloatRiceCompressor;
73  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.IntRiceCompressor;
74  import nom.tam.fits.compression.algorithm.rice.RiceCompressor.ShortRiceCompressor;
75  import nom.tam.fits.compression.algorithm.rice.RiceQuantizeCompressOption;
76  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.ByteNoCompressCompressor;
77  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.DoubleNoCompressCompressor;
78  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.FloatNoCompressCompressor;
79  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.IntNoCompressCompressor;
80  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.LongNoCompressCompressor;
81  import nom.tam.fits.compression.algorithm.uncompressed.NoCompressCompressor.ShortNoCompressCompressor;
82  import nom.tam.fits.compression.provider.api.ICompressorProvider;
83  import nom.tam.fits.compression.provider.param.api.ICompressHeaderParameter;
84  import nom.tam.fits.compression.provider.param.api.ICompressParameters;
85  import nom.tam.fits.compression.provider.param.base.CompressParameters;
86  import nom.tam.fits.compression.provider.param.hcompress.HCompressParameters;
87  import nom.tam.fits.compression.provider.param.rice.RiceCompressParameters;
88  
89  /**
90   * (<i>for internal use</i>) Standard implementation of the {@code ICompressorProvider} interface.
91   */
92  @SuppressWarnings({"javadoc", "deprecation"})
93  public class CompressorProvider implements ICompressorProvider {
94  
95      /**
96       * private implementation of the tile compression provider, all is based on the option based constructor of the
97       * compressors.
98       */
99      protected static class TileCompressorControl implements ICompressorControl {
100 
101         private final Constructor<ICompressor<Buffer>>[] constructors;
102 
103         private Class<? extends ICompressOption> optionClass;
104 
105         private Class<?> quantType;
106 
107         @SuppressWarnings("unchecked")
108         protected TileCompressorControl(Class<?> compressorClass) {
109             constructors = (Constructor<ICompressor<Buffer>>[]) compressorClass.getConstructors();
110             for (Constructor<ICompressor<Buffer>> c : constructors) {
111                 if (c.getParameterTypes().length == 1) {
112                     optionClass = (Class<? extends ICompressOption>) c.getParameterTypes()[0];
113                     break;
114                 }
115             }
116         }
117 
118         /**
119          * Sets the floating-point type to quantize to use for this tile compressor.
120          *
121          * @param  floatingPointType Floating-point primitive type to quantize. Must be either <code>double.class</code>
122          *                               or else <code>float.class</code>.
123          *
124          * @return                   itself
125          *
126          * @since                    1.18
127          */
128         protected TileCompressorControl setQuantType(Class<?> floatingPointType) {
129             quantType = floatingPointType;
130             return this;
131         }
132 
133         @Override
134         public boolean compress(Buffer in, ByteBuffer out, ICompressOption option) {
135             try {
136                 return newCompressor(option).compress(in, out);
137             } catch (Exception e) {
138                 LOG.log(Level.FINE, "could not compress using " + constructors[0].getName()
139                         + " must fallback to other compression method", e);
140                 return false;
141             }
142         }
143 
144         @Override
145         public void decompress(ByteBuffer in, Buffer out, ICompressOption option) {
146             try {
147                 newCompressor(option).decompress(in, out);
148             } catch (Exception e) {
149                 throw new IllegalStateException("could not decompress " + constructors[0].getName(), e);
150             }
151         }
152 
153         @Override
154         public ICompressOption option() {
155             ICompressOption option = null;
156             if (optionClass != null) {
157                 try {
158                     option = optionClass.getDeclaredConstructor().newInstance();
159                 } catch (Exception e) {
160                     throw new IllegalStateException("could not instantiate option class for " + constructors[0].getName(),
161                             e);
162                 }
163             }
164 
165             if (option == null) {
166                 option = NULL_OPTION;
167             }
168 
169             if (quantType != null) {
170                 return new QuantizeOption(option);
171             }
172 
173             return option;
174         }
175 
176         @SuppressWarnings({"unchecked", "rawtypes"})
177         private ICompressor<Buffer> newCompressor(ICompressOption option)
178                 throws FitsException, InstantiationException, IllegalAccessException, InvocationTargetException {
179             ICompressor<Buffer> compressor = null;
180             QuantizeOption quantOption = null;
181 
182             if (option instanceof QuantizeOption) {
183                 quantOption = (QuantizeOption) option;
184                 option = quantOption.getCompressOption();
185             }
186 
187             if (option == NULL_OPTION) {
188                 option = null;
189             }
190 
191             try {
192                 for (Constructor<ICompressor<Buffer>> c : constructors) {
193                     Class<?>[] parms = c.getParameterTypes();
194 
195                     if (parms.length == 0 && option == null) {
196                         // Use constructor without special options...
197                         compressor = c.newInstance();
198                         break;
199                     }
200 
201                     if (parms.length == 1 && option != null) {
202                         // Use constructor with the option
203                         Class<? extends ICompressOption> p = (Class<? extends ICompressOption>) parms[0];
204                         if (quantOption != null && p.isAssignableFrom(quantOption.getClass())) {
205                             compressor = c.newInstance(quantOption);
206                             quantOption = null; // Don't wrap in a quantizer below...
207                             break;
208                         }
209                         if (p.isAssignableFrom(option.getClass())) {
210                             compressor = c.newInstance(option);
211                             break;
212                         }
213                     }
214 
215                 }
216 
217                 if (compressor == null) {
218                     throw new FitsException("Could not instantiate (de)compressor for the specified options");
219                 }
220 
221                 if (quantOption != null && quantType != null) {
222                     if (quantType.equals(double.class)) {
223                         return (ICompressor) new DoubleQuantCompressor(quantOption, (ICompressor) compressor);
224                     }
225                     if (quantType.equals(float.class)) {
226                         return (ICompressor) new FloatQuantCompressor(quantOption, (ICompressor) compressor);
227                     }
228                 }
229 
230                 return compressor;
231             } catch (Exception e) {
232                 e.printStackTrace();
233             }
234 
235             return null;
236         }
237     }
238 
239     private static final ICompressOption NULL_OPTION = new ICompressOption() {
240 
241         @Override
242         public ICompressOption copy() {
243             return this;
244         }
245 
246         @Override
247         public ICompressParameters getCompressionParameters() {
248             return NULL_PARAMETERS;
249         }
250 
251         @Override
252         public boolean isLossyCompression() {
253             return false;
254         }
255 
256         @Override
257         public void setParameters(ICompressParameters parameters) {
258         }
259 
260         @Override
261         public ICompressOption setTileHeight(int value) {
262             return this;
263         }
264 
265         @Override
266         public ICompressOption setTileWidth(int value) {
267             return this;
268         }
269 
270         @Override
271         public <T> T unwrap(Class<T> clazz) {
272             return clazz.isAssignableFrom(this.getClass()) ? clazz.cast(this) : null;
273         }
274     };
275 
276     private static final ICompressParameters NULL_PARAMETERS = new CompressParameters() {
277 
278         @Override
279         protected ICompressHeaderParameter[] headerParameters() {
280             return new ICompressHeaderParameter[0];
281         }
282 
283         @Override
284         public ICompressParameters copy(ICompressOption option) {
285             return this;
286         }
287     };
288 
289     // @formatter:off
290     private static final Class<?>[][] AVAILABLE_COMPRESSORS = {//
291             {ByteRiceCompressor.class, RiceCompressParameters.class}, //
292             {ShortRiceCompressor.class, RiceCompressParameters.class}, //
293             {IntRiceCompressor.class, RiceCompressParameters.class}, //
294             {FloatRiceCompressor.class, RiceQuantizeCompressOption.class}, //
295             {DoubleRiceCompressor.class, RiceQuantizeCompressOption.class}, //
296             {BytePLIOCompressor.class}, //
297             {ShortPLIOCompressor.class}, //
298             {IntPLIOCompressor.class}, //
299             {ByteHCompressor.class, HCompressParameters.class}, //
300             {ShortHCompressor.class, HCompressParameters.class}, //
301             {IntHCompressor.class, HCompressParameters.class}, //
302             {FloatHCompressor.class, HCompressorQuantizeOption.class}, //
303             {DoubleHCompressor.class, HCompressorQuantizeOption.class}, //
304             {ByteGZip2Compressor.class}, //
305             {ShortGZip2Compressor.class}, //
306             {IntGZip2Compressor.class}, //
307             {FloatGZip2Compressor.class}, //
308             {DoubleGZip2Compressor.class}, //
309             {LongGZip2Compressor.class}, //
310             {ByteGZipCompressor.class}, //
311             {ShortGZipCompressor.class}, //
312             {IntGZipCompressor.class}, //
313             {LongGZipCompressor.class}, //
314             {FloatGZipCompressor.class}, //
315             {DoubleGZipCompressor.class}, //
316             {ByteNoCompressCompressor.class}, //
317             {ShortNoCompressCompressor.class}, //
318             {IntNoCompressCompressor.class}, //
319             {LongNoCompressCompressor.class}, //
320             {FloatNoCompressCompressor.class}, //
321             {DoubleNoCompressCompressor.class}};
322     // @formatter:on
323 
324     private static final CompressorControlNameComputer NAME_COMPUTER = new CompressorControlNameComputer();
325 
326     /**
327      * logger to log to.
328      */
329     private static final Logger LOG = Logger.getLogger(CompressorProvider.class.getName());
330 
331     public static ICompressorControl findCompressorControl(String quantAlgorithm, String compressionAlgorithm,
332             Class<?> baseType) {
333         for (ICompressorProvider iTileCompressorProvider : ServiceLoader.load(ICompressorProvider.class,
334                 Thread.currentThread().getContextClassLoader())) {
335             ICompressorControl result = iTileCompressorProvider.createCompressorControl(quantAlgorithm,
336                     compressionAlgorithm, baseType);
337             if (result != null) {
338                 return result;
339             }
340         }
341         return new CompressorProvider().createCompressorControl(quantAlgorithm, compressionAlgorithm, baseType);
342     }
343 
344     @Override
345     public ICompressorControl createCompressorControl(String quantAlgorithm, String compressionAlgorithm,
346             Class<?> baseType) {
347         Class<?> quantType = null;
348 
349         if (quantAlgorithm != null) {
350             // Standard compression via 32-bit integers...
351             if (baseType.equals(double.class) || baseType.equals(float.class)) {
352                 quantType = baseType;
353                 baseType = int.class;
354                 quantAlgorithm = null;
355             }
356         }
357 
358         String className = NAME_COMPUTER.createCompressorClassName(quantAlgorithm, compressionAlgorithm, baseType);
359 
360         for (Class<?>[] types : AVAILABLE_COMPRESSORS) {
361             Class<?> compressorClass = types[0];
362             if (compressorClass.getSimpleName().equals(className)) {
363                 TileCompressorControl tc = new TileCompressorControl(compressorClass);
364                 tc.setQuantType(quantType);
365                 return tc;
366             }
367         }
368 
369         return null;
370     }
371 }