222 lines
7.4 KiB
Java
222 lines
7.4 KiB
Java
|
|
/* ====================================================================
|
|
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
|
|
|
|
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 org.apache.poi.util.LittleEndian;
|
|
import org.apache.poi.util.POILogger;
|
|
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.util.Enumeration;
|
|
import java.util.Hashtable;
|
|
|
|
/**
|
|
* General holder for PersistPtrFullBlock and PersistPtrIncrementalBlock
|
|
* records. We need to handle them specially, since we have to go around
|
|
* updating UserEditAtoms if they shuffle about on disk
|
|
* These hold references to where slides "live". If the position of a slide
|
|
* moves, then we have update all of these. If we come up with a new version
|
|
* of a slide, then we have to add one of these to the end of the chain
|
|
* (via CurrentUserAtom and UserEditAtom) pointing to the new slide location
|
|
*
|
|
* @author Nick Burch
|
|
*/
|
|
|
|
public class PersistPtrHolder extends PositionDependentRecordAtom
|
|
{
|
|
private byte[] _header;
|
|
private byte[] _ptrData; // Will need to update this once we allow updates to _slideLocations
|
|
private long _type;
|
|
|
|
/**
|
|
* Holds the lookup for slides to their position on disk.
|
|
* You always need to check the most recent PersistPtrHolder
|
|
* that knows about a given slide to find the right location
|
|
*/
|
|
private Hashtable _slideLocations;
|
|
/**
|
|
* Holds the lookup from slide id to where their offset is
|
|
* held inside _ptrData. Used when writing out, and updating
|
|
* the positions of the slides
|
|
*/
|
|
private Hashtable _slideOffsetDataLocation;
|
|
|
|
/**
|
|
* Get the list of slides that this PersistPtrHolder knows about.
|
|
* (They will be the keys in the hashtable for looking up the positions
|
|
* of these slides)
|
|
*/
|
|
public int[] getKnownSlideIDs() {
|
|
int[] ids = new int[_slideLocations.size()];
|
|
Enumeration e = _slideLocations.keys();
|
|
for(int i=0; i<ids.length; i++) {
|
|
Integer id = (Integer)e.nextElement();
|
|
ids[i] = id.intValue();
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
/**
|
|
* Get the lookup from slide numbers to byte offsets, for the slides
|
|
* known about by this PersistPtrHolder.
|
|
*/
|
|
public Hashtable getSlideLocationsLookup() {
|
|
return _slideLocations;
|
|
}
|
|
/**
|
|
* Get the lookup from slide numbers to their offsets inside
|
|
* _ptrData, used when adding or moving slides.
|
|
*/
|
|
public Hashtable getSlideOffsetDataLocationsLookup() {
|
|
return _slideOffsetDataLocation;
|
|
}
|
|
|
|
/**
|
|
* Adds a new slide, notes or similar, to be looked up by this.
|
|
* For now, won't look for the most optimal on disk representation.
|
|
*/
|
|
public void addSlideLookup(int slideID, int posOnDisk) {
|
|
// PtrData grows by 8 bytes:
|
|
// 4 bytes for the new info block
|
|
// 4 bytes for the slide offset
|
|
byte[] newPtrData = new byte[_ptrData.length + 8];
|
|
System.arraycopy(_ptrData,0,newPtrData,0,_ptrData.length);
|
|
|
|
// Add to the slide location lookup hash
|
|
_slideLocations.put(new Integer(slideID), new Integer(posOnDisk));
|
|
// Add to the ptrData offset lookup hash
|
|
_slideOffsetDataLocation.put(new Integer(slideID),
|
|
new Integer(_ptrData.length + 4));
|
|
|
|
// Build the info block
|
|
// First 20 bits = offset number = slide ID
|
|
// Remaining 12 bits = offset count = 1
|
|
int infoBlock = slideID;
|
|
infoBlock += (1 << 20);
|
|
|
|
// Write out the data for this
|
|
LittleEndian.putInt(newPtrData,newPtrData.length-8,infoBlock);
|
|
LittleEndian.putInt(newPtrData,newPtrData.length-4,posOnDisk);
|
|
|
|
// Save the new ptr data
|
|
_ptrData = newPtrData;
|
|
|
|
// Update the atom header
|
|
LittleEndian.putInt(_header,4,newPtrData.length);
|
|
}
|
|
|
|
/**
|
|
* Create a new holder for a PersistPtr record
|
|
*/
|
|
protected PersistPtrHolder(byte[] source, int start, int len) {
|
|
// Sanity Checking - including whole header, so treat
|
|
// length as based of 0, not 8 (including header size based)
|
|
if(len < 8) { len = 8; }
|
|
|
|
// Treat as an atom, grab and hold everything
|
|
_header = new byte[8];
|
|
System.arraycopy(source,start,_header,0,8);
|
|
_type = LittleEndian.getUShort(_header,2);
|
|
|
|
// Try to make sense of the data part:
|
|
// Data part is made up of a number of these sets:
|
|
// 32 bit info value
|
|
// 12 bits count of # of entries
|
|
// base number for these entries
|
|
// count * 32 bit offsets
|
|
// Repeat as many times as you have data
|
|
_slideLocations = new Hashtable();
|
|
_slideOffsetDataLocation = new Hashtable();
|
|
_ptrData = new byte[len-8];
|
|
System.arraycopy(source,start+8,_ptrData,0,_ptrData.length);
|
|
|
|
int pos = 0;
|
|
while(pos < _ptrData.length) {
|
|
// Grab the info field
|
|
long info = LittleEndian.getUInt(_ptrData,pos);
|
|
|
|
// First 20 bits = offset number
|
|
// Remaining 12 bits = offset count
|
|
int offset_count = (int)(info >> 20);
|
|
int offset_no = (int)(info - (offset_count << 20));
|
|
//System.out.println("Info is " + info + ", count is " + offset_count + ", number is " + offset_no);
|
|
|
|
// Wind on by the 4 byte info header
|
|
pos += 4;
|
|
|
|
// Grab the offsets for each of the sheets
|
|
for(int i=0; i<offset_count; i++) {
|
|
int sheet_no = offset_no + i;
|
|
long sheet_offset = LittleEndian.getUInt(_ptrData,pos);
|
|
_slideLocations.put(new Integer(sheet_no), new Integer((int)sheet_offset));
|
|
_slideOffsetDataLocation.put(new Integer(sheet_no), new Integer(pos));
|
|
|
|
// Wind on by 4 bytes per sheet found
|
|
pos += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the value we were given at creation, be it 6001 or 6002
|
|
*/
|
|
public long getRecordType() { return _type; }
|
|
|
|
/**
|
|
* At write-out time, update the references to the sheets to their
|
|
* new positions
|
|
*/
|
|
public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) {
|
|
int[] slideIDs = getKnownSlideIDs();
|
|
|
|
// Loop over all the slides we know about
|
|
// Find where they used to live, and where they now live
|
|
// Then, update the right bit of _ptrData with their new location
|
|
for(int i=0; i<slideIDs.length; i++) {
|
|
Integer id = new Integer(slideIDs[i]);
|
|
Integer oldPos = (Integer)_slideLocations.get(id);
|
|
Integer newPos = (Integer)oldToNewReferencesLookup.get(oldPos);
|
|
|
|
if(newPos == null) {
|
|
logger.log(POILogger.WARN, "Couldn't find the new location of the \"slide\" with id " + id + " that used to be at " + oldPos);
|
|
logger.log(POILogger.WARN, "Not updating the position of it, you probably won't be able to find it any more (if you ever could!)");
|
|
newPos = oldPos;
|
|
}
|
|
|
|
// Write out the new location
|
|
Integer dataOffset = (Integer)_slideOffsetDataLocation.get(id);
|
|
LittleEndian.putInt(_ptrData,dataOffset.intValue(),newPos.intValue());
|
|
|
|
// Update our hashtable
|
|
_slideLocations.remove(id);
|
|
_slideLocations.put(id,newPos);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write the contents of the record back, so it can be written
|
|
* to disk
|
|
*/
|
|
public void writeOut(OutputStream out) throws IOException {
|
|
out.write(_header);
|
|
out.write(_ptrData);
|
|
}
|
|
}
|