]> source.dussan.org Git - poi.git/commitdiff
add user-friendly way to access field properties if char is a beginning of field
authorSergey Vladimirov <sergey@apache.org>
Fri, 8 Jul 2011 14:32:20 +0000 (14:32 +0000)
committerSergey Vladimirov <sergey@apache.org>
Fri, 8 Jul 2011 14:32:20 +0000 (14:32 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1144336 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hwpf/model/Field.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java
src/scratchpad/src/org/apache/poi/hwpf/model/PlexOfCps.java
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestDocumentProperties.java
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestFileInformationBlock.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 (file)
index 0000000..1c9a3b5
--- /dev/null
@@ -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() + " )";
+    }
+}
index 22ac3cdff8af14861b09df3c1e7ce0bb96f38957..b6812f550f3acca20b74a071a1bb7854837662cd 100644 (file)
@@ -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<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
      */
@@ -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<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 )
     {
index c666ff53fc8705568c171c9a677cf56bd71d7672..7a3684ef801ac97f2f5f47750168a5f30decd8ab 100644 (file)
@@ -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()] );
+    }
 }
index 63b47e9cbb4b6bec841050c69e676eb10ca46f49..0dc18659d53317c259cb247342bccf93057a28cc 100644 (file)
 
 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
 {
index dcfd2b117269eb1218606878dbc098e560112534..2154fda9bed9a2e22069eea173b1744cca2a829b 100644 (file)
 
 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