From: Sergey Vladimirov Date: Wed, 14 Sep 2011 05:41:21 +0000 (+0000) Subject: HWPF Bookmarks tables are correctly updated on text updates. Add HWPF API to update... X-Git-Tag: REL_3_8_BETA5~169 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=00e43b57cebfcfe87732d61fd7569fbf5907550e;p=poi.git HWPF Bookmarks tables are correctly updated on text updates. Add HWPF API to update range text and delete bookmarks. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1170437 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d8a08883d1..039edc3251 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,8 @@ + Add HWPF API to update range text and delete bookmarks + HWPF Bookmarks tables are correctly updated on text updates 51670 - avoid LeftoverDataException when reading .xls files with invalid LabelRecords 51196 - prevent NPE in XWPFPicture.getPictureData() 51771 - prevent NPE when getting object data from OLEShape in HSLF diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java index 127cbc3683..bc2d21f15c 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java @@ -17,7 +17,13 @@ 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; @@ -25,17 +31,45 @@ 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 names = new ArrayList( 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(); @@ -70,14 +104,14 @@ public class BookmarksTables 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 ) @@ -86,7 +120,8 @@ public class BookmarksTables int namesLength = fib.getLcbSttbfbkmk(); if ( namesStart != 0 && namesLength != 0 ) - this.names = SttbfUtils.read( tableStream, namesStart ); + this.names = new ArrayList( Arrays.asList( SttbfUtils.read( + tableStream, namesStart ) ) ); int firstDescriptorsStart = fib.getFcPlcfbkf(); int firstDescriptorsLength = fib.getLcbPlcfbkf(); @@ -102,15 +137,16 @@ public class BookmarksTables 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, @@ -152,7 +188,7 @@ public class BookmarksTables 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 ); @@ -160,7 +196,8 @@ public class BookmarksTables } int start = tableStream.getOffset(); - SttbfUtils.write( tableStream, names ); + SttbfUtils + .write( tableStream, names.toArray( new String[names.size()] ) ); int end = tableStream.getOffset(); fib.setFcSttbfbkmk( start ); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java index 64b05097ec..81c20d1621 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java @@ -19,6 +19,7 @@ package org.apache.poi.hwpf.model; import java.util.ArrayList; +import org.apache.poi.util.Internal; import org.apache.poi.util.LittleEndian; /** @@ -66,6 +67,36 @@ public final class PlexOfCps } } + @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 ); @@ -74,6 +105,13 @@ public final class PlexOfCps public void addProperty( GenericPropertyNode node ) { _props.add( node ); + _iMac++; + } + + void remove( int index ) + { + _props.remove( index ); + _iMac--; } public byte[] toByteArray() diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java index 636746becf..c4dae5e9db 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java @@ -47,4 +47,12 @@ public interface Bookmarks */ Map> 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 ); } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java index 8a1c88af4c..ce4df164be 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java @@ -37,6 +37,89 @@ import org.apache.poi.hwpf.model.PropertyNode; 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> sortedDescriptors = null; @@ -46,6 +129,19 @@ public class BookmarksImpl implements Bookmarks 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 ) @@ -113,6 +209,17 @@ public class BookmarksImpl implements Bookmarks 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 ) @@ -149,87 +256,4 @@ public class BookmarksImpl implements Bookmarks 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(); - } - - } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java index 47cba3f950..7c662fae1a 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java @@ -21,6 +21,8 @@ import java.lang.ref.WeakReference; 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; @@ -330,6 +332,11 @@ public class Range { // TODO -instantiable superclass _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 @@ -356,6 +363,11 @@ public class Range { // TODO -instantiable superclass _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(); @@ -558,6 +570,12 @@ public class Range { // TODO -instantiable superclass // + " -> " + 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 ) @@ -703,6 +721,35 @@ public class Range { // TODO -instantiable superclass 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 true 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... * @@ -714,6 +761,7 @@ public class Range { // TODO -instantiable superclass * The offset or index where the text to be replaced begins * (relative to/within this Range) */ + @Internal public void replaceText(String pPlaceHolder, String pValue, int pOffset) { int absPlaceHolderIndex = getStartOffset() + pOffset; diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java index ced953c7f3..4e013b3b7d 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java @@ -16,6 +16,8 @@ ==================================================================== */ package org.apache.poi.hwpf.model; +import org.apache.poi.hwpf.usermodel.Range; + import junit.framework.TestCase; import org.apache.poi.hwpf.HWPFDocument; @@ -43,4 +45,52 @@ public class TestBookmarksTables extends TestCase 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() ); + } }