diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 6019d3c14c..82000db299 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -38,7 +38,7 @@
51481 - Fixed autofilters in HSSF to avoid warnings in Excel 2007
51533 - Avoid exception when changing name of a sheet containing shared formulas
Support for appending images to existing drawings in HSSF
- Added initial support for bookmarks in HWFP
+ Added initial support for bookmarks in HWPF
46250 - Fixed cloning worksheets with images
51524 - PapBinTable constructor is slow (regression)
51514 - allow HSSFObjectData to work with both POIFS and NPOIFS
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
index b8a2cd5f8b..cc1b28ce0d 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
@@ -48,6 +48,8 @@ import org.apache.poi.hwpf.model.TextPiece;
import org.apache.poi.hwpf.model.TextPieceTable;
import org.apache.poi.hwpf.model.io.HWPFFileSystem;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
+import org.apache.poi.hwpf.usermodel.Bookmarks;
+import org.apache.poi.hwpf.usermodel.BookmarksImpl;
import org.apache.poi.hwpf.usermodel.HWPFList;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.poifs.common.POIFSConstants;
@@ -102,9 +104,12 @@ public final class HWPFDocument extends HWPFDocumentCore
/** Holds Office Art objects */
protected ShapesTable _officeArts;
- /** Holds the bookmarks */
+ /** Holds the bookmarks tables */
protected BookmarksTables _bookmarksTables;
-
+
+ /** Holds the bookmarks */
+ protected Bookmarks _bookmarks;
+
/** Holds the fields PLCFs */
protected FieldsTables _fieldsTables;
@@ -267,6 +272,7 @@ public final class HWPFDocument extends HWPFDocumentCore
}
_bookmarksTables = new BookmarksTables( _tableStream, _fib );
+ _bookmarks = new BookmarksImpl( _bookmarksTables );
_fieldsTables = new FieldsTables(_tableStream, _fib);
}
@@ -444,12 +450,11 @@ public final class HWPFDocument extends HWPFDocumentCore
}
/**
- * @return BookmarksTables object, that is able to extract bookmarks
- * descriptors from this document
+ * @return user-friendly interface to access document bookmarks
*/
- public BookmarksTables getBookmarksTables()
+ public Bookmarks getBookmarks()
{
- return _bookmarksTables;
+ return _bookmarks;
}
/**
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 2dd19d42d9..ead86a60d3 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java
@@ -1,10 +1,25 @@
+/* ====================================================================
+ 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.hwpf.model;
import java.io.IOException;
import java.util.Arrays;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
-import org.apache.poi.hwpf.usermodel.Bookmark;
public class BookmarksTables
{
@@ -14,67 +29,55 @@ public class BookmarksTables
private String[] names = new String[0];
- public BookmarksTables()
- {
- }
-
public BookmarksTables( byte[] tableStream, FileInformationBlock fib )
{
read( tableStream, fib );
}
- public Bookmark getBookmark( int index )
- {
- final GenericPropertyNode first = descriptorsFirst.getProperty( index );
- return new Bookmark()
- {
- public int getEnd()
- {
- int currentIndex = Arrays.asList(
- descriptorsFirst.toPropertiesArray() ).indexOf( first );
- if ( currentIndex >= descriptorsLim.length() )
- return first.getEnd();
-
- GenericPropertyNode lim = descriptorsLim
- .getProperty( currentIndex );
- return lim.getStart();
- }
-
- public String getName()
- {
- int currentIndex = Arrays.asList(
- descriptorsFirst.toPropertiesArray() ).indexOf( first );
- if ( currentIndex >= names.length )
- return "";
-
- return names[currentIndex];
- }
-
- public int getStart()
- {
- return first.getStart();
- }
-
- public void setName( String name )
- {
- int currentIndex = Arrays.asList(
- descriptorsFirst.toPropertiesArray() ).indexOf( first );
- if ( currentIndex < names.length )
- {
- String[] newNames = new String[currentIndex + 1];
- System.arraycopy( names, 0, newNames, 0, names.length );
- names = newNames;
- }
- names[currentIndex] = name;
- }
- };
- }
-
public int getBookmarksCount()
{
return descriptorsFirst.length();
}
+ public GenericPropertyNode getDescriptorFirst( int index )
+ throws IndexOutOfBoundsException
+ {
+ return descriptorsFirst.getProperty( index );
+ }
+
+ public int getDescriptorFirstIndex( GenericPropertyNode descriptorFirst )
+ {
+ // TODO: very non-optimal
+ return Arrays.asList( descriptorsFirst.toPropertiesArray() ).indexOf(
+ descriptorFirst );
+ }
+
+ public GenericPropertyNode getDescriptorLim( int index )
+ throws IndexOutOfBoundsException
+ {
+ return descriptorsLim.getProperty( index );
+ }
+
+ public int getDescriptorsFirstCount()
+ {
+ return descriptorsFirst.length();
+ }
+
+ public int getDescriptorsLimCount()
+ {
+ return descriptorsLim.length();
+ }
+
+ public String getName( int index ) throws ArrayIndexOutOfBoundsException
+ {
+ return names[index];
+ }
+
+ public int getNamesCount()
+ {
+ return names.length;
+ }
+
private void read( byte[] tableStream, FileInformationBlock fib )
{
int namesStart = fib.getFcSttbfbkmk();
@@ -97,6 +100,17 @@ public class BookmarksTables
limDescriptorsLength, 0 );
}
+ 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;
+ }
+
public void writePlcfBkmkf( FileInformationBlock fib,
HWPFOutputStream tableStream ) throws IOException
{
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PropertyNode.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PropertyNode.java
index 78f4b35bf4..ed6c2ac385 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/PropertyNode.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PropertyNode.java
@@ -35,9 +35,10 @@ import org.apache.poi.util.POILogger;
public abstract class PropertyNode> implements Comparable, Cloneable
{
- static final class EndComparator implements Comparator>
+ public static final class EndComparator implements
+ Comparator>
{
- static EndComparator instance = new EndComparator();
+ public static EndComparator instance = new EndComparator();
public int compare( PropertyNode> o1, PropertyNode> o2 )
{
@@ -48,9 +49,10 @@ public abstract class PropertyNode> implements Compar
}
}
- static final class StartComparator implements Comparator>
+ public static final class StartComparator implements
+ Comparator>
{
- static StartComparator instance = new StartComparator();
+ public static StartComparator instance = new StartComparator();
public int compare( PropertyNode> o1, PropertyNode> o2 )
{
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java
new file mode 100644
index 0000000000..636746becf
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmarks.java
@@ -0,0 +1,50 @@
+/* ====================================================================
+ 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.hwpf.usermodel;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * User-friendly interface to access document bookmarks
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+public interface Bookmarks
+{
+ /**
+ * @param index
+ * bookmark document index
+ * @return {@link Bookmark} with specified index
+ * @throws IndexOutOfBoundsException
+ * if bookmark with specified index not present in document
+ */
+ Bookmark getBookmark( int index ) throws IndexOutOfBoundsException;
+
+ /**
+ * @return count of {@link Bookmark}s in document
+ */
+ int getBookmarksCount();
+
+ /**
+ * @return {@link Map} of bookmarks started in specified range, where key is
+ * start position and value is sorted {@link List} of
+ * {@link Bookmark}
+ */
+ Map> getBookmarksStartedBetween(
+ int startInclusive, int endExclusive );
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java
new file mode 100644
index 0000000000..40b40644b3
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BookmarksImpl.java
@@ -0,0 +1,189 @@
+/* ====================================================================
+ 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.hwpf.usermodel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hwpf.model.BookmarksTables;
+import org.apache.poi.hwpf.model.GenericPropertyNode;
+import org.apache.poi.hwpf.model.PropertyNode;
+
+/**
+ * Implementation of user-friendly interface for document bookmarks
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {doc} com)
+ */
+public class BookmarksImpl implements Bookmarks
+{
+
+ private final BookmarksTables bookmarksTables;
+
+ private Map> sortedDescriptors = null;
+
+ private int[] sortedStartPositions = null;
+
+ public BookmarksImpl( BookmarksTables bookmarksTables )
+ {
+ this.bookmarksTables = bookmarksTables;
+ }
+
+ private Bookmark getBookmark( final GenericPropertyNode first )
+ {
+ return new Bookmark()
+ {
+ 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();
+ }
+
+ public void setName( String name )
+ {
+ int currentIndex = bookmarksTables
+ .getDescriptorFirstIndex( first );
+ bookmarksTables.setName( currentIndex, name );
+ }
+ };
+ }
+
+ public Bookmark getBookmark( int index )
+ {
+ final GenericPropertyNode first = bookmarksTables
+ .getDescriptorFirst( index );
+ return getBookmark( first );
+ }
+
+ public List getBookmarksAt( int startCp )
+ {
+ updateSortedDescriptors();
+
+ List nodes = sortedDescriptors.get( Integer
+ .valueOf( startCp ) );
+ if ( nodes == null || nodes.isEmpty() )
+ return Collections.emptyList();
+
+ List result = new ArrayList( nodes.size() );
+ for ( GenericPropertyNode node : nodes )
+ {
+ result.add( getBookmark( node ) );
+ }
+ return Collections.unmodifiableList( result );
+ }
+
+ public int getBookmarksCount()
+ {
+ return bookmarksTables.getDescriptorsFirstCount();
+ }
+
+ public Map> getBookmarksStartedBetween(
+ int startInclusive, int endExclusive )
+ {
+ updateSortedDescriptors();
+
+ int startLookupIndex = Arrays.binarySearch( this.sortedStartPositions,
+ startInclusive );
+ if ( startLookupIndex < 0 )
+ startLookupIndex = -( startLookupIndex + 1 );
+ int endLookupIndex = Arrays.binarySearch( this.sortedStartPositions,
+ endExclusive );
+ if ( endLookupIndex < 0 )
+ endLookupIndex = -( endLookupIndex + 1 );
+
+ Map> result = new LinkedHashMap>();
+ for ( int lookupIndex = startLookupIndex; lookupIndex < endLookupIndex; lookupIndex++ )
+ {
+ int s = sortedStartPositions[lookupIndex];
+ List startedAt = getBookmarksAt( s );
+ if ( startedAt != null )
+ result.put( Integer.valueOf( s ), startedAt );
+ }
+
+ return Collections.unmodifiableMap( result );
+ }
+
+ private void updateSortedDescriptors()
+ {
+ if ( sortedDescriptors != null )
+ return;
+
+ Map> result = new HashMap>();
+ for ( int b = 0; b < bookmarksTables.getDescriptorsFirstCount(); b++ )
+ {
+ GenericPropertyNode property = bookmarksTables
+ .getDescriptorFirst( b );
+ Integer positionKey = Integer.valueOf( property.getStart() );
+ List atPositionList = result.get( positionKey );
+ if ( atPositionList == null )
+ {
+ atPositionList = new LinkedList();
+ result.put( positionKey, atPositionList );
+ }
+ atPositionList.add( property );
+ }
+
+ int counter = 0;
+ int[] indices = new int[result.size()];
+ for ( Map.Entry> entry : result
+ .entrySet() )
+ {
+ indices[counter++] = entry.getKey().intValue();
+ List updated = new ArrayList(
+ entry.getValue() );
+ Collections.sort( updated, PropertyNode.EndComparator.instance );
+ entry.setValue( updated );
+ }
+
+ this.sortedDescriptors = result;
+ this.sortedStartPositions = indices;
+ }
+}
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 7b0b9b0f76..ced953c7f3 100644
--- a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java
+++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java
@@ -1,3 +1,19 @@
+/* ====================================================================
+ 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.hwpf.model;
import junit.framework.TestCase;
@@ -5,17 +21,24 @@ import junit.framework.TestCase;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFTestDataSamples;
import org.apache.poi.hwpf.usermodel.Bookmark;
+import org.apache.poi.hwpf.usermodel.Bookmarks;
+/**
+ * Test cases for {@link BookmarksTables} and default implementation of
+ * {@link Bookmarks}
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
public class TestBookmarksTables extends TestCase
{
public void test()
{
HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
- BookmarksTables bookmarksTables = doc.getBookmarksTables();
+ Bookmarks bookmarks = doc.getBookmarks();
- assertEquals( 1, bookmarksTables.getBookmarksCount() );
+ assertEquals( 1, bookmarks.getBookmarksCount() );
- Bookmark bookmark = bookmarksTables.getBookmark( 0 );
+ Bookmark bookmark = bookmarks.getBookmark( 0 );
assertEquals( "userref", bookmark.getName() );
assertEquals( 27, bookmark.getStart() );
assertEquals( 38, bookmark.getEnd() );