<action dev="poi-developers" type="fix">51481 - Fixed autofilters in HSSF to avoid warnings in Excel 2007</action>
<action dev="poi-developers" type="fix">51533 - Avoid exception when changing name of a sheet containing shared formulas</action>
<action dev="poi-developers" type="add">Support for appending images to existing drawings in HSSF</action>
- <action dev="poi-developers" type="fix">Added initial support for bookmarks in HWFP</action>
+ <action dev="poi-developers" type="fix">Added initial support for bookmarks in HWPF</action>
<action dev="poi-developers" type="fix">46250 - Fixed cloning worksheets with images</action>
<action dev="poi-developers" type="fix">51524 - PapBinTable constructor is slow (regression)</action>
<action dev="poi-developers" type="fix">51514 - allow HSSFObjectData to work with both POIFS and NPOIFS</action>
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;
/** 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;
}
_bookmarksTables = new BookmarksTables( _tableStream, _fib );
+ _bookmarks = new BookmarksImpl( _bookmarksTables );
_fieldsTables = new FieldsTables(_tableStream, _fib);
}
}
/**
- * @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;
}
/**
+/* ====================================================================
+ 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
{
private String[] names = new String[0];
- public BookmarksTables()
+ public BookmarksTables( byte[] tableStream, FileInformationBlock fib )
{
+ read( tableStream, fib );
}
- public BookmarksTables( byte[] tableStream, FileInformationBlock fib )
+ public int getBookmarksCount()
{
- read( tableStream, fib );
+ return descriptorsFirst.length();
}
- public Bookmark getBookmark( int index )
+ public GenericPropertyNode getDescriptorFirst( int index )
+ throws IndexOutOfBoundsException
{
- 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;
- }
- };
+ return descriptorsFirst.getProperty( index );
}
- public int getBookmarksCount()
+ 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();
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
{
public abstract class PropertyNode<T extends PropertyNode<T>> implements Comparable<T>, Cloneable
{
- static final class EndComparator implements Comparator<PropertyNode<?>>
+ public static final class EndComparator implements
+ Comparator<PropertyNode<?>>
{
- static EndComparator instance = new EndComparator();
+ public static EndComparator instance = new EndComparator();
public int compare( PropertyNode<?> o1, PropertyNode<?> o2 )
{
}
}
- static final class StartComparator implements Comparator<PropertyNode<?>>
+ public static final class StartComparator implements
+ Comparator<PropertyNode<?>>
{
- static StartComparator instance = new StartComparator();
+ public static StartComparator instance = new StartComparator();
public int compare( PropertyNode<?> o1, PropertyNode<?> o2 )
{
--- /dev/null
+/* ====================================================================
+ 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<Integer, List<Bookmark>> getBookmarksStartedBetween(
+ int startInclusive, int endExclusive );
+}
--- /dev/null
+/* ====================================================================
+ 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<Integer, List<GenericPropertyNode>> 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<Bookmark> getBookmarksAt( int startCp )
+ {
+ updateSortedDescriptors();
+
+ List<GenericPropertyNode> nodes = sortedDescriptors.get( Integer
+ .valueOf( startCp ) );
+ if ( nodes == null || nodes.isEmpty() )
+ return Collections.emptyList();
+
+ List<Bookmark> result = new ArrayList<Bookmark>( nodes.size() );
+ for ( GenericPropertyNode node : nodes )
+ {
+ result.add( getBookmark( node ) );
+ }
+ return Collections.unmodifiableList( result );
+ }
+
+ public int getBookmarksCount()
+ {
+ return bookmarksTables.getDescriptorsFirstCount();
+ }
+
+ public Map<Integer, List<Bookmark>> 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<Integer, List<Bookmark>> result = new LinkedHashMap<Integer, List<Bookmark>>();
+ for ( int lookupIndex = startLookupIndex; lookupIndex < endLookupIndex; lookupIndex++ )
+ {
+ int s = sortedStartPositions[lookupIndex];
+ List<Bookmark> startedAt = getBookmarksAt( s );
+ if ( startedAt != null )
+ result.put( Integer.valueOf( s ), startedAt );
+ }
+
+ return Collections.unmodifiableMap( result );
+ }
+
+ private void updateSortedDescriptors()
+ {
+ if ( sortedDescriptors != null )
+ return;
+
+ Map<Integer, List<GenericPropertyNode>> result = new HashMap<Integer, List<GenericPropertyNode>>();
+ for ( int b = 0; b < bookmarksTables.getDescriptorsFirstCount(); b++ )
+ {
+ GenericPropertyNode property = bookmarksTables
+ .getDescriptorFirst( b );
+ Integer positionKey = Integer.valueOf( property.getStart() );
+ List<GenericPropertyNode> atPositionList = result.get( positionKey );
+ if ( atPositionList == null )
+ {
+ atPositionList = new LinkedList<GenericPropertyNode>();
+ result.put( positionKey, atPositionList );
+ }
+ atPositionList.add( property );
+ }
+
+ int counter = 0;
+ int[] indices = new int[result.size()];
+ for ( Map.Entry<Integer, List<GenericPropertyNode>> entry : result
+ .entrySet() )
+ {
+ indices[counter++] = entry.getKey().intValue();
+ List<GenericPropertyNode> updated = new ArrayList<GenericPropertyNode>(
+ entry.getValue() );
+ Collections.sort( updated, PropertyNode.EndComparator.instance );
+ entry.setValue( updated );
+ }
+
+ this.sortedDescriptors = result;
+ this.sortedStartPositions = indices;
+ }
+}
+/* ====================================================================
+ 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;
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() );