<changes>
<release version="3.8-beta5" date="2011-??-??">
+ <action dev="poi-developers" type="add">Add HWPF API to update range text and delete bookmarks</action>
+ <action dev="poi-developers" type="add">HWPF Bookmarks tables are correctly updated on text updates</action>
<action dev="poi-developers" type="add">51670 - avoid LeftoverDataException when reading .xls files with invalid LabelRecords</action>
<action dev="poi-developers" type="add">51196 - prevent NPE in XWPFPicture.getPictureData() </action>
<action dev="poi-developers" type="add">51771 - prevent NPE when getting object data from OLEShape in HSLF</action>
package org.apache.poi.hwpf.model;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+
+import org.apache.poi.util.POILogFactory;
+
+import org.apache.poi.util.POILogger;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal;
@Internal
public class BookmarksTables
{
+ private static final POILogger logger = POILogFactory
+ .getLogger( BookmarksTables.class );
+
private PlexOfCps descriptorsFirst = new PlexOfCps( 4 );
private PlexOfCps descriptorsLim = new PlexOfCps( 0 );
- private String[] names = new String[0];
+ private List<String> names = new ArrayList<String>( 0 );
public BookmarksTables( byte[] tableStream, FileInformationBlock fib )
{
read( tableStream, fib );
}
+ public void afterDelete( int startCp, int length )
+ {
+ descriptorsFirst.adjust( startCp, -length );
+ descriptorsLim.adjust( startCp, -length );
+ for ( int i = 0; i < descriptorsFirst.length(); i++ )
+ {
+ GenericPropertyNode startNode = descriptorsFirst.getProperty( i );
+ GenericPropertyNode endNode = descriptorsLim.getProperty( i );
+ if ( startNode.getStart() == endNode.getStart() )
+ {
+ logger.log( POILogger.DEBUG, "Removing bookmark #",
+ Integer.valueOf( i ), "..." );
+ remove( i );
+ i--;
+ continue;
+ }
+ }
+ }
+
+ public void afterInsert( int startCp, int length )
+ {
+ descriptorsFirst.adjust( startCp, length );
+ descriptorsLim.adjust( startCp - 1, length );
+ }
+
public int getBookmarksCount()
{
return descriptorsFirst.length();
return descriptorsLim.length();
}
- public String getName( int index ) throws ArrayIndexOutOfBoundsException
+ public String getName( int index )
{
- return names[index];
+ return names.get( index );
}
public int getNamesCount()
{
- return names.length;
+ return names.size();
}
private void read( byte[] tableStream, FileInformationBlock fib )
int namesLength = fib.getLcbSttbfbkmk();
if ( namesStart != 0 && namesLength != 0 )
- this.names = SttbfUtils.read( tableStream, namesStart );
+ this.names = new ArrayList<String>( Arrays.asList( SttbfUtils.read(
+ tableStream, namesStart ) ) );
int firstDescriptorsStart = fib.getFcPlcfbkf();
int firstDescriptorsLength = fib.getLcbPlcfbkf();
limDescriptorsLength, 0 );
}
+ public void remove( int index )
+ {
+ descriptorsFirst.remove( index );
+ descriptorsLim.remove( index );
+ names.remove( index );
+ }
+
public void setName( int index, String name )
{
- if ( index < names.length )
- {
- String[] newNames = new String[index + 1];
- System.arraycopy( names, 0, newNames, 0, names.length );
- names = newNames;
- }
- names[index] = name;
+ names.set( index, name );
}
public void writePlcfBkmkf( FileInformationBlock fib,
public void writeSttbfBkmk( FileInformationBlock fib,
HWPFOutputStream tableStream ) throws IOException
{
- if ( names == null || names.length == 0 )
+ if ( names == null || names.isEmpty() )
{
fib.setFcSttbfbkmk( 0 );
fib.setLcbSttbfbkmk( 0 );
}
int start = tableStream.getOffset();
- SttbfUtils.write( tableStream, names );
+ SttbfUtils
+ .write( tableStream, names.toArray( new String[names.size()] ) );
int end = tableStream.getOffset();
fib.setFcSttbfbkmk( start );
import java.util.ArrayList;
+import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
/**
}
}
+ @Internal
+ void adjust( int startCp, int shift )
+ {
+ for ( GenericPropertyNode node : _props )
+ {
+ if ( node.getStart() > startCp )
+ {
+ if ( node.getStart() + shift < startCp )
+ {
+ node.setStart( startCp );
+ }
+ else
+ {
+ node.setStart( node.getStart() + shift );
+ }
+ }
+ if ( node.getEnd() >= startCp )
+ {
+ if ( node.getEnd() + shift < startCp )
+ {
+ node.setEnd( startCp );
+ }
+ else
+ {
+ node.setEnd( node.getEnd() + shift );
+ }
+ }
+ }
+ }
+
public GenericPropertyNode getProperty( int index )
{
return _props.get( index );
public void addProperty( GenericPropertyNode node )
{
_props.add( node );
+ _iMac++;
+ }
+
+ void remove( int index )
+ {
+ _props.remove( index );
+ _iMac--;
}
public byte[] toByteArray()
*/
Map<Integer, List<Bookmark>> getBookmarksStartedBetween(
int startInclusive, int endExclusive );
+
+ /**
+ * Remove bookmark from document (but not the bookmark text)
+ *
+ * @param index
+ * bookmark document index to be removed
+ */
+ void remove( int index );
}
public class BookmarksImpl implements Bookmarks
{
+ private final class BookmarkImpl implements Bookmark
+ {
+ private final GenericPropertyNode first;
+
+ private BookmarkImpl( GenericPropertyNode first )
+ {
+ this.first = first;
+ }
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( getClass() != obj.getClass() )
+ return false;
+ BookmarkImpl other = (BookmarkImpl) obj;
+ if ( first == null )
+ {
+ if ( other.first != null )
+ return false;
+ }
+ else if ( !first.equals( other.first ) )
+ return false;
+ return true;
+ }
+
+ public int getEnd()
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ try
+ {
+ GenericPropertyNode descriptorLim = bookmarksTables
+ .getDescriptorLim( currentIndex );
+ return descriptorLim.getStart();
+ }
+ catch ( IndexOutOfBoundsException exc )
+ {
+ return first.getEnd();
+ }
+ }
+
+ public String getName()
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ try
+ {
+ return bookmarksTables.getName( currentIndex );
+ }
+ catch ( ArrayIndexOutOfBoundsException exc )
+ {
+ return "";
+ }
+ }
+
+ public int getStart()
+ {
+ return first.getStart();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return 31 + ( first == null ? 0 : first.hashCode() );
+ }
+
+ public void setName( String name )
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ bookmarksTables.setName( currentIndex, name );
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Bookmark [" + getStart() + "; " + getEnd() + "): name: "
+ + getName();
+ }
+
+ }
+
private final BookmarksTables bookmarksTables;
private Map<Integer, List<GenericPropertyNode>> sortedDescriptors = null;
public BookmarksImpl( BookmarksTables bookmarksTables )
{
this.bookmarksTables = bookmarksTables;
+ reset();
+ }
+
+ void afterDelete( int startCp, int length )
+ {
+ bookmarksTables.afterDelete( startCp, length );
+ reset();
+ }
+
+ void afterInsert( int startCp, int length )
+ {
+ bookmarksTables.afterInsert( startCp, length );
+ reset();
}
private Bookmark getBookmark( final GenericPropertyNode first )
return Collections.unmodifiableMap( result );
}
+ public void remove( int index )
+ {
+ bookmarksTables.remove( index );
+ }
+
+ private void reset()
+ {
+ sortedDescriptors = null;
+ sortedStartPositions = null;
+ }
+
private void updateSortedDescriptors()
{
if ( sortedDescriptors != null )
this.sortedDescriptors = result;
this.sortedStartPositions = indices;
}
-
- private final class BookmarkImpl implements Bookmark
- {
- private final GenericPropertyNode first;
-
- private BookmarkImpl( GenericPropertyNode first )
- {
- this.first = first;
- }
-
- @Override
- public boolean equals( Object obj )
- {
- if ( this == obj )
- return true;
- if ( obj == null )
- return false;
- if ( getClass() != obj.getClass() )
- return false;
- BookmarkImpl other = (BookmarkImpl) obj;
- if ( first == null )
- {
- if ( other.first != null )
- return false;
- }
- else if ( !first.equals( other.first ) )
- return false;
- return true;
- }
-
- public int getEnd()
- {
- int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
- try
- {
- GenericPropertyNode descriptorLim = bookmarksTables
- .getDescriptorLim( currentIndex );
- return descriptorLim.getStart();
- }
- catch ( IndexOutOfBoundsException exc )
- {
- return first.getEnd();
- }
- }
-
- public String getName()
- {
- int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
- try
- {
- return bookmarksTables.getName( currentIndex );
- }
- catch ( ArrayIndexOutOfBoundsException exc )
- {
- return "";
- }
- }
-
- public int getStart()
- {
- return first.getStart();
- }
-
- @Override
- public int hashCode()
- {
- return 31 + ( first == null ? 0 : first.hashCode() );
- }
-
- public void setName( String name )
- {
- int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
- bookmarksTables.setName( currentIndex, name );
- }
-
- @Override
- public String toString()
- {
- return "Bookmark [" + getStart() + "; " + getEnd() + "): name: "
- + getName();
- }
-
- }
}
import java.util.List;
import java.util.NoSuchElementException;
+import org.apache.poi.util.Internal;
+
import org.apache.poi.hwpf.model.BytePropertyNode;
import org.apache.poi.hwpf.HWPFDocument;
_doc.getCharacterTable().adjustForInsert( _charStart, text.length() );
_doc.getParagraphTable().adjustForInsert( _parStart, text.length() );
_doc.getSectionTable().adjustForInsert( _sectionStart, text.length() );
+ if ( _doc instanceof HWPFDocument )
+ {
+ ( (BookmarksImpl) ( (HWPFDocument) _doc ).getBookmarks() )
+ .afterInsert( _start, text.length() );
+ }
adjustForInsert( text.length() );
// update the FIB.CCPText + friends fields
_doc.getCharacterTable().adjustForInsert( _charEnd - 1, text.length() );
_doc.getParagraphTable().adjustForInsert( _parEnd - 1, text.length() );
_doc.getSectionTable().adjustForInsert( _sectionEnd - 1, text.length() );
+ if ( _doc instanceof HWPFDocument )
+ {
+ ( (BookmarksImpl) ( (HWPFDocument) _doc ).getBookmarks() )
+ .afterInsert( _end, text.length() );
+ }
adjustForInsert( text.length() );
assert sanityCheck();
// + " -> " + sepx.getEnd());
}
+ if ( _doc instanceof HWPFDocument )
+ {
+ ( (BookmarksImpl) ( (HWPFDocument) _doc ).getBookmarks() )
+ .afterDelete( _start, ( _end - _start ) );
+ }
+
_text.delete( _start, _end );
Range parent = _parent.get();
if ( parent != null )
return (ListEntry) insertAfter(props, styleIndex);
}
+ /**
+ * Replace range text with new one, adding it to the range and deleting
+ * original text from document
+ *
+ * @param newText
+ * The text to be replaced with
+ * @param addAfter
+ * if <tt>true</tt> the text will be added at the end of current
+ * range, otherwise to the beginning
+ */
+ public void replaceText( String newText, boolean addAfter )
+ {
+ if ( addAfter )
+ {
+ int originalEnd = getEndOffset();
+ insertAfter( newText );
+ new Range( getStartOffset(), originalEnd, this ).delete();
+ }
+ else
+ {
+ int originalStart = getStartOffset();
+ int originalEnd = getEndOffset();
+
+ insertBefore( newText );
+ new Range( originalStart + newText.length(), originalEnd
+ + newText.length(), this ).delete();
+ }
+ }
+
/**
* Replace (one instance of) a piece of text with another...
*
* The offset or index where the text to be replaced begins
* (relative to/within this <code>Range</code>)
*/
+ @Internal
public void replaceText(String pPlaceHolder, String pValue, int pOffset) {
int absPlaceHolderIndex = getStartOffset() + pOffset;
==================================================================== */
package org.apache.poi.hwpf.model;
+import org.apache.poi.hwpf.usermodel.Range;
+
import junit.framework.TestCase;
import org.apache.poi.hwpf.HWPFDocument;
assertEquals( 27, bookmark.getStart() );
assertEquals( 38, bookmark.getEnd() );
}
+
+ public void testDeleteRange()
+ {
+ HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
+ Range range = new Range( 27, 41, doc );
+ range.delete();
+
+ assertEquals( 0, doc.getBookmarks().getBookmarksCount() );
+ }
+
+ public void testReplaceTextAfter()
+ {
+ HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
+ Bookmark bookmark = doc.getBookmarks().getBookmark( 0 );
+ Range range = new Range( bookmark.getStart(), bookmark.getEnd(), doc );
+ range.replaceText( "1destin2ation3", true );
+
+ bookmark = doc.getBookmarks().getBookmark( 0 );
+ assertEquals( "userref", bookmark.getName() );
+ assertEquals( 27, bookmark.getStart() );
+ assertEquals( 41, bookmark.getEnd() );
+ }
+
+ public void testReplaceTextBefore()
+ {
+ HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
+ Bookmark bookmark = doc.getBookmarks().getBookmark( 0 );
+ Range range = new Range( bookmark.getStart(), bookmark.getEnd(), doc );
+ range.replaceText( "1destin2ation3", false );
+
+ bookmark = doc.getBookmarks().getBookmark( 0 );
+ assertEquals( "userref", bookmark.getName() );
+ assertEquals( 27, bookmark.getStart() );
+ assertEquals( 41, bookmark.getEnd() );
+ }
+
+ public void testUpdateText()
+ {
+ HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
+ Bookmark bookmark = doc.getBookmarks().getBookmark( 0 );
+ Range range = new Range( bookmark.getStart(), bookmark.getEnd(), doc );
+ range.replaceText( "destination", "1destin2ation3" );
+
+ bookmark = doc.getBookmarks().getBookmark( 0 );
+ assertEquals( "userref", bookmark.getName() );
+ assertEquals( 27, bookmark.getStart() );
+ assertEquals( 41, bookmark.getEnd() );
+ }
}