From 63c571ca1f48a4675e649ebe08578587cc28b35b Mon Sep 17 00:00:00 2001 From: Sergey Vladimirov Date: Fri, 8 Jul 2011 14:32:20 +0000 Subject: [PATCH] add user-friendly way to access field properties if char is a beginning of field git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1144336 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/org/apache/poi/hwpf/model/Field.java | 150 ++++++++++++++++ .../apache/poi/hwpf/model/FieldsTables.java | 170 +++++++++++++++++- .../org/apache/poi/hwpf/model/PlexOfCps.java | 8 + .../hwpf/model/TestDocumentProperties.java | 9 +- .../hwpf/model/TestFileInformationBlock.java | 7 +- 5 files changed, 334 insertions(+), 10 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/Field.java diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java b/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java new file mode 100644 index 000000000..1c9a3b56d --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java @@ -0,0 +1,150 @@ +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() + " )"; + } +} 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 22ac3cdff..b6812f550 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java @@ -21,7 +21,11 @@ package org.apache.poi.hwpf.model; 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; @@ -33,6 +37,21 @@ 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 + { + 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 */ @@ -65,18 +84,163 @@ public class FieldsTables // The size in bytes of the FLD data structure private static final int FLD_SIZE = 2; - private HashMap _tables; + private Map _tables; + private Map> _fieldsByOffset; public FieldsTables( byte[] tableStream, FileInformationBlock fib ) { - _tables = new HashMap(); + _tables = new HashMap( PLCFFLDTXBX - PLCFFLDATN + 1 ); + _fieldsByOffset = new HashMap>( + 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 parseFieldStructure( PlexOfCps plexOfCps ) + { + if (plexOfCps == null) + return new HashMap(); + + GenericPropertyNode[] nodes = plexOfCps.toPropertiesArray(); + Arrays.sort( nodes, comparator ); + List fields = new ArrayList( nodes.length / 3 + 1 ); + parseFieldStructureImpl( nodes, 0, nodes.length, fields ); + + HashMap result = new HashMap( + 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 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 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 ) { diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java index c666ff53f..7a3684ef8 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java @@ -146,4 +146,12 @@ public final class PlexOfCps { return ( 4 * ( _iMac + 1 ) ) + ( _cbStruct * index ); } + + GenericPropertyNode[] toPropertiesArray() + { + if ( _props == null || _props.isEmpty() ) + return new GenericPropertyNode[0]; + + return _props.toArray( new GenericPropertyNode[_props.size()] ); + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestDocumentProperties.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestDocumentProperties.java index 63b47e9cb..0dc18659d 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestDocumentProperties.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestDocumentProperties.java @@ -17,12 +17,13 @@ package org.apache.poi.hwpf.model; -import junit.framework.*; -import org.apache.poi.hwpf.*; - -import java.lang.reflect.*; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; import java.util.Arrays; +import junit.framework.TestCase; +import org.apache.poi.hwpf.HWPFDocFixture; + public final class TestDocumentProperties extends TestCase { diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestFileInformationBlock.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestFileInformationBlock.java index dcfd2b117..2154fda9b 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestFileInformationBlock.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestFileInformationBlock.java @@ -17,10 +17,11 @@ package org.apache.poi.hwpf.model; -import junit.framework.*; -import org.apache.poi.hwpf.*; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; -import java.lang.reflect.*; +import junit.framework.TestCase; +import org.apache.poi.hwpf.HWPFDocFixture; public final class TestFileInformationBlock extends TestCase