All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.gbin.GbinObjectReader Maven / Gradle / Ivy

There is a newer version: 4.3
Show newest version
package uk.ac.starlink.gbin;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Provides read services for an object implementing the
 * gaia.cu1.tools.dal.gbin.GbinReader interface.
 * This object can read the elements stored in a GBIN file.
 *
 * 

Access is via reflection, so I don't need GaiaTools on the classpath * at build time, for several reasons: *

    *
  • The GbinReader class is targeted at Java 1.7, and at time of * writing STIL is targeted at java 1.5
  • *
  • GbinReader and its dependencies are probably large and complicated * (though I haven't actually checked that)
  • *
  • To use this class for most actual GBIN files, you're going to * need Gaia data model classes on the classpath which I have no * intention of packaging with STIL. If you have the data model * classes on the path, there's a good chance that you've got * the relevant bits of GaiaTools as well.
  • *
  • It can benefit from future GbinReader implementations without * requiring updates to the code in STIL (as long as the basic * interfaces don't change).
  • *
* * @author Mark Taylor * @since 13 Aug 2014 */ public class GbinObjectReader { private final Object gbinReaderObj_; private final Method hasNextMethod_; private final Method nextMethod_; private static final Object[] ARGS0 = new Object[ 0 ]; private static boolean isGaiaToolsInit_; private static final Logger logger_ = Logger.getLogger( "uk.ac.starlink.gbin" ); /** * Constructor. * * @param gbinReaderObj object implementing * gaia.cu1.tools.dal.gbin.GbinReader * @throws IllegalArgumentException if gbinReaderObj * doesn't appear to be a GbinReader */ public GbinObjectReader( Object gbinReaderObj ) { gbinReaderObj_ = gbinReaderObj; Class clazz = gbinReaderObj.getClass(); hasNextMethod_ = getNoArgMethod( clazz, "hasNext", boolean.class ); nextMethod_ = getNoArgMethod( clazz, "next", null ); } /** * Indicates whether this reader can read another element. * * @return true if another record will be returned by next */ public boolean hasNext() throws IOException { return Boolean.TRUE.equals( invokeNoArgMethod( hasNextMethod_ ) ); } /** * Reads the next record. * * @return object implementing gaia.cu1.tools.dm.GaiaRoot */ public Object next() throws IOException { return invokeNoArgMethod( nextMethod_ ); } /** * Invokes a no-arg method on this reader's GbinReader object, * translating exceptions appropriately. * * @param method method to invoke with no arguments * @return method return value * @throws IOException with an informative method if something * went wrong */ private Object invokeNoArgMethod( Method method ) throws IOException { try { return method.invoke( gbinReaderObj_, ARGS0 ); } catch ( IllegalAccessException e ) { throw new AssertionError( e ); } catch ( IllegalArgumentException e ) { throw new AssertionError( e ); } catch ( InvocationTargetException e ) { Throwable targetEx = e.getTargetException(); if ( targetEx instanceof ClassNotFoundException ) { throw (IOException) new IOException( "Missing GBIN object class" + " - probably lacking DM jars" ) .initCause( e ); } else { throw (IOException) new IOException( targetEx.getMessage() ) .initCause( e ); } } } /** * Returns a public instance method with no arguments and a given * signature for a given class. * * @param clazz class to query * @param name method name * @param retClazz method return type * @return method object * @throws IllegalArgumentException with informative message * if method does not exist */ private static Method getNoArgMethod( Class clazz, String name, Class retClazz ) { Method method; try { method = clazz.getMethod( name ); int mods = method.getModifiers(); if ( Modifier.isStatic( mods ) || ! Modifier.isPublic( mods ) || ( retClazz != null && ! retClazz.equals( method.getReturnType() ) ) ) { method = null; } } catch ( NoSuchMethodException e ) { method = null; } if ( method == null ) { throw new IllegalArgumentException( "Object of class " + clazz.getName() + " is not a GbinReader" + " (no suitable " + name + "()" + " method)" ); } return method; } /** * Attempts to construct a GbinObjectReader that can read records * from a given input stream. * * @param in input stream containing a GBIN file * @return gbin object reader */ public static GbinObjectReader createReader( InputStream in ) throws IOException { initGaiaTools(); return new GbinObjectReader( createGbinReaderObject( in ) ); } /** * Indicates whether a buffer containing the first few bytes of a * file look like a GBIN magic number. * *

Note this has not been exhaustively tested with all known * GBIN variants. * * @param intro first few bytes to test * @return true if buffer looks like it could be the start of a GBIN file */ public static boolean isMagic( byte[] intro ) { if ( intro.length < 13 ) { return false; } long magic = ((intro[ 7 ] & 0xffL) << 56) | ((intro[ 6 ] & 0xffL) << 48) | ((intro[ 5 ] & 0xffL) << 40) | ((intro[ 4 ] & 0xffL) << 32) | ((intro[ 3 ] & 0xffL) << 24) | ((intro[ 2 ] & 0xffL) << 16) | ((intro[ 1 ] & 0xffL) << 8) | ((intro[ 0 ] & 0xffL) << 0); /* Pre-V3 GBIN file - no magic number, but we can see if it's * a java serialization stream. */ if ( ( magic & 0xffff ) == 0xedac ) { int jserVers = ((intro[ 2 ] & 0xff) << 8) | ((intro[ 3 ] & 0xff) << 0); logger_.info( "Java serialised stream, may be GBIN v2 file" + " (java serialization version " + jserVers + ")" ); return true; } /* V3 or later GBIN file - see GAIA-C1-TN-ESAC-AH-004-1. */ else if ( magic == 0x1a0a0d4e49424789L ) { int gbinVers = ((intro[ 9 ] & 0xff) << 24) | ((intro[ 10 ] & 0xff) << 16) | ((intro[ 11 ] & 0xff) << 8) | ((intro[ 12 ] & 0xff) << 0); logger_.info( "Post-v2 GBIN file identified" + " (GBIN version " + gbinVers + ")" ); return true; } /* No known GBIN format. */ return false; } /** * Performs GaiaTools setup required before GbinReaders are used etc. * Called by {@link #createReader createReader}. * Calls after the first one do nothing, but are harmless and cheap. * Uses reflection. */ public static synchronized void initGaiaTools() { if ( ! isGaiaToolsInit_ ) { String loaderClassName = "gaia.cu1.tools.util.props.PropertyLoader"; String loaderMethodName = "load"; String sig = loaderClassName + "." + loaderMethodName + "()"; logger_.info( "Invoking " + sig ); try { Class.forName( loaderClassName ) .getMethod( loaderMethodName ) .invoke( null, new Object[ 0 ] ); } catch ( Throwable e ) { logError( Level.WARNING, "Failed to invoke " + sig, e ); } isGaiaToolsInit_ = true; } } /** * Constructs a GbinReader from a given input stream. * Uses reflection to invoke * gaia.cu1.tools.dal.gbin.GbinFactory.getGbinReader(). * * @param in input stream assumed to contain a GBIN file * @return object implementing * gaia.cu1.tools.dal.gbin.GbinReader interface * @throws IOException with informative message if something goes wrong, * including reflection trouble */ public static Object createGbinReaderObject( InputStream in ) throws IOException { try { Class factClazz = Class.forName( "gaia.cu1.tools.dal.gbin.GbinFactory" ); Method getReaderMethod = factClazz.getMethod( "getGbinReader", InputStream.class ); try { return getReaderMethod.invoke( null, in ); } catch ( IllegalArgumentException e ) { throw (IOException) new IOException( "Problem with GaiaTools classes" ) .initCause( e ); } } catch ( ClassNotFoundException e ) { throw (IOException) new IOException( "GaiaTools classes not available?" ) .initCause( e ); } catch ( NoSuchMethodException e ) { throw (IOException) new IOException( "Problem with GaiaTools classes" ) .initCause( e ); } catch ( IllegalAccessException e ) { throw (IOException) new IOException( "Problem with GaiaTools classes" ) .initCause( e ); } catch ( InvocationTargetException e ) { throw (IOException) new IOException( e.getTargetException().getMessage() ) .initCause( e ); } } /** * Write a logging message including an exception. * This is a drop-in replacement for the method * {@link java.util.logging.Logger#log(java.util.logging.Level, * String, Throwable)}; * it's required because loading the GaiaTools classes seems * to mess up the system configuration done by stilts/topcat on startup, * so this implementation includes the actual exception stringification * in the log message itself, as well as passing the exception * itself to the logging system (which seems to get ignored after loading * GaiaTools, even with stilts -debug). * Exception text is also unlikely to frighten GBIN-competent users. * * @param level logging level * @param msg basic log message * @param error exception */ public static void logError( Level level, String msg, Throwable error ) { logger_.log( level, msg + " - " + error, error ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy