]> source.dussan.org Git - poi.git/commitdiff
HWPF Bookmarks tables are correctly updated on text updates. Add HWPF API to update...
authorSergey Vladimirov <sergey@apache.org>
Wed, 14 Sep 2011 05:41:21 +0000 (05:41 +0000)
committerSergey Vladimirov <sergey@apache.org>
Wed, 14 Sep 2011 05:41:21 +0000 (05:41 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1170437 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java
src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java

index d8a08883d1e42723aa98f728500a31b69e7de4d7..039edc3251dcf23303bf20f70559888aa3ca45c4 100644 (file)
@@ -34,6 +34,8 @@
 
     <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>
index 127cbc36831f603906f079d64b3cdf9d0715efcd..bc2d21f15cbedbb980a43b3cfeceecc01579c364 100644 (file)
 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<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();
@@ -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<String>( 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 );
index 64b05097eca10e61d34e6d4d2a82b9cffa6948e1..81c20d1621ff15d9d625defd6495cf3dc1d57ea0 100644 (file)
@@ -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()
index 636746becfab366f8af3bfacf4c893225c59cec6..c4dae5e9db519fd1550fbd6bf860d0e8754e1c55 100644 (file)
@@ -47,4 +47,12 @@ public interface Bookmarks
      */
     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 );
 }
index 8a1c88af4cf0bc89b6e58f0ffd27fa9dbc18d2e9..ce4df164be4b605839aeaf793cf919b7e1204b79 100644 (file)
@@ -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<Integer, List<GenericPropertyNode>> 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();
-        }
-
-    }
 }
index 47cba3f9501a397959d0efe23fbda605bca87756..7c662fae1adf0006fb9d06e8bf5187c65b28f46c 100644 (file)
@@ -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 <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...
         *
@@ -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 <code>Range</code>)
         */
+       @Internal
        public void replaceText(String pPlaceHolder, String pValue, int pOffset) {
                int absPlaceHolderIndex = getStartOffset() + pOffset;
 
index ced953c7f38fa51eca35bdd29e2632ae52f3923d..4e013b3b7df41f7e937d244f6ba617465cdf56ed 100644 (file)
@@ -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() );
+    }
 }