From: Sergey Vladimirov Date: Sun, 23 Sep 2012 12:56:09 +0000 (+0000) Subject: refactor list format override structures (was marked with @Internal annotation) X-Git-Tag: 3.10-beta1~141 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7e47c6ee35f2d54df59c9104eb7f8bb8c2c8be67;p=poi.git refactor list format override structures (was marked with @Internal annotation) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1389037 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/util/ArrayUtil.java b/src/java/org/apache/poi/util/ArrayUtil.java index 3461ee7152..c8d46e1341 100644 --- a/src/java/org/apache/poi/util/ArrayUtil.java +++ b/src/java/org/apache/poi/util/ArrayUtil.java @@ -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 newLength is negative + * @throws NullPointerException + * if original is null + */ + public static 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 (from) must lie between zero diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java index c7ddf97518..a27eec046a 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java @@ -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 { diff --git a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java index caed3ad41c..d008ec907c 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java @@ -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 ), + "* " ); + } } } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/LFOData.java b/src/scratchpad/src/org/apache/poi/hwpf/model/LFOData.java index 9910dcaef8..ff2e69c9c9 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/LFOData.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/LFOData.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/ListTables.java b/src/scratchpad/src/org/apache/poi/hwpf/model/ListTables.java index 009f42f7b4..eaea30733d 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/ListTables.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/ListTables.java @@ -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 _overrideList = new ArrayList(); + /** + * Both PlfLst and the following LVLs + */ + private final LinkedHashMap _listMap = new LinkedHashMap(); + 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 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 { - private ArrayList keyList = new ArrayList(); - private HashMap parent = new HashMap(); - 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 map) { - for(Entry 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> entrySet() { - throw new IllegalStateException("Use sortedKeys() + get() instead"); - } - - public List sortedKeys() { - return Collections.unmodifiableList(keyList); - } - public Set keySet() { - throw new IllegalStateException("Use sortedKeys() instead"); - } - - public Collection values() { - ArrayList values = new ArrayList(); - 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 index 0000000000..4fd8cc2c35 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java @@ -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. + *

+ * 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 ); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/types/PAPAbstractType.java b/src/scratchpad/src/org/apache/poi/hwpf/model/types/PAPAbstractType.java index 5415f18248..f9918fc4e3 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/types/PAPAbstractType.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/types/PAPAbstractType.java @@ -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 ) diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/ListEntry.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/ListEntry.java index 25b41da777..378c26c90e 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/ListEntry.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/ListEntry.java @@ -17,60 +17,27 @@ 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; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java index 6d99465abc..65f907691b 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java @@ -17,9 +17,10 @@ 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. diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestListTables.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestListTables.java index 291fef3c8a..feb7a90c24 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestListTables.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestListTables.java @@ -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); diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBug50075.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBug50075.java index 24ac72fe40..a8302520c4 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBug50075.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBug50075.java @@ -16,13 +16,13 @@ ==================================================================== */ package org.apache.poi.hwpf.usermodel; +import junit.framework.TestCase; + import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFTestDataSamples; -import org.apache.poi.hwpf.model.ListFormatOverride; +import org.apache.poi.hwpf.model.LFO; import org.apache.poi.hwpf.model.ListLevel; -import junit.framework.TestCase; - public class TestBug50075 extends TestCase { @@ -31,7 +31,7 @@ public class TestBug50075 extends TestCase Range range = doc.getRange(); assertEquals(1, range.numParagraphs()); ListEntry entry = (ListEntry) range.getParagraph(0); - ListFormatOverride override = doc.getListTables().getOverride(entry.getIlfo()); + LFO override = doc.getListTables().getLfo( entry.getIlfo()); ListLevel level = doc.getListTables().getLevel(override.getLsid(), entry.getIlvl()); // the bug reproduces, if this call fails with NullPointerException diff --git a/src/types/definitions/lfo_type.xml b/src/types/definitions/lfo_type.xml index cc4d88df34..911f6ce6f1 100644 --- a/src/types/definitions/lfo_type.xml +++ b/src/types/definitions/lfo_type.xml @@ -21,19 +21,20 @@ AbstractType HDFType List Format Override (LFO). <p>Class and fields descriptions are quoted from - Microsoft Office Word 97-2007 Binary File Format + [MS-DOC] --v20110315; Word (.doc) Binary File Format - Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format - Specification [*.doc] + Sergey Vladimirov; according to [MS-DOC] --v20110315; Word (.doc) Binary File Format; + Copyright (c) Microsoft Corporation - - - + + + + description="An unsigned integer that specifies the field that this LFO represents."/> - - + +