2005-05-28 01:36:00 -04:00
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2006-12-22 14:18:16 -05:00
Licensed to the Apache Software Foundation ( ASF ) under one or more
contributor license agreements . See the NOTICE file distributed with
this work for additional information regarding copyright ownership .
The ASF licenses this file to You under the Apache License , Version 2 . 0
( the " License " ) ; you may not use this file except in compliance with
the License . You may obtain a copy of the License at
2005-05-28 01:36:00 -04:00
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
package org.apache.poi.hslf.record ;
import java.io.IOException ;
import java.io.OutputStream ;
2010-10-19 16:12:19 -04:00
import java.util.ArrayList ;
import java.util.List ;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException ;
2017-01-01 19:55:49 -05:00
import org.apache.poi.hslf.exceptions.HSLFException ;
2005-05-28 01:36:00 -04:00
import org.apache.poi.util.LittleEndian ;
2008-04-14 10:58:18 -04:00
import org.apache.poi.util.POILogFactory ;
2010-10-19 16:12:19 -04:00
import org.apache.poi.util.POILogger ;
2005-05-28 01:36:00 -04:00
/ * *
* This abstract class represents a record in the PowerPoint document .
* Record classes should extend with RecordContainer or RecordAtom , which
* extend this in turn .
*
* @author Nick Burch
* /
public abstract class Record
{
2007-04-12 03:40:15 -04:00
// For logging
2014-02-13 18:34:11 -05:00
protected static final POILogger logger = POILogFactory . getLogger ( Record . class ) ;
2007-04-12 03:40:15 -04:00
/ * *
2005-05-28 01:36:00 -04:00
* Is this record type an Atom record ( only has data ) ,
* or is it a non - Atom record ( has other records ) ?
* /
public abstract boolean isAnAtom ( ) ;
/ * *
* Returns the type ( held as a little endian in bytes 3 and 4 )
* that this class handles
* /
public abstract long getRecordType ( ) ;
2009-05-21 14:12:22 -04:00
/ * *
2005-05-28 01:36:00 -04:00
* Fetch all the child records of this record
* If this record is an atom , will return null
2009-05-21 14:12:22 -04:00
* If this record is a non - atom , but has no children , will return
2005-05-28 01:36:00 -04:00
* an empty array
* /
public abstract Record [ ] getChildRecords ( ) ;
/ * *
* Have the contents printer out into an OutputStream , used when
* writing a file back out to disk
* ( Normally , atom classes will keep their bytes around , but
2009-05-21 14:12:22 -04:00
* non atom classes will just request the bytes from their
2005-05-28 01:36:00 -04:00
* children , then chuck on their header and return )
* /
public abstract void writeOut ( OutputStream o ) throws IOException ;
/ * *
* When writing out , write out a signed int ( 32bit ) in Little Endian format
* /
public static void writeLittleEndian ( int i , OutputStream o ) throws IOException {
byte [ ] bi = new byte [ 4 ] ;
2014-12-24 20:56:29 -05:00
LittleEndian . putInt ( bi , 0 , i ) ;
2005-05-28 01:36:00 -04:00
o . write ( bi ) ;
}
/ * *
* When writing out , write out a signed short ( 16bit ) in Little Endian format
* /
public static void writeLittleEndian ( short s , OutputStream o ) throws IOException {
byte [ ] bs = new byte [ 2 ] ;
2014-12-24 20:56:29 -05:00
LittleEndian . putShort ( bs , 0 , s ) ;
2005-05-28 01:36:00 -04:00
o . write ( bs ) ;
}
2009-05-21 14:12:22 -04:00
2006-07-03 16:34:41 -04:00
/ * *
* Build and return the Record at the given offset .
* Note - does less error checking and handling than findChildRecords
* @param b The byte array to build from
* @param offset The offset to build at
* /
public static Record buildRecordAtOffset ( byte [ ] b , int offset ) {
long type = LittleEndian . getUShort ( b , offset + 2 ) ;
long rlen = LittleEndian . getUInt ( b , offset + 4 ) ;
// Sanity check the length
int rleni = ( int ) rlen ;
if ( rleni < 0 ) { rleni = 0 ; }
return createRecordForType ( type , b , offset , 8 + rleni ) ;
}
2005-05-28 01:36:00 -04:00
/ * *
2006-01-03 06:54:38 -05:00
* Default method for finding child records of a container record
2005-05-28 01:36:00 -04:00
* /
public static Record [ ] findChildRecords ( byte [ ] b , int start , int len ) {
2010-10-19 16:12:19 -04:00
List < Record > children = new ArrayList < Record > ( 5 ) ;
2005-05-28 01:36:00 -04:00
// Jump our little way along, creating records as we go
int pos = start ;
while ( pos < = ( start + len - 8 ) ) {
long type = LittleEndian . getUShort ( b , pos + 2 ) ;
long rlen = LittleEndian . getUInt ( b , pos + 4 ) ;
// Sanity check the length
int rleni = ( int ) rlen ;
if ( rleni < 0 ) { rleni = 0 ; }
2006-01-03 06:54:38 -05:00
// Abort if first record is of type 0000 and length FFFF,
// as that's a sign of a screwed up record
if ( pos = = 0 & & type = = 0l & & rleni = = 0xffff ) {
throw new CorruptPowerPointFileException ( " Corrupt document - starts with record of type 0000 and length 0xFFFF " ) ;
}
2005-05-28 01:36:00 -04:00
Record r = createRecordForType ( type , b , pos , 8 + rleni ) ;
2006-02-06 08:12:02 -05:00
if ( r ! = null ) {
children . add ( r ) ;
} else {
// Record was horribly corrupt
}
2005-05-28 01:36:00 -04:00
pos + = 8 ;
2007-03-21 10:04:32 -04:00
pos + = rleni ;
2005-05-28 01:36:00 -04:00
}
// Turn the vector into an array, and return
2010-10-19 16:12:19 -04:00
Record [ ] cRecords = children . toArray ( new Record [ children . size ( ) ] ) ;
2005-05-28 01:36:00 -04:00
return cRecords ;
}
/ * *
* For a given type ( little endian bytes 3 and 4 in record header ) ,
* byte array , start position and length :
* will return a Record object that will handle that record
*
* Remember that while PPT stores the record lengths as 8 bytes short
* ( not including the size of the header ) , this code assumes you ' re
* passing in corrected lengths
* /
2005-06-09 11:09:16 -04:00
public static Record createRecordForType ( long type , byte [ ] b , int start , int len ) {
Record toReturn = null ;
2006-02-06 08:12:02 -05:00
// Handle case of a corrupt last record, whose claimed length
// would take us passed the end of the file
if ( start + len > b . length ) {
2012-11-16 07:21:49 -05:00
logger . log ( POILogger . WARN , " Warning: Skipping record of type " + type + " at position " + start + " which claims to be longer than the file! ( " + len + " vs " + ( b . length - start ) + " ) " ) ;
2006-02-06 08:12:02 -05:00
return null ;
}
2005-07-08 13:38:55 -04:00
// We use the RecordTypes class to provide us with the right
// class to use for a given type
// A spot of reflection gets us the (byte[],int,int) constructor
// From there, we instanciate the class
// Any special record handling occurs once we have the class
2010-10-19 16:12:19 -04:00
Class < ? extends Record > c = null ;
2005-07-08 13:38:55 -04:00
try {
2015-12-31 17:10:17 -05:00
c = RecordTypes . forTypeID ( ( short ) type ) . handlingClass ;
2009-05-21 14:12:22 -04:00
if ( c = = null ) {
2016-02-24 17:43:51 -05:00
// How odd. RecordTypes normally substitutes in
2005-07-08 13:38:55 -04:00
// a default handler class if it has heard of the record
// type but there's no support for it. Explicitly request
// that now
2015-12-31 17:10:17 -05:00
c = RecordTypes . UnknownRecordPlaceholder . handlingClass ;
2005-07-08 13:38:55 -04:00
}
// Grab the right constructor
2010-10-19 16:12:19 -04:00
java . lang . reflect . Constructor < ? extends Record > con = c . getDeclaredConstructor ( new Class [ ] { byte [ ] . class , Integer . TYPE , Integer . TYPE } ) ;
2005-07-08 13:38:55 -04:00
// Instantiate
2015-12-31 17:10:17 -05:00
toReturn = con . newInstance ( new Object [ ] { b , start , len } ) ;
2005-07-08 13:38:55 -04:00
} catch ( InstantiationException ie ) {
2017-01-01 19:55:49 -05:00
throw new HSLFException ( " Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ie , ie ) ;
2005-07-08 13:38:55 -04:00
} catch ( java . lang . reflect . InvocationTargetException ite ) {
2017-01-01 19:55:49 -05:00
throw new HSLFException ( " Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ite + " \ nCause was : " + ite . getCause ( ) , ite ) ;
2005-07-08 13:38:55 -04:00
} catch ( IllegalAccessException iae ) {
2017-01-01 19:55:49 -05:00
throw new HSLFException ( " Couldn't access the constructor for type with id " + type + " on class " + c + " : " + iae , iae ) ;
2005-07-08 13:38:55 -04:00
} catch ( NoSuchMethodException nsme ) {
2017-01-01 19:55:49 -05:00
throw new HSLFException ( " Couldn't access the constructor for type with id " + type + " on class " + c + " : " + nsme , nsme ) ;
2005-06-09 11:09:16 -04:00
}
2005-07-08 13:38:55 -04:00
// Handling for special kinds of records follow
2005-06-09 11:09:16 -04:00
// If it's a position aware record, tell it where it is
if ( toReturn instanceof PositionDependentRecord ) {
PositionDependentRecord pdr = ( PositionDependentRecord ) toReturn ;
pdr . setLastOnDiskOffset ( start ) ;
2005-05-28 01:36:00 -04:00
}
2005-06-09 11:09:16 -04:00
2005-07-08 13:38:55 -04:00
// Return the created record
2005-06-09 11:09:16 -04:00
return toReturn ;
2005-05-28 01:36:00 -04:00
}
}