--- /dev/null
+package org.apache.poi.hwpf.model;
+
+import org.apache.poi.hwpf.usermodel.Range;
+
+public class Field
+{
+ private PlexOfField startPlex;
+ private PlexOfField separatorPlex;
+ private PlexOfField endPlex;
+
+ public Field( PlexOfField startPlex, PlexOfField separatorPlex,
+ PlexOfField endPlex )
+ {
+ if ( startPlex == null )
+ throw new IllegalArgumentException( "startPlex == null" );
+ if ( endPlex == null )
+ throw new IllegalArgumentException( "endPlex == null" );
+
+ if ( startPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_BEGIN_MARK )
+ throw new IllegalArgumentException( "startPlex (" + startPlex
+ + ") is not type of FIELD_BEGIN" );
+
+ if ( separatorPlex != null
+ && separatorPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_SEPARATOR_MARK )
+ throw new IllegalArgumentException( "separatorPlex" + separatorPlex
+ + ") is not type of FIELD_SEPARATOR" );
+
+ if ( endPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
+ throw new IllegalArgumentException( "endPlex (" + endPlex
+ + ") is not type of FIELD_END" );
+
+ this.startPlex = startPlex;
+ this.separatorPlex = separatorPlex;
+ this.endPlex = endPlex;
+ }
+
+ public int getStartOffset()
+ {
+ return startPlex.getFcStart();
+ }
+
+ public int getEndOffset()
+ {
+ return endPlex.getFcEnd();
+ }
+
+ public boolean hasSeparator()
+ {
+ return separatorPlex != null;
+ }
+
+ public int getSeparatorOffset()
+ {
+ return separatorPlex.getFcStart();
+ }
+
+ public int getType()
+ {
+ return startPlex.getFld().getFieldType();
+ }
+
+ public boolean isZombieEmbed()
+ {
+ return endPlex.getFld().isFZombieEmbed();
+ }
+
+ public boolean isResultDirty()
+ {
+ return endPlex.getFld().isFResultDirty();
+ }
+
+ public boolean isResultEdited()
+ {
+ return endPlex.getFld().isFResultEdited();
+ }
+
+ public boolean isLocked()
+ {
+ return endPlex.getFld().isFLocked();
+ }
+
+ public boolean isPrivateResult()
+ {
+ return endPlex.getFld().isFPrivateResult();
+ }
+
+ public boolean isNested()
+ {
+ return endPlex.getFld().isFNested();
+ }
+
+ public boolean isHasSep()
+ {
+ return endPlex.getFld().isFHasSep();
+ }
+
+ public Range firstSubrange( Range parent )
+ {
+ if ( hasSeparator() )
+ {
+ if ( getStartOffset() + 1 == getSeparatorOffset() )
+ return null;
+
+ return new Range( getStartOffset() + 1, getSeparatorOffset(),
+ parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange1 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ if ( getStartOffset() + 1 == getEndOffset() )
+ return null;
+
+ return new Range( getStartOffset() + 1, getEndOffset(), parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange1 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ public Range secondSubrange( Range parent )
+ {
+ if ( !hasSeparator() || getSeparatorOffset() + 1 == getEndOffset() )
+ return null;
+
+ return new Range( getSeparatorOffset() + 1, getEndOffset(), parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange2 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Field [" + getStartOffset() + "; " + getEndOffset()
+ + "] (type: 0x" + Integer.toHexString( getType() ) + " = "
+ + getType() + " )";
+ }
+}
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
*/
public class FieldsTables
{
+ private static final byte[] BYTES_EMPTY = new byte[0];
+
+ 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;
+ }
+ }
+
+ private GenericPropertyNodeComparator comparator = new GenericPropertyNodeComparator();
+
/**
* annotation subdocument
*/
// The size in bytes of the FLD data structure
private static final int FLD_SIZE = 2;
- private HashMap<Integer, PlexOfCps> _tables;
+ private Map<Integer, PlexOfCps> _tables;
+ private Map<Integer, Map<Integer, Field>> _fieldsByOffset;
public FieldsTables( byte[] tableStream, FileInformationBlock fib )
{
- _tables = new HashMap<Integer, PlexOfCps>();
+ _tables = new HashMap<Integer, PlexOfCps>( PLCFFLDTXBX - PLCFFLDATN + 1 );
+ _fieldsByOffset = new HashMap<Integer, Map<Integer, Field>>(
+ PLCFFLDTXBX - PLCFFLDATN + 1 );
for ( int i = PLCFFLDATN; i <= PLCFFLDTXBX; i++ )
{
- _tables.put( Integer.valueOf( i ), readPLCF( tableStream, fib, i ) );
+ final PlexOfCps plexOfCps = readPLCF( tableStream, fib, i );
+ _fieldsByOffset.put( Integer.valueOf( i ),
+ parseFieldStructure( plexOfCps ) );
+ _tables.put( Integer.valueOf( i ), plexOfCps );
}
}
+ 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.getStartOffset() ), 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 = Arrays.binarySearch(
+ nodes,
+ next + 1,
+ endOffsetExclusive,
+ new GenericPropertyNode( startNode.getEnd(), startNode
+ .getEnd() + 1, BYTES_EMPTY ), comparator );
+ 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 = Arrays.binarySearch( nodes,
+ nextNodePositionInArray, endOffsetExclusive,
+ new GenericPropertyNode( separatorNode.getEnd(),
+ separatorNode.getEnd() + 1, BYTES_EMPTY ),
+ comparator );
+ 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 );
+
+ 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;
+ }
+ }
+ }
+ }
+
+ public Field lookupFieldByStartOffset( int documentPart, int offset )
+ {
+ Map<Integer, Field> map = _fieldsByOffset.get( Integer
+ .valueOf( documentPart ) );
+ if ( map == null || map.isEmpty() )
+ return null;
+
+ return map.get( Integer.valueOf( offset ) );
+ }
+
private PlexOfCps readPLCF( byte[] tableStream, FileInformationBlock fib,
int type )
{