aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSergey Vladimirov <sergey@apache.org>2011-07-22 19:38:14 +0000
committerSergey Vladimirov <sergey@apache.org>2011-07-22 19:38:14 +0000
commite0e44fee69f7051aa3c4f82b25113c8da2024a4f (patch)
tree0a4f9f9de29e0a91528b3d0bbe323a1c57b6c6a9 /src
parent7aaa047ff615f7eac1789b59d16a5a035398e8e9 (diff)
downloadpoi-e0e44fee69f7051aa3c4f82b25113c8da2024a4f.tar.gz
poi-e0e44fee69f7051aa3c4f82b25113c8da2024a4f.zip
move Field interface to usermodel and create Fields interface as user-friendly replace for FieldsTables
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1149704 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java23
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java9
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java10
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java233
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/usermodel/Field.java55
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldImpl.java (renamed from src/scratchpad/src/org/apache/poi/hwpf/model/Field.java)36
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/usermodel/Fields.java33
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldsImpl.java276
8 files changed, 427 insertions, 248 deletions
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
index 852e895506..c2e5659984 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
@@ -52,6 +52,9 @@ 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.Field;
+import org.apache.poi.hwpf.usermodel.Fields;
+import org.apache.poi.hwpf.usermodel.FieldsImpl;
import org.apache.poi.hwpf.usermodel.HWPFList;
import org.apache.poi.hwpf.usermodel.Notes;
import org.apache.poi.hwpf.usermodel.NotesImpl;
@@ -60,6 +63,7 @@ import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.Internal;
/**
@@ -129,6 +133,9 @@ public final class HWPFDocument extends HWPFDocumentCore
/** Holds the fields PLCFs */
protected FieldsTables _fieldsTables;
+ /** Holds the fields */
+ protected Fields _fields;
+
protected HWPFDocument()
{
super();
@@ -296,6 +303,7 @@ public final class HWPFDocument extends HWPFDocumentCore
_footnotes = new NotesImpl( _footnotesTables );
_fieldsTables = new FieldsTables(_tableStream, _fib);
+ _fields = new FieldsImpl(_fieldsTables);
}
public TextPieceTable getTextTable()
@@ -510,11 +518,24 @@ public final class HWPFDocument extends HWPFDocumentCore
/**
* @return FieldsTables object, that is able to extract fields descriptors from this document
+ * @deprecated
*/
+ @Deprecated
+ @Internal
public FieldsTables getFieldsTables() {
return _fieldsTables;
}
-
+
+ /**
+ * Returns user-friendly interface to access document {@link Field}s
+ *
+ * @return user-friendly interface to access document {@link Field}s
+ */
+ public Fields getFields()
+ {
+ return _fields;
+ }
+
/**
* Writes out the word file that is represented by an instance of this class.
*
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java
index fc9ece7a51..faee8345a6 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java
@@ -29,12 +29,12 @@ import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
-import org.apache.poi.hwpf.model.Field;
import org.apache.poi.hwpf.model.FieldsDocumentPart;
import org.apache.poi.hwpf.model.ListFormatOverride;
import org.apache.poi.hwpf.model.ListTables;
import org.apache.poi.hwpf.usermodel.Bookmark;
import org.apache.poi.hwpf.usermodel.CharacterRun;
+import org.apache.poi.hwpf.usermodel.Field;
import org.apache.poi.hwpf.usermodel.Notes;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
@@ -229,9 +229,8 @@ public abstract class AbstractWordConverter
{
if ( document instanceof HWPFDocument )
{
- Field aliveField = ( (HWPFDocument) document )
- .getFieldsTables().lookupFieldByStartOffset(
- FieldsDocumentPart.MAIN,
+ Field aliveField = ( (HWPFDocument) document ).getFields()
+ .getFieldByStartOffset( FieldsDocumentPart.MAIN,
characterRun.getStartOffset() );
if ( aliveField != null )
{
@@ -453,7 +452,7 @@ public abstract class AbstractWordConverter
return null;
HWPFDocument hwpfDocument = (HWPFDocument) wordDocument;
- Field field = hwpfDocument.getFieldsTables().lookupFieldByStartOffset(
+ Field field = hwpfDocument.getFields().getFieldByStartOffset(
FieldsDocumentPart.MAIN, startOffset );
if ( field == null )
return null;
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java
index 5b1df39e13..751081abdf 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java
@@ -23,7 +23,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -46,6 +45,7 @@ import org.apache.poi.hwpf.model.StyleSheet;
import org.apache.poi.hwpf.model.TextPiece;
import org.apache.poi.hwpf.sprm.SprmIterator;
import org.apache.poi.hwpf.sprm.SprmOperation;
+import org.apache.poi.hwpf.usermodel.Field;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
import org.apache.poi.hwpf.usermodel.Range;
@@ -311,7 +311,8 @@ public final class HWPFLister
for ( char c : text.toCharArray() )
{
if ( c < 30 )
- stringBuilder.append( "\\0x" + Integer.toHexString( c ) );
+ stringBuilder
+ .append( "\\0x" + Integer.toHexString( c ) );
else
stringBuilder.append( c );
}
@@ -340,8 +341,7 @@ public final class HWPFLister
for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
{
System.out.println( "=== Document part: " + part + " ===" );
- for ( org.apache.poi.hwpf.model.Field field : document
- .getFieldsTables().getFields( part ) )
+ for ( Field field : document.getFields().getFields( part ) )
{
System.out.println( field );
}
@@ -356,7 +356,7 @@ public final class HWPFLister
HWPFDocument doc = (HWPFDocument) _doc;
- Field fMainStream = HWPFDocumentCore.class
+ java.lang.reflect.Field fMainStream = HWPFDocumentCore.class
.getDeclaredField( "_mainStream" );
fMainStream.setAccessible( true );
byte[] mainStream = (byte[]) fMainStream.get( _doc );
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java
index 2c2b766a03..3bc59fbf64 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java
@@ -21,12 +21,7 @@ package org.apache.poi.hwpf.model;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
@@ -79,70 +74,13 @@ public class FieldsTables
@Deprecated
public static final int PLCFFLDTXBX = 6;
- /**
- * This is port and adaptation of Arrays.binarySearch from Java 6 (Apache
- * Harmony).
- */
- private static <T> int binarySearch( GenericPropertyNode[] array,
- int startIndex, int endIndex, int requiredStartOffset )
- {
- checkIndexForBinarySearch( array.length, startIndex, endIndex );
-
- int low = startIndex, mid = -1, high = endIndex - 1, result = 0;
- while ( low <= high )
- {
- mid = ( low + high ) >>> 1;
- int midStart = array[mid].getStart();
-
- if ( midStart == requiredStartOffset )
- {
- return mid;
- }
- else if ( midStart < requiredStartOffset )
- {
- low = mid + 1;
- }
- else
- {
- high = mid - 1;
- }
- }
- if ( mid < 0 )
- {
- int insertPoint = endIndex;
- for ( int index = startIndex; index < endIndex; index++ )
- {
- if ( requiredStartOffset < array[index].getStart() )
- {
- insertPoint = index;
- }
- }
- return -insertPoint - 1;
- }
- return -mid - ( result >= 0 ? 1 : 2 );
- }
-
- private static void checkIndexForBinarySearch( int length, int start,
- int end )
- {
- if ( start > end )
- {
- throw new IllegalArgumentException();
- }
- if ( length < end || 0 > start )
- {
- throw new ArrayIndexOutOfBoundsException();
- }
- }
-
private static ArrayList<PlexOfField> toArrayList( PlexOfCps plexOfCps )
{
if ( plexOfCps == null )
return new ArrayList<PlexOfField>();
- ArrayList<PlexOfField> fields = new ArrayList<PlexOfField>();
- fields.ensureCapacity( plexOfCps.length() );
-
+ ArrayList<PlexOfField> fields = new ArrayList<PlexOfField>(
+ plexOfCps.length() );
for ( int i = 0; i < plexOfCps.length(); i++ )
{
GenericPropertyNode propNode = plexOfCps.getProperty( i );
@@ -153,37 +91,20 @@ public class FieldsTables
return fields;
}
- private Map<FieldsDocumentPart, Map<Integer, Field>> _fieldsByOffset;
-
private Map<FieldsDocumentPart, PlexOfCps> _tables;
- private GenericPropertyNodeComparator comparator = new GenericPropertyNodeComparator();
-
public FieldsTables( byte[] tableStream, FileInformationBlock fib )
{
_tables = new HashMap<FieldsDocumentPart, PlexOfCps>(
FieldsDocumentPart.values().length );
- _fieldsByOffset = new HashMap<FieldsDocumentPart, Map<Integer, Field>>(
- FieldsDocumentPart.values().length );
for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
{
final PlexOfCps plexOfCps = readPLCF( tableStream, fib, part );
-
- _fieldsByOffset.put( part, parseFieldStructure( plexOfCps ) );
_tables.put( part, plexOfCps );
}
}
- public Collection<Field> getFields( FieldsDocumentPart part )
- {
- Map<Integer, Field> map = _fieldsByOffset.get( part );
- if ( map == null || map.isEmpty() )
- return Collections.emptySet();
-
- return Collections.unmodifiableCollection( map.values() );
- }
-
public ArrayList<PlexOfField> getFieldsPLCF( FieldsDocumentPart part )
{
return toArrayList( _tables.get( part ) );
@@ -195,146 +116,6 @@ public class FieldsTables
return getFieldsPLCF( FieldsDocumentPart.values()[partIndex] );
}
- public Field lookupFieldByStartOffset( FieldsDocumentPart documentPart,
- int offset )
- {
- Map<Integer, Field> map = _fieldsByOffset.get( documentPart );
- if ( map == null || map.isEmpty() )
- return null;
-
- return map.get( Integer.valueOf( offset ) );
- }
-
- private Map<Integer, Field> parseFieldStructure( PlexOfCps plexOfCps )
- {
- if ( plexOfCps == null )
- return new HashMap<Integer, Field>();
-
- GenericPropertyNode[] nodes = plexOfCps.toPropertiesArray();
- Arrays.sort( nodes, comparator );
- List<Field> fields = new ArrayList<Field>( nodes.length / 3 + 1 );
- parseFieldStructureImpl( nodes, 0, nodes.length, fields );
-
- HashMap<Integer, Field> result = new HashMap<Integer, Field>(
- fields.size() );
- for ( Field field : fields )
- {
- result.put( Integer.valueOf( field.getFieldStartOffset() ), field );
- }
- return result;
- }
-
- private void parseFieldStructureImpl( GenericPropertyNode[] nodes,
- int startOffsetInclusive, int endOffsetExclusive, List<Field> result )
- {
- int next = startOffsetInclusive;
- while ( next < endOffsetExclusive )
- {
- GenericPropertyNode startNode = nodes[next];
- PlexOfField startPlexOfField = new PlexOfField( startNode );
- if ( startPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_BEGIN_MARK )
- {
- /* Start mark seems to be missing */
- next++;
- continue;
- }
-
- /*
- * we have start node. end offset points to next node, separator or
- * end
- */
- int nextNodePositionInArray = binarySearch( nodes, next + 1,
- endOffsetExclusive, startNode.getEnd() );
- if ( nextNodePositionInArray < 0 )
- {
- /*
- * too bad, this start field mark doesn't have corresponding end
- * field mark or separator field mark in fields table
- */
- next++;
- continue;
- }
- GenericPropertyNode nextNode = nodes[nextNodePositionInArray];
- PlexOfField nextPlexOfField = new PlexOfField( nextNode );
-
- switch ( nextPlexOfField.getFld().getBoundaryType() )
- {
- case FieldDescriptor.FIELD_SEPARATOR_MARK:
- {
- GenericPropertyNode separatorNode = nextNode;
- PlexOfField separatorPlexOfField = nextPlexOfField;
-
- int endNodePositionInArray = binarySearch( nodes,
- nextNodePositionInArray, endOffsetExclusive,
- separatorNode.getEnd() );
- if ( endNodePositionInArray < 0 )
- {
- /*
- * too bad, this separator field mark doesn't have
- * corresponding end field mark in fields table
- */
- next++;
- continue;
- }
- GenericPropertyNode endNode = nodes[endNodePositionInArray];
- PlexOfField endPlexOfField = new PlexOfField( endNode );
-
- if ( endPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
- {
- /* Not and ending mark */
- next++;
- continue;
- }
-
- Field field = new Field( startPlexOfField,
- separatorPlexOfField, endPlexOfField );
- result.add( field );
-
- // adding included fields
- if ( startNode.getStart() + 1 < separatorNode.getStart() - 1 )
- {
- parseFieldStructureImpl( nodes, next + 1,
- nextNodePositionInArray, result );
- }
- if ( separatorNode.getStart() + 1 < endNode.getStart() - 1 )
- {
- parseFieldStructureImpl( nodes,
- nextNodePositionInArray + 1,
- endNodePositionInArray, result );
- }
-
- next = endNodePositionInArray + 1;
-
- break;
- }
- case FieldDescriptor.FIELD_END_MARK:
- {
- // we have no separator
- Field field = new Field( startPlexOfField, null,
- nextPlexOfField );
- result.add( field );
-
- // adding included fields
- if ( startNode.getStart() + 1 < nextNode.getStart() - 1 )
- {
- parseFieldStructureImpl( nodes, next + 1,
- nextNodePositionInArray, result );
- }
-
- next = nextNodePositionInArray + 1;
- break;
- }
- case FieldDescriptor.FIELD_BEGIN_MARK:
- default:
- {
- /* something is wrong, ignoring this mark along with start mark */
- next++;
- continue;
- }
- }
- }
- }
-
private PlexOfCps readPLCF( byte[] tableStream, FileInformationBlock fib,
FieldsDocumentPart documentPart )
{
@@ -377,14 +158,4 @@ public class FieldsTables
}
}
- private static final class GenericPropertyNodeComparator implements
- Comparator<GenericPropertyNode>
- {
- public int compare( GenericPropertyNode o1, GenericPropertyNode o2 )
- {
- int thisVal = o1.getStart();
- int anotherVal = o2.getStart();
- return thisVal < anotherVal ? -1 : thisVal == anotherVal ? 0 : 1;
- }
- }
}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Field.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Field.java
new file mode 100644
index 0000000000..72b5220dde
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Field.java
@@ -0,0 +1,55 @@
+package org.apache.poi.hwpf.usermodel;
+
+public interface Field
+{
+
+ Range firstSubrange( Range parent );
+
+ /**
+ * @return character position of first character after field (i.e.
+ * {@link #getMarkEndOffset()} + 1)
+ */
+ int getFieldEndOffset();
+
+ /**
+ * @return character position of first character in field (i.e.
+ * {@link #getFieldStartOffset()})
+ */
+ int getFieldStartOffset();
+
+ /**
+ * @return character position of end field mark
+ */
+ int getMarkEndOffset();
+
+ /**
+ * @return character position of separator field mark (if present,
+ * {@link NullPointerException} otherwise)
+ */
+ int getMarkSeparatorOffset();
+
+ /**
+ * @return character position of start field mark
+ */
+ int getMarkStartOffset();
+
+ int getType();
+
+ boolean hasSeparator();
+
+ boolean isHasSep();
+
+ boolean isLocked();
+
+ boolean isNested();
+
+ boolean isPrivateResult();
+
+ boolean isResultDirty();
+
+ boolean isResultEdited();
+
+ boolean isZombieEmbed();
+
+ Range secondSubrange( Range parent );
+} \ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldImpl.java
index f12209a16b..4832b23f96 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldImpl.java
@@ -1,14 +1,38 @@
-package org.apache.poi.hwpf.model;
-
-import org.apache.poi.hwpf.usermodel.Range;
-
-public class Field
+/* ====================================================================
+ 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 org.apache.poi.hwpf.model.FieldDescriptor;
+import org.apache.poi.hwpf.model.PlexOfField;
+import org.apache.poi.util.Internal;
+
+/**
+ * TODO: document me
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+@Internal
+class FieldImpl implements Field
{
private PlexOfField endPlex;
private PlexOfField separatorPlex;
private PlexOfField startPlex;
- public Field( PlexOfField startPlex, PlexOfField separatorPlex,
+ public FieldImpl( PlexOfField startPlex, PlexOfField separatorPlex,
PlexOfField endPlex )
{
if ( startPlex == null )
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Fields.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Fields.java
new file mode 100644
index 0000000000..2db551e5e2
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Fields.java
@@ -0,0 +1,33 @@
+/* ====================================================================
+ 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.Collection;
+
+import org.apache.poi.hwpf.model.FieldsDocumentPart;
+
+/**
+ * User-friendly interface to access document {@link Field}s
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+public interface Fields
+{
+ Field getFieldByStartOffset( FieldsDocumentPart documentPart, int offset );
+
+ Collection<Field> getFields( FieldsDocumentPart part );
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldsImpl.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldsImpl.java
new file mode 100644
index 0000000000..3c5eba60e3
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldsImpl.java
@@ -0,0 +1,276 @@
+/* ====================================================================
+ 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.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hwpf.model.FieldDescriptor;
+import org.apache.poi.hwpf.model.FieldsDocumentPart;
+import org.apache.poi.hwpf.model.FieldsTables;
+import org.apache.poi.hwpf.model.PlexOfField;
+import org.apache.poi.util.Internal;
+
+/**
+ * Default implementation of {@link Field}
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+@Internal
+public class FieldsImpl implements Fields
+{
+ /**
+ * This is port and adaptation of Arrays.binarySearch from Java 6 (Apache
+ * Harmony).
+ */
+ private static <T> int binarySearch( List<PlexOfField> list,
+ int startIndex, int endIndex, int requiredStartOffset )
+ {
+ checkIndexForBinarySearch( list.size(), startIndex, endIndex );
+
+ int low = startIndex, mid = -1, high = endIndex - 1, result = 0;
+ while ( low <= high )
+ {
+ mid = ( low + high ) >>> 1;
+ int midStart = list.get( mid ).getFcStart();
+
+ if ( midStart == requiredStartOffset )
+ {
+ return mid;
+ }
+ else if ( midStart < requiredStartOffset )
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ high = mid - 1;
+ }
+ }
+ if ( mid < 0 )
+ {
+ int insertPoint = endIndex;
+ for ( int index = startIndex; index < endIndex; index++ )
+ {
+ if ( requiredStartOffset < list.get( index ).getFcStart() )
+ {
+ insertPoint = index;
+ }
+ }
+ return -insertPoint - 1;
+ }
+ return -mid - ( result >= 0 ? 1 : 2 );
+ }
+
+ private static void checkIndexForBinarySearch( int length, int start,
+ int end )
+ {
+ if ( start > end )
+ {
+ throw new IllegalArgumentException();
+ }
+ if ( length < end || 0 > start )
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ private Map<FieldsDocumentPart, Map<Integer, FieldImpl>> _fieldsByOffset;
+
+ private PlexOfFieldComparator comparator = new PlexOfFieldComparator();
+
+ public FieldsImpl( FieldsTables fieldsTables )
+ {
+ _fieldsByOffset = new HashMap<FieldsDocumentPart, Map<Integer, FieldImpl>>(
+ FieldsDocumentPart.values().length );
+
+ for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
+ {
+ List<PlexOfField> plexOfCps = fieldsTables.getFieldsPLCF( part );
+ _fieldsByOffset.put( part, parseFieldStructure( plexOfCps ) );
+ }
+ }
+
+ public Collection<Field> getFields( FieldsDocumentPart part )
+ {
+ Map<Integer, FieldImpl> map = _fieldsByOffset.get( part );
+ if ( map == null || map.isEmpty() )
+ return Collections.emptySet();
+
+ return Collections.<Field> unmodifiableCollection( map.values() );
+ }
+
+ public FieldImpl getFieldByStartOffset( FieldsDocumentPart documentPart,
+ int offset )
+ {
+ Map<Integer, FieldImpl> map = _fieldsByOffset.get( documentPart );
+ if ( map == null || map.isEmpty() )
+ return null;
+
+ return map.get( Integer.valueOf( offset ) );
+ }
+
+ private Map<Integer, FieldImpl> parseFieldStructure(
+ List<PlexOfField> plexOfFields )
+ {
+ if ( plexOfFields == null || plexOfFields.isEmpty() )
+ return new HashMap<Integer, FieldImpl>();
+
+ Collections.sort( plexOfFields, comparator );
+ List<FieldImpl> fields = new ArrayList<FieldImpl>(
+ plexOfFields.size() / 3 + 1 );
+ parseFieldStructureImpl( plexOfFields, 0, plexOfFields.size(), fields );
+
+ HashMap<Integer, FieldImpl> result = new HashMap<Integer, FieldImpl>(
+ fields.size() );
+ for ( FieldImpl field : fields )
+ {
+ result.put( Integer.valueOf( field.getFieldStartOffset() ), field );
+ }
+ return result;
+ }
+
+ private void parseFieldStructureImpl( List<PlexOfField> plexOfFields,
+ int startOffsetInclusive, int endOffsetExclusive,
+ List<FieldImpl> result )
+ {
+ int next = startOffsetInclusive;
+ while ( next < endOffsetExclusive )
+ {
+ PlexOfField startPlexOfField = plexOfFields.get( next );
+ if ( startPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_BEGIN_MARK )
+ {
+ /* Start mark seems to be missing */
+ next++;
+ continue;
+ }
+
+ /*
+ * we have start node. end offset points to next node, separator or
+ * end
+ */
+ int nextNodePositionInList = binarySearch( plexOfFields, next + 1,
+ endOffsetExclusive, startPlexOfField.getFcEnd() );
+ if ( nextNodePositionInList < 0 )
+ {
+ /*
+ * too bad, this start field mark doesn't have corresponding end
+ * field mark or separator field mark in fields table
+ */
+ next++;
+ continue;
+ }
+ PlexOfField nextPlexOfField = plexOfFields
+ .get( nextNodePositionInList );
+
+ switch ( nextPlexOfField.getFld().getBoundaryType() )
+ {
+ case FieldDescriptor.FIELD_SEPARATOR_MARK:
+ {
+ PlexOfField separatorPlexOfField = nextPlexOfField;
+
+ int endNodePositionInList = binarySearch( plexOfFields,
+ nextNodePositionInList, endOffsetExclusive,
+ separatorPlexOfField.getFcEnd() );
+ if ( endNodePositionInList < 0 )
+ {
+ /*
+ * too bad, this separator field mark doesn't have
+ * corresponding end field mark in fields table
+ */
+ next++;
+ continue;
+ }
+ PlexOfField endPlexOfField = plexOfFields
+ .get( endNodePositionInList );
+
+ if ( endPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
+ {
+ /* Not and ending mark */
+ next++;
+ continue;
+ }
+
+ FieldImpl field = new FieldImpl( startPlexOfField,
+ separatorPlexOfField, endPlexOfField );
+ result.add( field );
+
+ // adding included fields
+ if ( startPlexOfField.getFcStart() + 1 < separatorPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields, next + 1,
+ nextNodePositionInList, result );
+ }
+ if ( separatorPlexOfField.getFcStart() + 1 < endPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields,
+ nextNodePositionInList + 1, endNodePositionInList,
+ result );
+ }
+
+ next = endNodePositionInList + 1;
+
+ break;
+ }
+ case FieldDescriptor.FIELD_END_MARK:
+ {
+ // we have no separator
+ FieldImpl field = new FieldImpl( startPlexOfField, null,
+ nextPlexOfField );
+ result.add( field );
+
+ // adding included fields
+ if ( startPlexOfField.getFcStart() + 1 < nextPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields, next + 1,
+ nextNodePositionInList, result );
+ }
+
+ next = nextNodePositionInList + 1;
+ break;
+ }
+ case FieldDescriptor.FIELD_BEGIN_MARK:
+ default:
+ {
+ /* something is wrong, ignoring this mark along with start mark */
+ next++;
+ continue;
+ }
+ }
+ }
+ }
+
+ private static final class PlexOfFieldComparator implements
+ Comparator<PlexOfField>
+ {
+ public int compare( PlexOfField o1, PlexOfField o2 )
+ {
+ int thisVal = o1.getFcStart();
+ int anotherVal = o2.getFcStart();
+ return thisVal < anotherVal ? -1 : thisVal == anotherVal ? 0 : 1;
+ }
+ }
+
+}