]> source.dussan.org Git - poi.git/commitdiff
refactor list format override structures (was marked with @Internal annotation)
authorSergey Vladimirov <sergey@apache.org>
Sun, 23 Sep 2012 12:56:09 +0000 (12:56 +0000)
committerSergey Vladimirov <sergey@apache.org>
Sun, 23 Sep 2012 12:56:09 +0000 (12:56 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1389037 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
src/java/org/apache/poi/util/ArrayUtil.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/LFOData.java
src/scratchpad/src/org/apache/poi/hwpf/model/ListTables.java
src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/types/PAPAbstractType.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/ListEntry.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestListTables.java
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBug50075.java
src/types/definitions/lfo_type.xml

index 3461ee71527cfd4c377a394fe662c464572f127c..c8d46e134123c97ff8c9c21eac6ed3c5bd070193 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.util;
 
+
 /**
  * Utility classes for dealing with arrays.
  *
@@ -129,6 +130,28 @@ public class ArrayUtil
         return result;
     }
 
+    /**
+     * Copies the specified array into specified result array, truncating or
+     * padding with zeros (if necessary) so the copy has the specified length.
+     * This method is temporary replace for Arrays.copyOf() until we start to
+     * require JDK 1.6.
+     * 
+     * @param source
+     *            the array to be copied
+     * @param result
+     *            the array to be filled and returned
+     * @throws NegativeArraySizeException
+     *             if <tt>newLength</tt> is negative
+     * @throws NullPointerException
+     *             if <tt>original</tt> is null
+     */
+    public static <T> T[] copyOf( T[] source, T[] result )
+    {
+        System.arraycopy( source, 0, result, 0,
+                Math.min( source.length, result.length ) );
+        return result;
+    }
+
     /**
      * Copies the specified range of the specified array into a new array.
      * The initial index of the range (<tt>from</tt>) must lie between zero
index c7ddf975181d8ef85f4164c12d6ec4a70748b445..a27eec046a7aba622ed2709920424683375237bd 100644 (file)
@@ -30,13 +30,13 @@ import java.util.regex.Pattern;
 import org.apache.poi.hpsf.SummaryInformation;
 import org.apache.poi.hwpf.HWPFDocument;
 import org.apache.poi.hwpf.HWPFDocumentCore;
+import org.apache.poi.hwpf.converter.AbstractWordUtils.NumberingState;
 import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
 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.HWPFList;
 import org.apache.poi.hwpf.usermodel.Notes;
 import org.apache.poi.hwpf.usermodel.OfficeDrawing;
 import org.apache.poi.hwpf.usermodel.Paragraph;
@@ -172,6 +172,8 @@ public abstract class AbstractWordConverter
 
     private FontReplacer fontReplacer = new DefaultFontReplacer();
 
+    private NumberingState numberingState = new NumberingState();
+
     private PicturesManager picturesManager;
 
     /**
@@ -1022,9 +1024,6 @@ public abstract class AbstractWordConverter
     protected void processParagraphes( HWPFDocumentCore wordDocument,
             Element flow, Range range, int currentTableLevel )
     {
-        final ListTables listTables = wordDocument.getListTables();
-        int currentListInfo = 0;
-
         final int paragraphs = range.numParagraphs();
         for ( int p = 0; p < paragraphs; p++ )
         {
@@ -1054,36 +1053,15 @@ public abstract class AbstractWordConverter
                 processPageBreak( wordDocument, flow );
             }
 
-            if ( paragraph.getIlfo() != currentListInfo )
-            {
-                currentListInfo = paragraph.getIlfo();
-            }
-
-            if ( currentListInfo != 0 )
+            if ( paragraph.isInList() )
             {
-                if ( listTables != null )
-                {
-                    final ListFormatOverride listFormatOverride = listTables
-                            .getOverride( paragraph.getIlfo() );
+                HWPFList hwpfList = paragraph.getList();
 
-                    String label = AbstractWordUtils.getBulletText( listTables,
-                            paragraph, listFormatOverride.getLsid() );
+                String label = AbstractWordUtils.getBulletText( numberingState,
+                        hwpfList, (char) paragraph.getIlvl() );
 
-                    processParagraph( wordDocument, flow, currentTableLevel,
-                            paragraph, label );
-                }
-                else
-                {
-                    logger.log( POILogger.WARN,
-                            "Paragraph #" + paragraph.getStartOffset() + "-"
-                                    + paragraph.getEndOffset()
-                                    + " has reference to list structure #"
-                                    + currentListInfo
-                                    + ", but listTables not defined in file" );
-
-                    processParagraph( wordDocument, flow, currentTableLevel,
-                            paragraph, AbstractWordUtils.EMPTY );
-                }
+                processParagraph( wordDocument, flow, currentTableLevel,
+                        paragraph, label );
             }
             else
             {
index caed3ad41c050374a97fb68cf1bfa4ab532c3391..d008ec907c63468d3d578fe6411362bb4fc284a1 100644 (file)
@@ -41,7 +41,8 @@ import org.apache.poi.hwpf.model.CHPX;
 import org.apache.poi.hwpf.model.FieldsDocumentPart;
 import org.apache.poi.hwpf.model.FileInformationBlock;
 import org.apache.poi.hwpf.model.GenericPropertyNode;
-import org.apache.poi.hwpf.model.ListFormatOverride;
+import org.apache.poi.hwpf.model.LFO;
+import org.apache.poi.hwpf.model.LFOData;
 import org.apache.poi.hwpf.model.ListLevel;
 import org.apache.poi.hwpf.model.ListTables;
 import org.apache.poi.hwpf.model.PAPFormattedDiskPage;
@@ -716,27 +717,33 @@ public final class HWPFLister
     {
         if ( paragraph.getIlfo() != 0 )
         {
-            final ListFormatOverride listFormatOverride = listTables
-                    .getOverride( paragraph.getIlfo() );
+            final LFO lfo = listTables.getLfo( paragraph.getIlfo() );
+            System.out.println( "PAP's LFO: " + lfo );
 
-            System.out.println( "PAP's LFO: " + listFormatOverride );
+            final LFOData lfoData = listTables.getLfoData( paragraph.getIlfo() );
+            System.out.println( "PAP's LFOData: " + lfoData );
 
-            final ListLevel listLevel = listTables.getLevel(
-                    listFormatOverride.getLsid(), paragraph.getIlvl() );
-
-            System.out.println( "PAP's ListLevel: " + listLevel );
-            if ( listLevel.getGrpprlPapx() != null )
+            if ( lfo != null )
             {
-                System.out.println( "PAP's ListLevel PAPX:" );
-                dumpSprms( new SprmIterator( listLevel.getGrpprlPapx(), 0 ),
-                        "* " );
-            }
+                final ListLevel listLevel = listTables.getLevel( lfo.getLsid(),
+                        paragraph.getIlvl() );
 
-            if ( listLevel.getGrpprlPapx() != null )
-            {
-                System.out.println( "PAP's ListLevel CHPX:" );
-                dumpSprms( new SprmIterator( listLevel.getGrpprlChpx(), 0 ),
-                        "* " );
+                System.out.println( "PAP's ListLevel: " + listLevel );
+                if ( listLevel.getGrpprlPapx() != null )
+                {
+                    System.out.println( "PAP's ListLevel PAPX:" );
+                    dumpSprms(
+                            new SprmIterator( listLevel.getGrpprlPapx(), 0 ),
+                            "* " );
+                }
+
+                if ( listLevel.getGrpprlPapx() != null )
+                {
+                    System.out.println( "PAP's ListLevel CHPX:" );
+                    dumpSprms(
+                            new SprmIterator( listLevel.getGrpprlChpx(), 0 ),
+                            "* " );
+                }
             }
         }
     }
index 9910dcaef8b3d17e53f1bd0bb5e92d3ec6c7d83e..ff2e69c9c9b564d5b8af2192f57c0cdc68f126c5 100644 (file)
@@ -31,12 +31,18 @@ import org.apache.poi.util.LittleEndian;
  * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
  */
 @Internal
-class LFOData
+public class LFOData
 {
     private int _cp;
 
     private ListFormatOverrideLevel[] _rgLfoLvl;
 
+    public LFOData()
+    {
+        _cp = 0;
+        _rgLfoLvl = new ListFormatOverrideLevel[0];
+    }
+
     LFOData( byte[] buf, int startOffset, int cLfolvl )
     {
         int offset = startOffset;
@@ -52,17 +58,17 @@ class LFOData
         }
     }
 
-    int getCp()
+    public int getCp()
     {
         return _cp;
     }
 
-    ListFormatOverrideLevel[] getRgLfoLvl()
+    public ListFormatOverrideLevel[] getRgLfoLvl()
     {
         return _rgLfoLvl;
     }
 
-    int getSizeInBytes()
+    public int getSizeInBytes()
     {
         int result = 0;
         result += LittleEndian.INT_SIZE;
index 009f42f7b496dd8ba32a9d604dbaf72b0081a194..eaea30733d3d142d1bcc10a23f9bad8adadb66a7 100644 (file)
@@ -19,15 +19,8 @@ package org.apache.poi.hwpf.model;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.LinkedHashMap;
 import java.util.NoSuchElementException;
-import java.util.Set;
 
 import org.apache.poi.hwpf.model.io.HWPFOutputStream;
 import org.apache.poi.util.Internal;
@@ -43,8 +36,11 @@ public final class ListTables
 {
   private static POILogger log = POILogFactory.getLogger(ListTables.class);
 
-  ListMap _listMap = new ListMap();
-  ArrayList<ListFormatOverride> _overrideList = new ArrayList<ListFormatOverride>();
+    /**
+     * Both PlfLst and the following LVLs
+     */
+    private final LinkedHashMap<Integer, ListData> _listMap = new LinkedHashMap<Integer, ListData>();
+    private PlfLfo _plfLfo;
 
   public ListTables()
   {
@@ -52,100 +48,43 @@ public final class ListTables
   }
 
     public ListTables( byte[] tableStream, final int lstOffset,
-            final int lfoOffset )
+            final int fcPlfLfo, final int lcbPlfLfo )
     {
-        {
-            /*
-             * The PlfLst structure contains the list formatting information for
-             * the document. -- Page 425 of 621. [MS-DOC] -- v20110315 Word
-             * (.doc) Binary File Format
-             */
-            int offset = lstOffset;
-
-            int cLst = LittleEndian.getShort( tableStream, offset );
-            offset += LittleEndian.SHORT_SIZE;
-            int levelOffset = offset + ( cLst * LSTF.getSize() );
 
-            for ( int x = 0; x < cLst; x++ )
-            {
-                ListData lst = new ListData( tableStream, offset );
-                _listMap.put( Integer.valueOf( lst.getLsid() ), lst );
-                offset += LSTF.getSize();
+        /*
+         * The PlfLst structure contains the list formatting information for the
+         * document. -- Page 425 of 621. [MS-DOC] -- v20110315 Word (.doc)
+         * Binary File Format
+         */
+        int offset = lstOffset;
 
-                int num = lst.numLevels();
-                for ( int y = 0; y < num; y++ )
-                {
-                    ListLevel lvl = new ListLevel();
-                    levelOffset += lvl.read( tableStream, levelOffset );
-                    lst.setLevel( y, lvl );
-                }
-            }
-        }
+        int cLst = LittleEndian.getShort( tableStream, offset );
+        offset += LittleEndian.SHORT_SIZE;
+        int levelOffset = offset + ( cLst * LSTF.getSize() );
 
+        for ( int x = 0; x < cLst; x++ )
         {
-            /*
-             * The PlfLfo structure contains the list format override data for
-             * the document. -- Page 424 of 621. [MS-DOC] -- v20110315 Word
-             * (.doc) Binary File Format
-             */
-            int offset = lfoOffset;
-
-            /*
-             * lfoMac (4 bytes): An unsigned integer that specifies the count of
-             * elements in both the rgLfo and rgLfoData arrays. -- Page 424 of
-             * 621. [MS-DOC] -- v20110315 Word (.doc) Binary File Format
-             */
-            long lfoMac = LittleEndian.getUInt( tableStream, offset );
-            offset += LittleEndian.INT_SIZE;
+            ListData lst = new ListData( tableStream, offset );
+            _listMap.put( Integer.valueOf( lst.getLsid() ), lst );
+            offset += LSTF.getSize();
 
-            /*
-             * An array of LFO structures. The number of elements in this array
-             * is specified by lfoMac. -- Page 424 of 621. [MS-DOC] -- v20110315
-             * Word (.doc) Binary File Format
-             */
-            for ( int x = 0; x < lfoMac; x++ )
+            int num = lst.numLevels();
+            for ( int y = 0; y < num; y++ )
             {
-                ListFormatOverride lfo = new ListFormatOverride( tableStream,
-                        offset );
-                offset += LFO.getSize();
-                _overrideList.add( lfo );
-            }
-
-            /*
-             * An array of LFOData that is parallel to rgLfo. The number of
-             * elements that are contained in this array is specified by lfoMac.
-             * -- Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc) Binary File
-             * Format
-             */
-            for ( int x = 0; x < lfoMac; x++ )
-            {
-                ListFormatOverride lfo = _overrideList.get( x );
-                LFOData lfoData = new LFOData( tableStream, offset,
-                        lfo.numOverrides() );
-                lfo.setLfoData( lfoData );
-                offset += lfoData.getSizeInBytes();
+                ListLevel lvl = new ListLevel();
+                levelOffset += lvl.read( tableStream, levelOffset );
+                lst.setLevel( y, lvl );
             }
         }
-    }
 
-  public int addList(ListData lst, ListFormatOverride override)
-  {
-    int lsid = lst.getLsid();
-    while (_listMap.get(Integer.valueOf(lsid)) != null)
-    {
-      lsid = lst.resetListID();
-      override.setLsid(lsid);
+        this._plfLfo = new PlfLfo( tableStream, fcPlfLfo, lcbPlfLfo );
     }
-    _listMap.put(Integer.valueOf(lsid), lst);
-    _overrideList.add(override);
-    return lsid;
-  }
 
     public void writeListDataTo( FileInformationBlock fib,
             HWPFOutputStream tableStream ) throws IOException
     {
         final int startOffset = tableStream.getOffset();
-        fib.setFcPlcfLst( startOffset );
+        fib.setFcPlfLst( startOffset );
 
     int listSize = _listMap.size();
 
@@ -156,7 +95,7 @@ public final class ListTables
     LittleEndian.putShort(shortHolder, (short)listSize);
     tableStream.write(shortHolder);
 
-    for(Integer x : _listMap.sortedKeys()) {
+    for(Integer x : _listMap.keySet()) {
       ListData lst = _listMap.get(x);
       tableStream.write(lst.toByteArray());
       ListLevel[] lvls = lst.getLevels();
@@ -171,59 +110,35 @@ public final class ListTables
          * account for the array of LVLs. -- Page 76 of 621 -- [MS-DOC] --
          * v20110315 Word (.doc) Binary File Format
          */
-        fib.setLcbPlcfLst( tableStream.getOffset() - startOffset );
+        fib.setLcbPlfLst( tableStream.getOffset() - startOffset );
         tableStream.write( levelBuf.toByteArray() );
     }
 
-    public void writeListOverridesTo( HWPFOutputStream tableStream )
-            throws IOException
+    public void writeListOverridesTo( FileInformationBlock fib,
+            HWPFOutputStream tableStream ) throws IOException
     {
-        LittleEndian.putUInt( _overrideList.size(), tableStream );
-
-        for ( ListFormatOverride lfo : _overrideList )
-        {
-            tableStream.write( lfo.getLfo().serialize() );
-        }
-
-        for ( ListFormatOverride lfo : _overrideList )
-        {
-            lfo.getLfoData().writeTo( tableStream );
-        }
+        _plfLfo.writeTo( fib, tableStream );
     }
 
-  public ListFormatOverride getOverride(int lfoIndex)
-  {
-    return _overrideList.get(lfoIndex - 1);
-  }
-
-  public int getOverrideCount() {
-    return _overrideList.size();
-  }
+    public LFO getLfo( int ilfo ) throws NoSuchElementException
+    {
+        return _plfLfo.getLfo( ilfo );
+    }
 
-  public int getOverrideIndexFromListID(int lstid)
-  {
-    int returnVal = -1;
-    int size = _overrideList.size();
-    for (int x = 0; x < size; x++)
+    public LFOData getLfoData( int ilfo ) throws NoSuchElementException
     {
-      ListFormatOverride next = _overrideList.get(x);
-      if (next.getLsid() == lstid)
-      {
-        // 1-based index I think
-        returnVal = x+1;
-        break;
-      }
+        return _plfLfo.getLfoData( ilfo );
     }
-    if (returnVal == -1)
+
+    public int getOverrideIndexFromListID( int lsid )
+            throws NoSuchElementException
     {
-      throw new NoSuchElementException("No list found with the specified ID");
+        return _plfLfo.getIlfoByLsid( lsid );
     }
-    return returnVal;
-  }
 
-  public ListLevel getLevel(int listID, int level)
+  public ListLevel getLevel(int lsid, int level)
   {
-    ListData lst = _listMap.get(Integer.valueOf(listID));
+    ListData lst = _listMap.get(Integer.valueOf(lsid));
     if(level < lst.numLevels()) {
        ListLevel lvl = lst.getLevels()[level];
        return lvl;
@@ -232,112 +147,69 @@ public final class ListTables
        return null;
   }
 
-  public ListData getListData(int listID)
+  public ListData getListData(int lsid)
   {
-    return _listMap.get(Integer.valueOf(listID));
+    return _listMap.get(Integer.valueOf(lsid));
   }
 
-  public boolean equals(Object obj)
-  {
-    if (obj == null)
+    @Override
+    public int hashCode()
     {
-      return false;
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ( ( _listMap == null ) ? 0 : _listMap.hashCode() );
+        result = prime * result
+                + ( ( _plfLfo == null ) ? 0 : _plfLfo.hashCode() );
+        return result;
     }
 
-    ListTables tables = (ListTables)obj;
-
-    if (_listMap.size() == tables._listMap.size())
+    @Override
+    public boolean equals( Object obj )
     {
-      Iterator<Integer> it = _listMap.keySet().iterator();
-      while (it.hasNext())
-      {
-        Integer key = it.next();
-        ListData lst1 = _listMap.get(key);
-        ListData lst2 = tables._listMap.get(key);
-        if (!lst1.equals(lst2))
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        ListTables other = (ListTables) obj;
+        if ( _listMap == null )
         {
-          return false;
+            if ( other._listMap != null )
+                return false;
         }
-      }
-      int size = _overrideList.size();
-      if (size == tables._overrideList.size())
-      {
-        for (int x = 0; x < size; x++)
-        {
-          if (!_overrideList.get(x).equals(tables._overrideList.get(x)))
-          {
+        else if ( !_listMap.equals( other._listMap ) )
             return false;
-          }
+        if ( _plfLfo == null )
+        {
+            if ( other._plfLfo != null )
+                return false;
         }
+        else if ( !_plfLfo.equals( other._plfLfo ) )
+            return false;
         return true;
-      }
     }
-    return false;
-  }
-  
-  private static class ListMap implements Map<Integer, ListData> {
-     private ArrayList<Integer> keyList = new ArrayList<Integer>();
-     private HashMap<Integer,ListData> parent = new HashMap<Integer,ListData>();
-     private ListMap() {}
-     
-     public void clear() {
-        keyList.clear();
-        parent.clear();
-     }
 
-     public boolean containsKey(Object key) {
-        return parent.containsKey(key);
-     }
-
-     public boolean containsValue(Object value) {
-        return parent.containsValue(value);
-     }
-
-     public ListData get(Object key) {
-        return parent.get(key);
-     }
-
-     public boolean isEmpty() {
-        return parent.isEmpty();
-     }
-
-     public ListData put(Integer key, ListData value) {
-        keyList.add(key);
-        return parent.put(key, value);
-     }
-
-     public void putAll(Map<? extends Integer, ? extends ListData> map) {
-        for(Entry<? extends Integer, ? extends ListData> entry : map.entrySet()) {
-           put(entry.getKey(), entry.getValue());
+    public int addList( ListData lst, LFO lfo, LFOData lfoData )
+    {
+        int lsid = lst.getLsid();
+        while ( _listMap.get( Integer.valueOf( lsid ) ) != null )
+        {
+            lsid = lst.resetListID();
+            lfo.setLsid( lsid );
         }
-     }
-
-     public ListData remove(Object key) {
-        keyList.remove(key);
-        return parent.remove(key);
-     }
+        _listMap.put( Integer.valueOf( lsid ), lst );
 
-     public int size() {
-        return parent.size();
-     }
-
-     public Set<Entry<Integer, ListData>> entrySet() {
-        throw new IllegalStateException("Use sortedKeys() + get() instead");
-     }
-     
-     public List<Integer> sortedKeys() {
-        return Collections.unmodifiableList(keyList);
-     }
-     public Set<Integer> keySet() {
-        throw new IllegalStateException("Use sortedKeys() instead");
-     }
-
-     public Collection<ListData> values() {
-        ArrayList<ListData> values = new ArrayList<ListData>();
-        for(Integer key : keyList) {
-           values.add(parent.get(key));
+        if ( lfo == null && lfoData != null )
+        {
+            throw new IllegalArgumentException(
+                    "LFO and LFOData should be specified both or noone" );
         }
-        return values;
-     }
-  }
+        if ( lfo != null )
+        {
+            _plfLfo.add( lfo, lfoData );
+        }
+        return lsid;
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java
new file mode 100644 (file)
index 0000000..4fd8cc2
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *  ====================================================================
+ *    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.model;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+
+import org.apache.poi.hwpf.model.io.HWPFOutputStream;
+import org.apache.poi.util.ArrayUtil;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+/**
+ * The PlfLfo structure contains the list format override data for the document.
+ * <p>
+ * Documentation quoted from Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc)
+ * Binary File Format
+ * 
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+public class PlfLfo
+{
+    private static POILogger log = POILogFactory.getLogger( PlfLfo.class );
+
+    /**
+     * An unsigned integer that specifies the count of elements in both the
+     * rgLfo and rgLfoData arrays.
+     */
+    private int _lfoMac;
+
+    private LFO[] _rgLfo;
+
+    private LFOData[] _rgLfoData;
+
+    
+    PlfLfo( byte[] tableStream, int fcPlfLfo, int lcbPlfLfo )
+    {
+        /*
+         * The PlfLfo structure contains the list format override data for the
+         * document. -- Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc)
+         * Binary File Format
+         */
+        int offset = fcPlfLfo;
+
+        /*
+         * lfoMac (4 bytes): An unsigned integer that specifies the count of
+         * elements in both the rgLfo and rgLfoData arrays. -- Page 424 of 621.
+         * [MS-DOC] -- v20110315 Word (.doc) Binary File Format
+         */
+        long lfoMacLong = LittleEndian.getUInt( tableStream, offset );
+        offset += LittleEndian.INT_SIZE;
+
+        if ( lfoMacLong > Integer.MAX_VALUE )
+        {
+            throw new UnsupportedOperationException(
+                    "Apache POI doesn't support rgLfo/rgLfoData size large than "
+                            + Integer.MAX_VALUE + " elements" );
+        }
+
+        this._lfoMac = (int) lfoMacLong;
+        _rgLfo = new LFO[_lfoMac];
+        _rgLfoData = new LFOData[_lfoMac];
+
+        /*
+         * An array of LFO structures. The number of elements in this array is
+         * specified by lfoMac. -- Page 424 of 621. [MS-DOC] -- v20110315 Word
+         * (.doc) Binary File Format
+         */
+        for ( int x = 0; x < _lfoMac; x++ )
+        {
+            LFO lfo = new LFO( tableStream, offset );
+            offset += LFO.getSize();
+            _rgLfo[x] = lfo;
+        }
+
+        /*
+         * An array of LFOData that is parallel to rgLfo. The number of elements
+         * that are contained in this array is specified by lfoMac. -- Page 424
+         * of 621. [MS-DOC] -- v20110315 Word (.doc) Binary File Format
+         */
+        for ( int x = 0; x < _lfoMac; x++ )
+        {
+            LFOData lfoData = new LFOData( tableStream, offset,
+                    _rgLfo[x].getClfolvl() );
+            offset += lfoData.getSizeInBytes();
+            _rgLfoData[x] = lfoData;
+        }
+
+        if ( ( offset - fcPlfLfo ) != lcbPlfLfo )
+        {
+            log.log( POILogger.WARN, "Actual size of PlfLfo is "
+                    + ( offset - fcPlfLfo ) + " bytes, but expected "
+                    + lcbPlfLfo );
+        }
+    }
+
+    void add( LFO lfo, LFOData lfoData )
+    {
+        final int newLfoMac = _lfoMac + 1;
+
+        _rgLfo = ArrayUtil.copyOf( _rgLfo, new LFO[newLfoMac] );
+        _rgLfo[_lfoMac + 1] = lfo;
+
+        _rgLfoData = ArrayUtil.copyOf( _rgLfoData, new LFOData[_lfoMac + 1] );
+        _rgLfoData[_lfoMac + 1] = lfoData;
+
+        this._lfoMac = newLfoMac;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        PlfLfo other = (PlfLfo) obj;
+        if ( _lfoMac != other._lfoMac )
+            return false;
+        if ( !Arrays.equals( _rgLfo, other._rgLfo ) )
+            return false;
+        if ( !Arrays.equals( _rgLfoData, other._rgLfoData ) )
+            return false;
+        return true;
+    }
+
+    /**
+     * An unsigned integer that specifies the count of elements in both the
+     * rgLfo and rgLfoData arrays.
+     */
+    public int getLfoMac()
+    {
+        return _lfoMac;
+    }
+
+    public int getIlfoByLsid( int lsid )
+    {
+        for ( int i = 0; i < _lfoMac; i++ )
+        {
+            if ( _rgLfo[i].getLsid() == lsid )
+            {
+                return i + 1;
+            }
+        }
+        throw new NoSuchElementException( "LFO with lsid " + lsid
+                + " not found" );
+    }
+
+    public LFO getLfo( int ilfo ) throws NoSuchElementException
+    {
+        if ( ilfo <= 0 || ilfo > _lfoMac )
+        {
+            throw new NoSuchElementException( "LFO with ilfo " + ilfo
+                    + " not found. lfoMac is " + _lfoMac );
+        }
+        return _rgLfo[ilfo - 1];
+    }
+
+    public LFOData getLfoData( int ilfo ) throws NoSuchElementException
+    {
+        if ( ilfo <= 0 || ilfo > _lfoMac )
+        {
+            throw new NoSuchElementException( "LFOData with ilfo " + ilfo
+                    + " not found. lfoMac is " + _lfoMac );
+        }
+        return _rgLfoData[ilfo - 1];
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + _lfoMac;
+        result = prime * result + Arrays.hashCode( _rgLfo );
+        result = prime * result + Arrays.hashCode( _rgLfoData );
+        return result;
+    }
+
+    void writeTo( FileInformationBlock fib, HWPFOutputStream outputStream )
+            throws IOException
+    {
+        final int offset = outputStream.getOffset();
+        fib.setFcPlfLfo( offset );
+
+        LittleEndian.putUInt( _lfoMac, outputStream );
+
+        byte[] bs = new byte[LFO.getSize() * _lfoMac];
+        for ( int i = 0; i < _lfoMac; i++ )
+        {
+            _rgLfo[i].serialize( bs, i * LFO.getSize() );
+        }
+        outputStream.write( bs, 0, LFO.getSize() * _lfoMac );
+
+        for ( int i = 0; i < _lfoMac; i++ )
+        {
+            _rgLfoData[i].writeTo( outputStream );
+        }
+        fib.setLcbPlfLfo( outputStream.getOffset() - offset );
+    }
+}
index 5415f182481545241006f646ed31b6a168e89277..f9918fc4e3008aca409e2bbfbf811a50a272dd59 100644 (file)
@@ -508,7 +508,26 @@ public abstract class PAPAbstractType
     }
 
     /**
-     * 1-based index into the pllfo (lists structure), if non-zero.
+     * "A 16-bit signed integer value that is used to determine which list
+     * contains the paragraph. This value MUST be one of the following:
+     * 
+     * 0x0000 -- This paragraph is not in a list, and any list formatting on the
+     * paragraph is removed.
+     * 
+     * 0x0001 - 0x07FE -- The value is a 1-based index into PlfLfo.rgLfo. The
+     * LFO at this index defines the list that this paragraph is in.
+     * 
+     * 0xF801 -- This paragraph is not in a list.
+     * 
+     * 0xF802 - 0xFFFF -- The value is the negation of a 1-based index into
+     * PlfLfo.rgLfo. The LFO at this index defines the list that this paragraph
+     * is in. The logical left indentation (see sprmPDxaLeft) and the logical
+     * left first line indentation (see sprmPDxaLeft1) of the paragraph MUST be
+     * preserved despite any list formatting.
+     * 
+     * By default, a paragraph is not in a list."
+     * 
+     * Quote from [MS-DOC] -- v20110315, page 125
      */
     @Internal
     public int getIlfo()
@@ -517,7 +536,25 @@ public abstract class PAPAbstractType
     }
 
     /**
-     * 1-based index into the pllfo (lists structure), if non-zero.
+     * "A 16-bit signed integer value that is used to determine which list
+     * contains the paragraph. This value MUST be one of the following:
+     * 
+     * 0x0000 -- This paragraph is not in a list, and any list formatting on the
+     * paragraph is removed.
+     * 
+     * 0x0001 - 0x07FE -- The value is a 1-based index into PlfLfo.rgLfo. The
+     * LFO at this index defines the list that this paragraph is in.
+     * 
+     * 0xF801 -- This paragraph is not in a list.
+     * 
+     * 0xF802 - 0xFFFF -- The value is the negation of a 1-based index into
+     * PlfLfo.rgLfo. The LFO at this index defines the list that this paragraph
+     * is in. The logical left indentation (see sprmPDxaLeft) and the logical
+     * left first line indentation (see sprmPDxaLeft1) of the paragraph MUST be
+     * preserved despite any list formatting. By default, a paragraph is not in
+     * a list."
+     * 
+     * Quote from [MS-DOC] -- v20110315, page 125
      */
     @Internal
     public void setIlfo( int field_9_ilfo )
index 25b41da777c6b5d7285255336272d64c9e75b8b8..378c26c90ee45745201ed7b40c1bd8544626567c 100644 (file)
 
 package org.apache.poi.hwpf.usermodel;
 
-import org.apache.poi.hwpf.model.ListFormatOverride;
-import org.apache.poi.hwpf.model.ListFormatOverrideLevel;
-import org.apache.poi.hwpf.model.ListLevel;
 import org.apache.poi.hwpf.model.ListTables;
 import org.apache.poi.hwpf.model.PAPX;
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
 
-public final class ListEntry
-  extends Paragraph
+public final class ListEntry extends Paragraph
 {
-       private static POILogger log = POILogFactory.getLogger(ListEntry.class);
-
-       ListLevel _level;
-       ListFormatOverrideLevel _overrideLevel;
-
-       @Internal
+    @Internal
     ListEntry( PAPX papx, ParagraphProperties properties, Range parent )
     {
         super( papx, properties, parent );
-
-        final ListTables tables = parent._doc.getListTables();
-        if ( tables != null && _props.getIlfo() < tables.getOverrideCount() )
-        {
-            ListFormatOverride override = tables.getOverride( _props.getIlfo() );
-            _overrideLevel = override.getOverrideLevel( _props.getIlvl() );
-            _level = tables.getLevel( override.getLsid(), _props.getIlvl() );
-        }
-        else
-        {
-            log.log( POILogger.WARN,
-                    "No ListTables found for ListEntry - document probably partly corrupt, "
-                            + "and you may experience problems" );
-        }
     }
 
     @Deprecated
-  ListEntry(PAPX papx, Range parent, ListTables tables)
-  {
-    super(papx, parent);
-
-    if(tables != null && _props.getIlfo() < tables.getOverrideCount()) {
-           ListFormatOverride override = tables.getOverride(_props.getIlfo());
-           _overrideLevel = override.getOverrideLevel(_props.getIlvl());
-           _level = tables.getLevel(override.getLsid(), _props.getIlvl());
-    } else {
-       log.log(POILogger.WARN, "No ListTables found for ListEntry - document probably partly corrupt, and you may experience problems");
+    ListEntry( PAPX papx, Range parent, ListTables tables )
+    {
+        super( papx, parent );
     }
-  }
 
-  @Deprecated
-  public int type()
-  {
-    return TYPE_LISTENTRY;
-  }
+    @Deprecated
+    public int type()
+    {
+        return TYPE_LISTENTRY;
+    }
 }
index 6d99465abcdb34dfb76b17b926718241bf9f3a93..65f907691ba906a94eae6953ce4f617373140fd6 100644 (file)
 
 package org.apache.poi.hwpf.usermodel;
 
-import org.apache.poi.hwpf.HWPFDocumentCore;
+import java.util.NoSuchElementException;
 
-import org.apache.poi.hwpf.model.ListFormatOverride;
+import org.apache.poi.hwpf.HWPFDocumentCore;
+import org.apache.poi.hwpf.model.LFO;
 import org.apache.poi.hwpf.model.ListLevel;
 import org.apache.poi.hwpf.model.ListTables;
 import org.apache.poi.hwpf.model.PAPX;
@@ -28,8 +29,14 @@ import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor;
 import org.apache.poi.hwpf.sprm.SprmBuffer;
 import org.apache.poi.hwpf.sprm.TableSprmCompressor;
 import org.apache.poi.util.Internal;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+public class Paragraph extends Range implements Cloneable
+{
+
+    private static POILogger log = POILogFactory.getLogger( Paragraph.class );
 
-public class Paragraph extends Range implements Cloneable {
   public final static short SPRM_JC = 0x2403;
   public final static short SPRM_FSIDEBYSIDE = 0x2404;
   public final static short SPRM_FKEEP = 0x2405;
@@ -105,20 +112,31 @@ public class Paragraph extends Range implements Cloneable {
 
         if ( properties.getIlfo() != 0 && listTables != null )
         {
-            final ListFormatOverride listFormatOverride = listTables
-                    .getOverride( properties.getIlfo() );
-            final ListLevel listLevel = listTables.getLevel(
-                    listFormatOverride.getLsid(), properties.getIlvl() );
-
-            if ( listLevel!=null && listLevel.getGrpprlPapx() != null )
+            LFO lfo = null;
+            try
+            {
+                lfo = listTables.getLfo( properties.getIlfo() );
+            }
+            catch ( NoSuchElementException exc )
+            {
+                log.log( POILogger.WARN, "Paragraph refers to LFO #",
+                        properties.getIlfo(), " that does not exists" );
+            }
+            if ( lfo != null )
             {
-                properties = ParagraphSprmUncompressor.uncompressPAP(
-                        properties, listLevel.getGrpprlPapx(), 0 );
-                // reapply style and local PAPX properties
-                properties = newParagraph_applyStyleProperties( styleSheet,
-                        papx, properties );
-                properties = ParagraphSprmUncompressor.uncompressPAP(
-                        properties, papx.getGrpprl(), 2 );
+                final ListLevel listLevel = listTables.getLevel( lfo.getLsid(),
+                        properties.getIlvl() );
+
+                if ( listLevel != null && listLevel.getGrpprlPapx() != null )
+                {
+                    properties = ParagraphSprmUncompressor.uncompressPAP(
+                            properties, listLevel.getGrpprlPapx(), 0 );
+                    // reapply style and local PAPX properties
+                    properties = newParagraph_applyStyleProperties( styleSheet,
+                            papx, properties );
+                    properties = ParagraphSprmUncompressor.uncompressPAP(
+                            properties, papx.getGrpprl(), 2 );
+                }
             }
         }
 
@@ -576,6 +594,22 @@ public short getStyleIndex()
         return _props.getRgdxaTab();
     }
 
+    public HWPFList getList()
+    {
+        if ( getIlfo() == 0x000 || getIlfo() == 0xF801 )
+        {
+            throw new IllegalStateException( "Paragraph not in list" );
+        }
+        HWPFList hwpfList = new HWPFList( getDocument().getStyleSheet(),
+                getDocument().getListTables(), getIlfo() );
+        return hwpfList;
+    }
+
+    public boolean isInList()
+    {
+        return getIlfo() != 0x000 && getIlfo() != 0xF801;
+    }
+
   /**
    * clone the ParagraphProperties object associated with this Paragraph so
    * that you can apply the same properties to another paragraph.
index 291fef3c8a65d1367b3ac35e7ac5eb62cd93d42e..feb7a90c248d73308186d9d76c3c98dda9999376 100644 (file)
@@ -35,21 +35,21 @@ public final class TestListTables
     FileInformationBlock fib = _hWPFDocFixture._fib;
     byte[] tableStream = _hWPFDocFixture._tableStream;
 
-    int listOffset = fib.getFcPlcfLst();
+    int listOffset = fib.getFcPlfLst();
     int lfoOffset = fib.getFcPlfLfo();
-    if (listOffset != 0 && fib.getLcbPlcfLst() != 0)
+    if (listOffset != 0 && fib.getLcbPlfLst() != 0)
     {
-      ListTables listTables = new ListTables (tableStream, fib.getFcPlcfLst (),
-                                              fib.getFcPlfLfo ());
+      ListTables listTables = new ListTables (tableStream, fib.getFcPlfLst(),
+                                              fib.getFcPlfLfo (), fib.getLcbPlfLfo());
       HWPFFileSystem fileSys = new HWPFFileSystem ();
 
       HWPFOutputStream tableOut = fileSys.getStream ("1Table");
 
       listTables.writeListDataTo (fib, tableOut);
-      int offset = tableOut.getOffset ();
-      listTables.writeListOverridesTo (tableOut);
+      listTables.writeListOverridesTo( fib, tableOut);
 
-      ListTables newTables = new ListTables (tableOut.toByteArray (), 0, offset);
+      ListTables newTables = new ListTables (tableOut.toByteArray (), fib.getFcPlfLst(),
+              fib.getFcPlfLfo (), fib.getLcbPlfLfo());
 
       assertEquals(listTables, newTables);
 
index 24ac72fe402ed7909bef735ee9ac8c53b65985d1..a8302520c456ac607d375b1ff63fb3cc28f0157a 100644 (file)
 ==================================================================== */\r
 package org.apache.poi.hwpf.usermodel;\r
 \r
+import junit.framework.TestCase;\r
+\r
 import org.apache.poi.hwpf.HWPFDocument;\r
 import org.apache.poi.hwpf.HWPFTestDataSamples;\r
-import org.apache.poi.hwpf.model.ListFormatOverride;\r
+import org.apache.poi.hwpf.model.LFO;\r
 import org.apache.poi.hwpf.model.ListLevel;\r
 \r
-import junit.framework.TestCase;\r
-\r
 public class TestBug50075 extends TestCase\r
 {\r
 \r
@@ -31,7 +31,7 @@ public class TestBug50075 extends TestCase
     Range range = doc.getRange();\r
     assertEquals(1, range.numParagraphs());\r
     ListEntry entry = (ListEntry) range.getParagraph(0);\r
-    ListFormatOverride override = doc.getListTables().getOverride(entry.getIlfo());\r
+    LFO override = doc.getListTables().getLfo( entry.getIlfo());\r
     ListLevel level = doc.getListTables().getLevel(override.getLsid(), entry.getIlvl());\r
     \r
     // the bug reproduces, if this call fails with NullPointerException\r
index cc4d88df34e79c097bda7b0990dbf899728ee7dc..911f6ce6f19065a0995f6a2a67a1f6c0b02be3d7 100644 (file)
     <suffix>AbstractType</suffix>
     <extends>HDFType</extends>
     <description>List Format Override (LFO). &lt;p&gt;Class and fields descriptions are quoted from
-        Microsoft Office Word 97-2007 Binary File Format
+        [MS-DOC] --v20110315; Word (.doc) Binary File Format
     </description>
-    <author>Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format
-        Specification [*.doc]
+    <author>Sergey Vladimirov; according to [MS-DOC] --v20110315; Word (.doc) Binary File Format;
+        Copyright (c) Microsoft Corporation
     </author>
     <fields>
-        <field type="int" size="4" name="lsid" description="List ID of corresponding LSTF (see LSTF)"/>
-        <field type="int" size="4" name="reserved1" description="Reserved"/>
-        <field type="int" size="4" name="reserved2" description="Reserved"/>
+        <field type="int" size="4" name="lsid"
+            description="A signed integer that specifies the list identifier of an LSTF. This LFO corresponds to the LSTF in PlfLst.rgLstf that has an lsid whose value is equal to this value."/>
+        <field type="int" size="4" name="unused1" description="This field MUST be ignored"/>
+        <field type="int" size="4" name="unused2" description="This field MUST be ignored"/>
         <field type="byte" size="1" name="clfolvl"
-            description="Count of levels whose format is overridden (see LFOLVL)"/>
+            description="An unsigned integer that specifies the field that this LFO represents."/>
         <field type="byte" size="1" name="ibstFltAutoNum" description="Used for AUTONUM field emulation"/>
-        <field type="Grfhic" size="1" name="grfhic" description="HTML compatibility flags" />
-        <field type="byte" size="1" name="reserved3" description="Reserved"/>
+        <field type="Grfhic" size="1" name="grfhic" description="HTML compatibility flags"/>
+        <field type="byte" size="1" name="unused3" description="This field MUST be ignored"/>
     </fields>
 </record>