]> source.dussan.org Git - poi.git/commitdiff
move Field interface to usermodel and create Fields interface as user-friendly replac...
authorSergey Vladimirov <sergey@apache.org>
Fri, 22 Jul 2011 19:38:14 +0000 (19:38 +0000)
committerSergey Vladimirov <sergey@apache.org>
Fri, 22 Jul 2011 19:38:14 +0000 (19:38 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1149704 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java
src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java
src/scratchpad/src/org/apache/poi/hwpf/model/Field.java [deleted file]
src/scratchpad/src/org/apache/poi/hwpf/model/FieldsTables.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Field.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldImpl.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Fields.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldsImpl.java [new file with mode: 0644]

index 852e895506d1aa448b624a9d1bd71caef02ea27e..c2e5659984e1d55e20307224e1f125efb19de9f0 100644 (file)
@@ -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.
    *
index fc9ece7a51e3a261bae1255c9f0b86e31881946d..faee8345a649acd53e9adb5647ee83af2f1b22ec 100644 (file)
@@ -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;
index 5b1df39e13a046c139cadee49de592bcfed486cb..751081abdfe5efa00199fdcdcffc18564e6cf580 100644 (file)
@@ -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/Field.java b/src/scratchpad/src/org/apache/poi/hwpf/model/Field.java
deleted file mode 100644 (file)
index f12209a..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-package org.apache.poi.hwpf.model;
-
-import org.apache.poi.hwpf.usermodel.Range;
-
-public class Field
-{
-    private PlexOfField endPlex;
-    private PlexOfField separatorPlex;
-    private PlexOfField startPlex;
-
-    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 Range firstSubrange( Range parent )
-    {
-        if ( hasSeparator() )
-        {
-            if ( getMarkStartOffset() + 1 == getMarkSeparatorOffset() )
-                return null;
-
-            return new Range( getMarkStartOffset() + 1,
-                    getMarkSeparatorOffset(), parent )
-            {
-                @Override
-                public String toString()
-                {
-                    return "FieldSubrange1 (" + super.toString() + ")";
-                }
-            };
-        }
-
-        if ( getMarkStartOffset() + 1 == getMarkEndOffset() )
-            return null;
-
-        return new Range( getMarkStartOffset() + 1, getMarkEndOffset(), parent )
-        {
-            @Override
-            public String toString()
-            {
-                return "FieldSubrange1 (" + super.toString() + ")";
-            }
-        };
-    }
-
-    /**
-     * @return character position of first character after field (i.e.
-     *         {@link #getMarkEndOffset()} + 1)
-     */
-    public int getFieldEndOffset()
-    {
-        /*
-         * sometimes plex looks like [100, 2000), where 100 is the position of
-         * field-end character, and 2000 - some other char position, far away
-         * from field (not inside). So taking into account only start --sergey
-         */
-        return endPlex.getFcStart() + 1;
-    }
-
-    /**
-     * @return character position of first character in field (i.e.
-     *         {@link #getFieldStartOffset()})
-     */
-    public int getFieldStartOffset()
-    {
-        return startPlex.getFcStart();
-    }
-
-    /**
-     * @return character position of end field mark
-     */
-    public int getMarkEndOffset()
-    {
-        return endPlex.getFcStart();
-    }
-
-    /**
-     * @return character position of separator field mark (if present,
-     *         {@link NullPointerException} otherwise)
-     */
-    public int getMarkSeparatorOffset()
-    {
-        return separatorPlex.getFcStart();
-    }
-
-    /**
-     * @return character position of start field mark
-     */
-    public int getMarkStartOffset()
-    {
-        return startPlex.getFcStart();
-    }
-
-    public int getType()
-    {
-        return startPlex.getFld().getFieldType();
-    }
-
-    public boolean hasSeparator()
-    {
-        return separatorPlex != null;
-    }
-
-    public boolean isHasSep()
-    {
-        return endPlex.getFld().isFHasSep();
-    }
-
-    public boolean isLocked()
-    {
-        return endPlex.getFld().isFLocked();
-    }
-
-    public boolean isNested()
-    {
-        return endPlex.getFld().isFNested();
-    }
-
-    public boolean isPrivateResult()
-    {
-        return endPlex.getFld().isFPrivateResult();
-    }
-
-    public boolean isResultDirty()
-    {
-        return endPlex.getFld().isFResultDirty();
-    }
-
-    public boolean isResultEdited()
-    {
-        return endPlex.getFld().isFResultEdited();
-    }
-
-    public boolean isZombieEmbed()
-    {
-        return endPlex.getFld().isFZombieEmbed();
-    }
-
-    public Range secondSubrange( Range parent )
-    {
-        if ( !hasSeparator()
-                || getMarkSeparatorOffset() + 1 == getMarkEndOffset() )
-            return null;
-
-        return new Range( getMarkSeparatorOffset() + 1, getMarkEndOffset(),
-                parent )
-        {
-            @Override
-            public String toString()
-            {
-                return "FieldSubrange2 (" + super.toString() + ")";
-            }
-        };
-    }
-
-    @Override
-    public String toString()
-    {
-        return "Field [" + getFieldStartOffset() + "; " + getFieldEndOffset()
-                + "] (type: 0x" + Integer.toHexString( getType() ) + " = "
-                + getType() + " )";
-    }
-}
index 2c2b766a03aaff685d5a757f9e10aa6a0e4aa381..3bc59fbf640f5c5b5403f17783c9953f3245e4ba 100644 (file)
@@ -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 (file)
index 0000000..72b5220
--- /dev/null
@@ -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/usermodel/FieldImpl.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/FieldImpl.java
new file mode 100644 (file)
index 0000000..4832b23
--- /dev/null
@@ -0,0 +1,209 @@
+/* ====================================================================
+   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 FieldImpl( 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 Range firstSubrange( Range parent )
+    {
+        if ( hasSeparator() )
+        {
+            if ( getMarkStartOffset() + 1 == getMarkSeparatorOffset() )
+                return null;
+
+            return new Range( getMarkStartOffset() + 1,
+                    getMarkSeparatorOffset(), parent )
+            {
+                @Override
+                public String toString()
+                {
+                    return "FieldSubrange1 (" + super.toString() + ")";
+                }
+            };
+        }
+
+        if ( getMarkStartOffset() + 1 == getMarkEndOffset() )
+            return null;
+
+        return new Range( getMarkStartOffset() + 1, getMarkEndOffset(), parent )
+        {
+            @Override
+            public String toString()
+            {
+                return "FieldSubrange1 (" + super.toString() + ")";
+            }
+        };
+    }
+
+    /**
+     * @return character position of first character after field (i.e.
+     *         {@link #getMarkEndOffset()} + 1)
+     */
+    public int getFieldEndOffset()
+    {
+        /*
+         * sometimes plex looks like [100, 2000), where 100 is the position of
+         * field-end character, and 2000 - some other char position, far away
+         * from field (not inside). So taking into account only start --sergey
+         */
+        return endPlex.getFcStart() + 1;
+    }
+
+    /**
+     * @return character position of first character in field (i.e.
+     *         {@link #getFieldStartOffset()})
+     */
+    public int getFieldStartOffset()
+    {
+        return startPlex.getFcStart();
+    }
+
+    /**
+     * @return character position of end field mark
+     */
+    public int getMarkEndOffset()
+    {
+        return endPlex.getFcStart();
+    }
+
+    /**
+     * @return character position of separator field mark (if present,
+     *         {@link NullPointerException} otherwise)
+     */
+    public int getMarkSeparatorOffset()
+    {
+        return separatorPlex.getFcStart();
+    }
+
+    /**
+     * @return character position of start field mark
+     */
+    public int getMarkStartOffset()
+    {
+        return startPlex.getFcStart();
+    }
+
+    public int getType()
+    {
+        return startPlex.getFld().getFieldType();
+    }
+
+    public boolean hasSeparator()
+    {
+        return separatorPlex != null;
+    }
+
+    public boolean isHasSep()
+    {
+        return endPlex.getFld().isFHasSep();
+    }
+
+    public boolean isLocked()
+    {
+        return endPlex.getFld().isFLocked();
+    }
+
+    public boolean isNested()
+    {
+        return endPlex.getFld().isFNested();
+    }
+
+    public boolean isPrivateResult()
+    {
+        return endPlex.getFld().isFPrivateResult();
+    }
+
+    public boolean isResultDirty()
+    {
+        return endPlex.getFld().isFResultDirty();
+    }
+
+    public boolean isResultEdited()
+    {
+        return endPlex.getFld().isFResultEdited();
+    }
+
+    public boolean isZombieEmbed()
+    {
+        return endPlex.getFld().isFZombieEmbed();
+    }
+
+    public Range secondSubrange( Range parent )
+    {
+        if ( !hasSeparator()
+                || getMarkSeparatorOffset() + 1 == getMarkEndOffset() )
+            return null;
+
+        return new Range( getMarkSeparatorOffset() + 1, getMarkEndOffset(),
+                parent )
+        {
+            @Override
+            public String toString()
+            {
+                return "FieldSubrange2 (" + super.toString() + ")";
+            }
+        };
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Field [" + getFieldStartOffset() + "; " + getFieldEndOffset()
+                + "] (type: 0x" + Integer.toHexString( getType() ) + " = "
+                + getType() + " )";
+    }
+}
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 (file)
index 0000000..2db551e
--- /dev/null
@@ -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 (file)
index 0000000..3c5eba6
--- /dev/null
@@ -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;
+        }
+    }
+
+}