File: CdfDump.java

package info (click to toggle)
jcdf 1.2.5%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 572 kB
  • sloc: java: 5,315; makefile: 198; sh: 98
file content (350 lines) | stat: -rw-r--r-- 11,620 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
package uk.ac.bristol.star.cdf.util;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import uk.ac.bristol.star.cdf.CdfReader;
import uk.ac.bristol.star.cdf.record.Buf;
import uk.ac.bristol.star.cdf.record.CdfDescriptorRecord;
import uk.ac.bristol.star.cdf.record.CdfField;
import uk.ac.bristol.star.cdf.record.GlobalDescriptorRecord;
import uk.ac.bristol.star.cdf.record.OffsetField;
import uk.ac.bristol.star.cdf.record.Record;
import uk.ac.bristol.star.cdf.record.RecordFactory;

/**
 * Utility to dump the records of a CDF file, optionally with field values.
 * Intended to be used fro the command line via the <code>main</code> method.
 * The function is roughly comparable to the <code>cdfirsdump</code>
 * command in the CDF distribution.
 *
 * <p>The output can optionally be written in HTML format.
 * The point of this is so that field values which represent pointers
 * to records can be displayed as hyperlinks, which makes it very easy
 * to chase pointers around the CDF file in a web browser.
 *
 * @author   Mark Taylor
 * @since    21 Jun 2013
 */
public class CdfDump {

    private final CdfReader crdr_;
    private final PrintStream out_;
    private final boolean writeFields_;
    private final boolean html_;

    /**
     * Constructor.
     *
     * @param  crdr  CDF reader
     * @param  out   output stream for listing
     * @param  writeFields   true to write field data as well as record IDs
     * @param  html   true to write output in HTML format
     */
    public CdfDump( CdfReader crdr, PrintStream out, boolean writeFields,
                    boolean html ) {
        crdr_ = crdr;
        out_ = out;
        writeFields_ = writeFields;
        html_ = html;
    }

    /**
     * Does the work, writing output.
     */
    public void run() throws IOException {
        Buf buf = crdr_.getBuf();
        RecordFactory recFact = crdr_.getRecordFactory();
        long offset = 8;  // magic number
        long leng = buf.getLength();
        long eof = leng;
        CdfDescriptorRecord cdr = null;
        GlobalDescriptorRecord gdr = null;
        long gdroff = -1;
        if ( html_ ) {
            out_.println( "<html><body><pre>" );
        }
        for ( int ix = 0; offset < eof; ix++ ) {
            Record rec = recFact.createRecord( buf, offset );
            dumpRecord( ix, rec, offset );
            if ( cdr == null && rec instanceof CdfDescriptorRecord ) {
                cdr = (CdfDescriptorRecord) rec;
                gdroff = cdr.gdrOffset;
            }
            if ( offset == gdroff && rec instanceof GlobalDescriptorRecord ) {
                gdr = (GlobalDescriptorRecord) rec;
                eof = gdr.eof;
            }
            offset += rec.getRecordSize();
        }
        if ( html_ ) {
            out_.println( "<hr />" );
        }
        long extra = leng - eof;
        if ( extra > 0 ) {
            out_.println( " + " + extra + " bytes after final record" );
        }
        if ( html_ ) {
            out_.println( "</pre></body></html>" );
        }
    }

    /**
     * Writes infromation about a single record to the output.
     *
     * @param   index  record index
     * @param   rec   recor object
     * @param   offset  byte offset into the file of the record
     */
    private void dumpRecord( int index, Record rec, long offset ) {
        StringBuffer sbuf = new StringBuffer();
        if ( html_ ) {
            sbuf.append( "<hr /><strong>" );
        }
        sbuf.append( index )
            .append( ":\t" )
            .append( rec.getRecordTypeAbbreviation() )
            .append( "\t" )
            .append( rec.getRecordType() )
            .append( "\t" )
            .append( rec.getRecordSize() )
            .append( "\t" )
            .append( formatOffsetId( offset ) );
        if ( html_ ) {
            sbuf.append( "</strong>" );
        }
        out_.println( sbuf.toString() );

        // If required write the field values.  Rather than list them
        // for each record type, just obtain them by introspection.
        if ( writeFields_ ) {
            Field[] fields = rec.getClass().getFields();
            for ( int i = 0; i < fields.length; i++ ) {
                Field field = fields[ i ];
                if ( isCdfRecordField( field ) ) {
                    String name = field.getName();
                    Object value;
                    try {
                        value = field.get( rec );
                    }
                    catch ( IllegalAccessException e ) {
                        throw new RuntimeException( "Reflection error", e );
                    }
                    out_.println( formatFieldValue( name, value,
                                                    isOffsetField( field ) ) );
                }
            }
        }
    }

    /** 
     * Determines whether a given object field is a field of the CDF record.
     *
     * @param   field  field of java Record subclass
     * @return  true iff field represents a field of the corresponding CDF
     *          record type
     */
    private boolean isCdfRecordField( Field field ) {
        if ( field.getAnnotation( CdfField.class ) != null ) {
            int mods = field.getModifiers();
            assert Modifier.isFinal( mods )
                && Modifier.isPublic( mods )
                && ! Modifier.isStatic( mods );
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Determines whetehr a given object field represents a file offset.
     *
     * @param  field  field of java Record subclass
     * @return  true iff field represents a scalar or array file offset value
     */
    private boolean isOffsetField( Field field ) {
        return field.getAnnotation( OffsetField.class ) != null;
    }

    /**
     * Formats a field name/value pair for output.
     *
     * @param   name  field name
     * @param  value  field value
     */
    private String formatFieldValue( String name, Object value,
                                     boolean isOffset ) {
        StringBuffer sbuf = new StringBuffer();
        sbuf.append( spaces( 4 ) );
        sbuf.append( name )
            .append( ":" );
        sbuf.append( spaces( 28 - sbuf.length() ) );
        if ( value == null ) {
        }
        else if ( value.getClass().isArray() ) {
            int len = Array.getLength( value );
            if ( isOffset ) {
                assert value instanceof long[];
                long[] larray = (long[]) value;
                for ( int i = 0; i < len; i++ ) {
                    if ( i > 0 ) {
                        sbuf.append( ", " );
                    }
                    sbuf.append( formatOffsetRef( larray[ i ] ) );
                }
            }
            else {
                for ( int i = 0; i < len; i++ ) {
                    if ( i > 0 ) {
                        sbuf.append( ", " );
                    }
                    sbuf.append( Array.get( value, i ) );
                }
            }
        }
        else if ( isOffset ) {
            assert value instanceof Long;
            sbuf.append( formatOffsetRef( ((Long) value).longValue() ) );
        }
        else {
            sbuf.append( value.toString() );
        }
        return sbuf.toString();
    }

    /**
     * Format a value for output if it represents a possible target of
     * a pointer.
     *
     * @param  offset  pointer target value
     * @return   string for output
     */
    private String formatOffsetId( long offset ) {
        String txt = "0x" + Long.toHexString( offset );
        return html_ ? "<a name='" + txt + "'>" + txt + "</a>"
                     : txt;
    }

    /**
     * Format a value for output if it apparentl represents a pointer
     * to a particular file offset.
     *
     * @param  offset  target file offset
     * @return  string for output
     */
    private String formatOffsetRef( long offset ) {
        String txt = "0x" + Long.toHexString( offset );

        // Only format strictly positive values.  In some circumstances
        // -1 and 0 are used as special values indicating no reference exists.
        // The first record in any case starts at 0x8 (after the magic numbers)
        // so any such values can't be genuine offsets.
        return ( html_ && offset > 0L )
             ? "<a href='#" + txt + "'>" + txt + "</a>"
             : txt;
    }

    /**
     * Construct a padding string.
     *
     * @param   count   number of spaces
     * @return  string composed only of <code>count</code> spaces
     */
    static String spaces( int count ) {
        StringBuffer sbuf = new StringBuffer( count );
        for ( int i = 0; i < count; i++ ) {
            sbuf.append( ' ' );
        }
        return sbuf.toString();
    }

    /**
     * Does the work for the command line tool, handling arguments.
     * Sucess is indicated by the return value.
     *
     * @param  args   command-line arguments
     * @return   0 for success, non-zero for failure
     */
    public static int runMain( String[] args ) throws IOException {
        String usage = new StringBuffer()
           .append( "\n   Usage:" )
           .append( CdfDump.class.getName() )
           .append( " [-help]" )
           .append( " [-verbose]" )
           .append( " [-fields]" )
           .append( " [-html]" )
           .append( " <cdf-file>" )
           .append( "\n" )
           .toString();

        // Process arguments.
        List<String> argList = new ArrayList<String>( Arrays.asList( args ) );
        int verb = 0;
        File file = null;
        boolean writeFields = false;
        boolean html = false;
        for ( Iterator<String> it = argList.iterator(); it.hasNext(); ) {
            String arg = it.next();
            if ( arg.equals( "-html" ) ) {
                it.remove();
                html = true;
            }
            else if ( arg.startsWith( "-h" ) ) {
                it.remove();
                System.out.println( usage );
                return 0;
            }
            else if ( arg.equals( "-v" ) || arg.equals( "-verbose" ) ) {
                it.remove();
                verb++;
            }
            else if ( arg.equals( "+v" ) || arg.equals( "+verbose" ) ) {
                it.remove();
                verb--;
            }
            else if ( arg.startsWith( "-field" ) ) {
                it.remove();
                writeFields = true;
            }
            else if ( file == null ) {
                it.remove();
                file = new File( arg );
            }
        }

        // Validate arguments.
        if ( ! argList.isEmpty() ) {
            System.err.println( "Unused args: " + argList );
            System.err.println( usage );
            return 1;
        }
        if ( file == null ) {
            System.err.println( usage );
            return 1;
        }

        // Configure and run.
        LogUtil.setVerbosity( verb );
        new CdfDump( new CdfReader( file ), System.out, writeFields, html )
           .run();
        return 0;
    }

    /**
     * Main method.  Use -help for arguments.
     */
    public static void main( String[] args ) throws IOException {
        int status = runMain( args );
        if ( status != 0 ) {
            System.exit( status );
        }
    }
}