Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hslf » record » [javadoc | source]
    1   
    2   /* ====================================================================
    3      Licensed to the Apache Software Foundation (ASF) under one or more
    4      contributor license agreements.  See the NOTICE file distributed with
    5      this work for additional information regarding copyright ownership.
    6      The ASF licenses this file to You under the Apache License, Version 2.0
    7      (the "License"); you may not use this file except in compliance with
    8      the License.  You may obtain a copy of the License at
    9   
   10          http://www.apache.org/licenses/LICENSE-2.0
   11   
   12      Unless required by applicable law or agreed to in writing, software
   13      distributed under the License is distributed on an "AS IS" BASIS,
   14      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15      See the License for the specific language governing permissions and
   16      limitations under the License.
   17   ==================================================================== */
   18           
   19   
   20   
   21   package org.apache.poi.hslf.record;
   22   
   23   import java.io.IOException;
   24   import java.io.OutputStream;
   25   import java.util.Vector;
   26   import org.apache.poi.util.LittleEndian;
   27   import org.apache.poi.util.POILogger;
   28   import org.apache.poi.util.POILogFactory;
   29   import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
   30   
   31   /**
   32    * This abstract class represents a record in the PowerPoint document.
   33    * Record classes should extend with RecordContainer or RecordAtom, which
   34    *  extend this in turn.
   35    *
   36    * @author Nick Burch
   37    */
   38   
   39   public abstract class Record
   40   {
   41       // For logging
   42       protected POILogger logger = POILogFactory.getLogger(this.getClass());
   43   
   44   	/**
   45   	 * Is this record type an Atom record (only has data),
   46   	 *  or is it a non-Atom record (has other records)?
   47   	 */
   48   	public abstract boolean isAnAtom();
   49   
   50   	/**
   51   	 * Returns the type (held as a little endian in bytes 3 and 4)
   52   	 *  that this class handles
   53   	 */
   54   	public abstract long getRecordType();
   55   
   56   	/** 
   57   	 * Fetch all the child records of this record
   58   	 * If this record is an atom, will return null
   59   	 * If this record is a non-atom, but has no children, will return 
   60   	 *  an empty array
   61   	 */
   62   	public abstract Record[] getChildRecords();
   63   
   64   	/**
   65   	 * Have the contents printer out into an OutputStream, used when
   66   	 *  writing a file back out to disk
   67   	 * (Normally, atom classes will keep their bytes around, but
   68   	 *  non atom classes will just request the bytes from their 
   69   	 *  children, then chuck on their header and return)
   70   	 */
   71   	public abstract void writeOut(OutputStream o) throws IOException;
   72   
   73   	/**
   74   	 * When writing out, write out a signed int (32bit) in Little Endian format
   75   	 */
   76   	public static void writeLittleEndian(int i,OutputStream o) throws IOException {
   77   		byte[] bi = new byte[4];
   78   		LittleEndian.putInt(bi,i);
   79   		o.write(bi);
   80   	}
   81   	/**
   82   	 * When writing out, write out a signed short (16bit) in Little Endian format
   83   	 */
   84   	public static void writeLittleEndian(short s,OutputStream o) throws IOException {
   85   		byte[] bs = new byte[2];
   86   		LittleEndian.putShort(bs,s);
   87   		o.write(bs);
   88   	}
   89   	
   90   	/**
   91   	 * Build and return the Record at the given offset.
   92   	 * Note - does less error checking and handling than findChildRecords
   93   	 * @param b The byte array to build from
   94   	 * @param offset The offset to build at
   95   	 */
   96   	public static Record buildRecordAtOffset(byte[] b, int offset) {
   97   		long type = LittleEndian.getUShort(b,offset+2);
   98   		long rlen = LittleEndian.getUInt(b,offset+4);
   99   
  100   		// Sanity check the length
  101   		int rleni = (int)rlen;
  102   		if(rleni < 0) { rleni = 0; }
  103   
  104   		return createRecordForType(type,b,offset,8+rleni);
  105   	}
  106   
  107   	/**
  108   	 * Default method for finding child records of a container record
  109   	 */
  110   	public static Record[] findChildRecords(byte[] b, int start, int len) {
  111   		Vector children = new Vector(5);
  112   
  113   		// Jump our little way along, creating records as we go
  114   		int pos = start;
  115   		while(pos <= (start+len-8)) {
  116   			long type = LittleEndian.getUShort(b,pos+2);
  117   			long rlen = LittleEndian.getUInt(b,pos+4);
  118   
  119   			// Sanity check the length
  120   			int rleni = (int)rlen;
  121   			if(rleni < 0) { rleni = 0; }
  122   
  123   			// Abort if first record is of type 0000 and length FFFF,
  124   			//  as that's a sign of a screwed up record
  125   			if(pos == 0 && type == 0l && rleni == 0xffff) {
  126   				throw new CorruptPowerPointFileException("Corrupt document - starts with record of type 0000 and length 0xFFFF");
  127   			}
  128   
  129   			Record r = createRecordForType(type,b,pos,8+rleni);
  130   			if(r != null) {
  131   				children.add(r);
  132   			} else {
  133   				// Record was horribly corrupt
  134   			}
  135   			pos += 8;
  136   			pos += rleni;
  137   		}
  138   
  139   		// Turn the vector into an array, and return
  140   		Record[] cRecords = new Record[children.size()];
  141   		for(int i=0; i < children.size(); i++) {
  142   			cRecords[i] = (Record)children.get(i);
  143   		}
  144   		return cRecords;
  145   	}
  146   
  147   	/**
  148   	 * For a given type (little endian bytes 3 and 4 in record header),
  149   	 *  byte array, start position and length:
  150   	 *  will return a Record object that will handle that record
  151   	 *
  152   	 * Remember that while PPT stores the record lengths as 8 bytes short
  153   	 *  (not including the size of the header), this code assumes you're
  154   	 *  passing in corrected lengths
  155   	 */
  156   	public static Record createRecordForType(long type, byte[] b, int start, int len) {
  157   		Record toReturn = null;
  158   
  159   		// Handle case of a corrupt last record, whose claimed length
  160   		//  would take us passed the end of the file
  161   		if(start + len > b.length) {
  162   			System.err.println("Warning: Skipping record of type " + type + " at position " + start + " which claims to be longer than the file! (" + len + " vs " + (b.length-start) + ")");
  163   			return null;
  164   		}
  165   
  166   		// We use the RecordTypes class to provide us with the right
  167   		//  class to use for a given type
  168   		// A spot of reflection gets us the (byte[],int,int) constructor
  169   		// From there, we instanciate the class
  170   		// Any special record handling occurs once we have the class
  171   		Class c = null;
  172   		try {
  173   			c = RecordTypes.recordHandlingClass((int)type);
  174   			if(c == null) { 
  175   				// How odd. RecordTypes normally subsitutes in
  176   				//  a default handler class if it has heard of the record
  177   				//  type but there's no support for it. Explicitly request
  178   				//  that now
  179   				c = RecordTypes.recordHandlingClass( RecordTypes.Unknown.typeID );
  180   			}
  181   
  182   			// Grab the right constructor
  183   			java.lang.reflect.Constructor con = c.getDeclaredConstructor(new Class[] { byte[].class, Integer.TYPE, Integer.TYPE });
  184   			// Instantiate
  185   			toReturn = (Record)(con.newInstance(new Object[] { b, new Integer(start), new Integer(len) }));
  186   		} catch(InstantiationException ie) {
  187   			throw new RuntimeException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ie, ie);
  188   		} catch(java.lang.reflect.InvocationTargetException ite) {
  189   			throw new RuntimeException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ite + "\nCause was : " + ite.getCause(), ite);
  190   		} catch(IllegalAccessException iae) {
  191   			throw new RuntimeException("Couldn't access the constructor for type with id " + type + " on class " + c + " : " + iae, iae);
  192   		} catch(NoSuchMethodException nsme) {
  193   			throw new RuntimeException("Couldn't access the constructor for type with id " + type + " on class " + c + " : " + nsme, nsme);
  194   		}
  195   
  196   		// Handling for special kinds of records follow
  197   
  198   		// If it's a position aware record, tell it where it is
  199   		if(toReturn instanceof PositionDependentRecord) {
  200   			PositionDependentRecord pdr = (PositionDependentRecord)toReturn;
  201   			pdr.setLastOnDiskOffset(start);
  202   		}
  203   
  204   		// Return the created record
  205   		return toReturn;
  206   	}
  207   }

Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hslf » record » [javadoc | source]