]> source.dussan.org Git - poi.git/commitdiff
added initial support for bookmark
authorSergey Vladimirov <sergey@apache.org>
Tue, 19 Jul 2011 16:18:27 +0000 (16:18 +0000)
committerSergey Vladimirov <sergey@apache.org>
Tue, 19 Jul 2011 16:18:27 +0000 (16:18 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1148428 13f79535-47bb-0310-9956-ffa450edef68

14 files changed:
src/documentation/content/xdocs/status.xml
src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
src/scratchpad/src/org/apache/poi/hwpf/model/BookmarkFirstDescriptor.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/FIBFieldHandler.java
src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java
src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/types/BKFAbstractType.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmark.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hwpf/AllHWPFTests.java
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java [new file with mode: 0644]
src/types/definitions/bkf_type.xml [new file with mode: 0644]
src/types/styles/hdftype.xsl

index 613c304dc458bf838453399e0dde2bd4628c339b..23fe366889db61051f9dea4734fdde1c3dba4e99 100644 (file)
@@ -34,6 +34,7 @@
 
     <changes>
         <release version="3.8-beta4" date="2011-??-??">
+           <action dev="poi-developers" type="fix">Added initial support for bookmarks in HWFP</action>
            <action dev="poi-developers" type="fix">46250 - Fixed cloning worksheets with images</action>
            <action dev="poi-developers" type="fix">51524 - PapBinTable constructor is slow (regression)</action>
            <action dev="poi-developers" type="fix">51514 - allow HSSFObjectData to work with both POIFS and NPOIFS</action>
index 7bcfd98328d2a0b734272f4c55f84c57ce1e6ee9..b8a2cd5f8b6e65a52968c1328512feefbea2b3cb 100644 (file)
@@ -26,6 +26,7 @@ import java.io.OutputStream;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.poi.hwpf.model.BookmarksTables;
 import org.apache.poi.hwpf.model.CHPBinTable;
 import org.apache.poi.hwpf.model.CPSplitCalculator;
 import org.apache.poi.hwpf.model.ComplexFileTable;
@@ -101,6 +102,9 @@ public final class HWPFDocument extends HWPFDocumentCore
   /** Holds Office Art objects */
   protected ShapesTable _officeArts;
   
+  /** Holds the bookmarks */
+  protected BookmarksTables _bookmarksTables;
+  
   /** Holds the fields PLCFs */
   protected FieldsTables _fieldsTables;
 
@@ -261,7 +265,8 @@ public final class HWPFDocument extends HWPFDocumentCore
     {
       _rmat = new RevisionMarkAuthorTable(_tableStream, rmarkOffset, rmarkLength);
     }
-    
+
+    _bookmarksTables = new BookmarksTables( _tableStream, _fib );
     _fieldsTables = new FieldsTables(_tableStream, _fib);
   }
 
@@ -438,6 +443,15 @@ public final class HWPFDocument extends HWPFDocumentCore
          return _officeArts;
   }
 
+    /**
+     * @return BookmarksTables object, that is able to extract bookmarks
+     *         descriptors from this document
+     */
+    public BookmarksTables getBookmarksTables()
+    {
+        return _bookmarksTables;
+    }
+
   /**
    * @return FieldsTables object, that is able to extract fields descriptors from this document
    */
@@ -487,6 +501,15 @@ public final class HWPFDocument extends HWPFDocumentCore
     // complex table.
     int fcMin = mainOffset;
 
+        /*
+         * clx (encoding of the sprm lists for a complex file and piece table
+         * for a any file) Written immediately after the end of the previously
+         * recorded structure. This is recorded in all Word documents
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 23 of 210
+         */
+    
     // write out the Complex table, includes text.
     _fib.setFcClx(tableOffset);
     _cft.writeTo(docSys);
@@ -494,12 +517,54 @@ public final class HWPFDocument extends HWPFDocumentCore
     tableOffset = tableStream.getOffset();
     int fcMac = mainStream.getOffset();
 
+        /*
+         * plcfBkmkf (table recording beginning CPs of bookmarks) Written
+         * immediately after the sttbfBkmk, if the document contains bookmarks.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 24 of 210
+         */
+        if ( _bookmarksTables != null )
+        {
+            _bookmarksTables.writePlcfBkmkf( _fib, tableStream );
+            tableOffset = tableStream.getOffset();
+        }
+
+        /*
+         * plcfBkmkl (table recording limit CPs of bookmarks) Written
+         * immediately after the plcfBkmkf, if the document contains bookmarks.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 24 of 210
+         */
+        if ( _bookmarksTables != null )
+        {
+            _bookmarksTables.writePlcfBkmkl( _fib, tableStream );
+            tableOffset = tableStream.getOffset();
+        }
+
+        /*
+         * plcfbteChpx (bin table for CHP FKPs) Written immediately after the
+         * previously recorded table. This is recorded in all Word documents.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 24 of 210
+         */
+
     // write out the CHPBinTable.
     _fib.setFcPlcfbteChpx(tableOffset);
     _cbt.writeTo(docSys, fcMin);
     _fib.setLcbPlcfbteChpx(tableStream.getOffset() - tableOffset);
     tableOffset = tableStream.getOffset();
 
+        /*
+         * plcfbtePapx (bin table for PAP FKPs) Written immediately after the
+         * plcfbteChpx. This is recorded in all Word documents.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 24 of 210
+         */
+
     // write out the PAPBinTable.
     _fib.setFcPlcfbtePapx(tableOffset);
     _pbt.writeTo(docSys, fcMin);
@@ -531,6 +596,27 @@ public final class HWPFDocument extends HWPFDocumentCore
       tableOffset = tableStream.getOffset();
     }
 
+        /*
+         * sttbfBkmk (table of bookmark name strings) Written immediately after
+         * the previously recorded table, if the document contains bookmarks.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 27 of 210
+         */
+        if ( _bookmarksTables != null )
+        {
+            _bookmarksTables.writeSttbfBkmk( _fib, tableStream );
+            tableOffset = tableStream.getOffset();
+        }
+
+        /*
+         * sttbSavedBy (last saved by string table) Written immediately after
+         * the previously recorded table.
+         * 
+         * Microsoft Office Word 97-2007 Binary File Format (.doc)
+         * Specification; Page 27 of 210
+         */
+
     // write out the saved-by table.
     if (_sbt != null)
     {
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarkFirstDescriptor.java b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarkFirstDescriptor.java
new file mode 100644 (file)
index 0000000..d6493f2
--- /dev/null
@@ -0,0 +1,70 @@
+package org.apache.poi.hwpf.model;
+
+import org.apache.poi.hwpf.model.types.BKFAbstractType;
+
+public final class BookmarkFirstDescriptor extends BKFAbstractType implements
+        Cloneable
+{
+    public BookmarkFirstDescriptor()
+    {
+    }
+
+    public BookmarkFirstDescriptor( byte[] data, int offset )
+    {
+        fillFields( data, offset );
+    }
+
+    @Override
+    protected BookmarkFirstDescriptor clone()
+    {
+        try
+        {
+            return (BookmarkFirstDescriptor) super.clone();
+        }
+        catch ( CloneNotSupportedException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        BookmarkFirstDescriptor other = (BookmarkFirstDescriptor) obj;
+        if ( field_1_ibkl != other.field_1_ibkl )
+            return false;
+        if ( field_2_bkf_flags != other.field_2_bkf_flags )
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + field_1_ibkl;
+        result = prime * result + field_2_bkf_flags;
+        return result;
+    }
+
+    public boolean isEmpty()
+    {
+        return field_1_ibkl == 0 && field_2_bkf_flags == 0;
+    }
+
+    @Override
+    public String toString()
+    {
+        if ( isEmpty() )
+            return "[BKF] EMPTY";
+
+        return super.toString();
+    }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java
new file mode 100644 (file)
index 0000000..2dd19d4
--- /dev/null
@@ -0,0 +1,153 @@
+package org.apache.poi.hwpf.model;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.poi.hwpf.model.io.HWPFOutputStream;
+import org.apache.poi.hwpf.usermodel.Bookmark;
+
+public class BookmarksTables
+{
+    private PlexOfCps descriptorsFirst = new PlexOfCps( 4 );
+
+    private PlexOfCps descriptorsLim = new PlexOfCps( 0 );
+
+    private String[] names = new String[0];
+
+    public BookmarksTables()
+    {
+    }
+
+    public BookmarksTables( byte[] tableStream, FileInformationBlock fib )
+    {
+        read( tableStream, fib );
+    }
+
+    public Bookmark getBookmark( int index )
+    {
+        final GenericPropertyNode first = descriptorsFirst.getProperty( index );
+        return new Bookmark()
+        {
+            public int getEnd()
+            {
+                int currentIndex = Arrays.asList(
+                        descriptorsFirst.toPropertiesArray() ).indexOf( first );
+                if ( currentIndex >= descriptorsLim.length() )
+                    return first.getEnd();
+
+                GenericPropertyNode lim = descriptorsLim
+                        .getProperty( currentIndex );
+                return lim.getStart();
+            }
+
+            public String getName()
+            {
+                int currentIndex = Arrays.asList(
+                        descriptorsFirst.toPropertiesArray() ).indexOf( first );
+                if ( currentIndex >= names.length )
+                    return "";
+
+                return names[currentIndex];
+            }
+
+            public int getStart()
+            {
+                return first.getStart();
+            }
+
+            public void setName( String name )
+            {
+                int currentIndex = Arrays.asList(
+                        descriptorsFirst.toPropertiesArray() ).indexOf( first );
+                if ( currentIndex < names.length )
+                {
+                    String[] newNames = new String[currentIndex + 1];
+                    System.arraycopy( names, 0, newNames, 0, names.length );
+                    names = newNames;
+                }
+                names[currentIndex] = name;
+            }
+        };
+    }
+
+    public int getBookmarksCount()
+    {
+        return descriptorsFirst.length();
+    }
+
+    private void read( byte[] tableStream, FileInformationBlock fib )
+    {
+        int namesStart = fib.getFcSttbfbkmk();
+        int namesLength = fib.getLcbSttbfbkmk();
+
+        if ( namesStart != 0 && namesLength != 0 )
+            this.names = SttbfUtils.read( tableStream, namesStart );
+
+        int firstDescriptorsStart = fib.getFcPlcfbkf();
+        int firstDescriptorsLength = fib.getLcbPlcfbkf();
+        if ( firstDescriptorsStart != 0 && firstDescriptorsLength != 0 )
+            descriptorsFirst = new PlexOfCps( tableStream,
+                    firstDescriptorsStart, firstDescriptorsLength,
+                    BookmarkFirstDescriptor.getSize() );
+
+        int limDescriptorsStart = fib.getFcPlcfbkl();
+        int limDescriptorsLength = fib.getLcbPlcfbkl();
+        if ( limDescriptorsStart != 0 && limDescriptorsLength != 0 )
+            descriptorsLim = new PlexOfCps( tableStream, limDescriptorsStart,
+                    limDescriptorsLength, 0 );
+    }
+
+    public void writePlcfBkmkf( FileInformationBlock fib,
+            HWPFOutputStream tableStream ) throws IOException
+    {
+        if ( descriptorsFirst == null || descriptorsFirst.length() == 0 )
+        {
+            fib.setFcPlcfbkf( 0 );
+            fib.setLcbPlcfbkf( 0 );
+            return;
+        }
+
+        int start = tableStream.getOffset();
+        tableStream.write( descriptorsFirst.toByteArray() );
+        int end = tableStream.getOffset();
+
+        fib.setFcPlcfbkf( start );
+        fib.setLcbPlcfbkf( end - start );
+    }
+
+    public void writePlcfBkmkl( FileInformationBlock fib,
+            HWPFOutputStream tableStream ) throws IOException
+    {
+        if ( descriptorsLim == null || descriptorsLim.length() == 0 )
+        {
+            fib.setFcPlcfbkl( 0 );
+            fib.setLcbPlcfbkl( 0 );
+            return;
+        }
+
+        int start = tableStream.getOffset();
+        tableStream.write( descriptorsLim.toByteArray() );
+        int end = tableStream.getOffset();
+
+        fib.setFcPlcfbkl( start );
+        fib.setLcbPlcfbkl( end - start );
+    }
+
+    public void writeSttbfBkmk( FileInformationBlock fib,
+            HWPFOutputStream tableStream ) throws IOException
+    {
+        if ( names == null || names.length == 0 )
+        {
+            fib.setFcSttbfbkmk( 0 );
+            fib.setLcbSttbfbkmk( 0 );
+            return;
+        }
+
+        int start = tableStream.getOffset();
+        SttbfUtils.write( tableStream, names );
+        int end = tableStream.getOffset();
+
+        fib.setFcSttbfbkmk( start );
+        fib.setLcbSttbfbkmk( end - start );
+    }
+}
index 8ec2f79fca716020af81cd2aaf6f8f0a35c3b296..d5613a6948db70f0a44a95fda5579eac0c92df27 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.poi.util.POILogger;
 
 public final class FIBFieldHandler
 {
+    // 154 == 0x009A; 158 == 0x009E
   public static final int STSHFORIG = 0;
   public static final int STSHF = 1;
   public static final int PLCFFNDREF = 2;
@@ -48,10 +49,13 @@ public final class FIBFieldHandler
   public static final int STTBFFFN = 15;
   public static final int PLCFFLDMOM = 16;
   public static final int PLCFFLDHDR = 17;
+  // 298 == 0x12A; 302 == 0x12E
   public static final int PLCFFLDFTN = 18;
+  // 306 == 0x132; 310 == 0x0136
   public static final int PLCFFLDATN = 19;
   public static final int PLCFFLDMCR = 20;
   public static final int STTBFBKMK = 21;
+  // 330 == 0x014A; 334 == 0x014E
   public static final int PLCFBKF = 22;
   public static final int PLCFBKL = 23;
   public static final int CMDS = 24;
@@ -70,24 +74,29 @@ public final class FIBFieldHandler
   public static final int STTBFATNBKMK = 37;
   public static final int PLCFDOAMOM = 38;
   public static final int PLCDOAHDR = 39;
-  public static final int PLCSPAMOM = 40;
+    // 474 == 0x01DA; 478 == 0x01DE
+    public static final int PLCSPAMOM = 40;
   public static final int PLCSPAHDR = 41;
-  public static final int PLCFATNBKF = 42;
-  public static final int PLCFATNBKL = 43;
-  public static final int PMS = 44;
+    public static final int PLCFATNBKF = 42;
+    // 498 == 0x01F2; 502 == 0x01F6
+    public static final int PLCFATNBKL = 43;
+    // 506 == 0x01FA; 510 == 0x01FE
+    public static final int PMS = 44;
   public static final int FORMFLDSTTBS = 45;
   public static final int PLCFENDREF = 46;
   public static final int PLCFENDTXT = 47;
   public static final int PLCFFLDEDN = 48;
   public static final int PLCFPGDEDN = 49;
-  public static final int DGGINFO = 50;
+    // 554 == 0x022A; 558 == 0x022E -- long
+    public static final int DGGINFO = 50;
   public static final int STTBFRMARK = 51;
   public static final int STTBCAPTION = 52;
   public static final int STTBAUTOCAPTION = 53;
   public static final int PLCFWKB = 54;
   public static final int PLCFSPL = 55;
   public static final int PLCFTXBXTXT = 56;
-  public static final int PLCFFLDTXBX = 57;//validated
+    // 610 -- 0x0262; 614 == 0x0266
+    public static final int PLCFFLDTXBX = 57;// validated
   public static final int PLCFHDRTXBXTXT = 58;
   public static final int PLCFFLDHDRTXBX = 59;
   public static final int STWUSER = 60;
@@ -132,11 +141,11 @@ public final class FIBFieldHandler
   private int[] _fields;
 
 
-  public FIBFieldHandler(byte[] mainStream, int offset, byte[] tableStream,
+  public FIBFieldHandler(byte[] mainStream, int startOffset, byte[] tableStream,
                          HashSet<Integer> offsetList, boolean areKnown)
   {
-    int numFields = LittleEndian.getShort(mainStream, offset);
-    offset += LittleEndian.SHORT_SIZE;
+    int numFields = LittleEndian.getShort(mainStream, startOffset);
+    int offset = startOffset + LittleEndian.SHORT_SIZE;
     _fields = new int[numFields * 2];
 
     for (int x = 0; x < numFields; x++)
index d5d9c5046d71e0df83168c54db50c360225a34be..a15847d6efbdc70b4bc45187b4b2bbdd19a4c572 100644 (file)
@@ -55,35 +55,45 @@ public final class FileInformationBlock extends FIBAbstractType
         fillFields(mainDocument, 0);
     }
 
-    public void fillVariableFields(byte[] mainDocument, byte[] tableStream)
-    {
-      HashSet<Integer> fieldSet = new HashSet<Integer>();
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.STSHF));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.CLX));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.DOP));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFBTECHPX));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFBTEPAPX));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFSED));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFLST));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLFLFO));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDATN));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDEDN));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDFTN));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDHDR));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDHDRTXBX));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDMOM));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.PLCFFLDTXBX));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.STTBFFFN));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.STTBFRMARK));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.STTBSAVEDBY));
-      fieldSet.add(Integer.valueOf(FIBFieldHandler.MODIFIED));
-
-
-      _shortHandler = new FIBShortHandler(mainDocument);
-      _longHandler = new FIBLongHandler(mainDocument, FIBShortHandler.START + _shortHandler.sizeInBytes());
-      _fieldHandler = new FIBFieldHandler(mainDocument,
-                                          FIBShortHandler.START + _shortHandler.sizeInBytes() + _longHandler.sizeInBytes(),
-                                          tableStream, fieldSet, true);
+    public void fillVariableFields( byte[] mainDocument, byte[] tableStream )
+    {
+        _shortHandler = new FIBShortHandler( mainDocument );
+        _longHandler = new FIBLongHandler( mainDocument, FIBShortHandler.START
+                + _shortHandler.sizeInBytes() );
+
+        /*
+         * Listed fields won't be treat as UnhandledDataStructure. For all other
+         * fields FIBFieldHandler will load it content into
+         * UnhandledDataStructure and save them on save.
+         */
+        HashSet<Integer> knownFieldSet = new HashSet<Integer>();
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.STSHF ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.CLX ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.DOP ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFBTECHPX ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFBTEPAPX ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFSED ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFLST ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLFLFO ) );
+
+        // field info
+        for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
+            knownFieldSet.add( Integer.valueOf( part.getFibFieldsField() ) );
+
+        // bookmarks
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFBKF ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.PLCFBKL ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.STTBFBKMK ) );
+
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.STTBFFFN ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.STTBFRMARK ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.STTBSAVEDBY ) );
+        knownFieldSet.add( Integer.valueOf( FIBFieldHandler.MODIFIED ) );
+
+        _fieldHandler = new FIBFieldHandler( mainDocument,
+                FIBShortHandler.START + _shortHandler.sizeInBytes()
+                        + _longHandler.sizeInBytes(), tableStream,
+                knownFieldSet, true );
     }
 
     @Override
@@ -286,6 +296,89 @@ public final class FileInformationBlock extends FIBAbstractType
       return _fieldHandler.getFieldSize(FIBFieldHandler.PLFLFO);
     }
 
+    /**
+     * @return Offset in table stream of the STTBF that records bookmark names
+     *         in the main document
+     */
+    public int getFcSttbfbkmk()
+    {
+        return _fieldHandler.getFieldOffset( FIBFieldHandler.STTBFBKMK );
+    }
+
+    public void setFcSttbfbkmk( int offset )
+    {
+        _fieldHandler.setFieldOffset( FIBFieldHandler.STTBFBKMK, offset );
+    }
+
+    /**
+     * @return Count of bytes in Sttbfbkmk
+     */
+    public int getLcbSttbfbkmk()
+    {
+        return _fieldHandler.getFieldSize( FIBFieldHandler.STTBFBKMK );
+    }
+
+    public void setLcbSttbfbkmk( int length )
+    {
+        _fieldHandler.setFieldSize( FIBFieldHandler.STTBFBKMK, length );
+    }
+
+    /**
+     * @return Offset in table stream of the PLCF that records the beginning CP
+     *         offsets of bookmarks in the main document. See BKF structure
+     *         definition.
+     */
+    public int getFcPlcfbkf()
+    {
+        return _fieldHandler.getFieldOffset( FIBFieldHandler.PLCFBKF );
+    }
+
+    public void setFcPlcfbkf( int offset )
+    {
+        _fieldHandler.setFieldOffset( FIBFieldHandler.PLCFBKF, offset );
+    }
+
+    /**
+     * @return Count of bytes in Plcfbkf
+     */
+    public int getLcbPlcfbkf()
+    {
+        return _fieldHandler.getFieldSize( FIBFieldHandler.PLCFBKF );
+    }
+
+    public void setLcbPlcfbkf( int length )
+    {
+        _fieldHandler.setFieldSize( FIBFieldHandler.PLCFBKF, length );
+    }
+
+    /**
+     * @return Offset in table stream of the PLCF that records the ending CP
+     *         offsets of bookmarks recorded in the main document. No structure
+     *         is stored in this PLCF.
+     */
+    public int getFcPlcfbkl()
+    {
+        return _fieldHandler.getFieldOffset( FIBFieldHandler.PLCFBKL );
+    }
+
+    public void setFcPlcfbkl( int offset )
+    {
+        _fieldHandler.setFieldOffset( FIBFieldHandler.PLCFBKL, offset );
+    }
+
+    /**
+     * @return Count of bytes in Plcfbkl
+     */
+    public int getLcbPlcfbkl()
+    {
+        return _fieldHandler.getFieldSize( FIBFieldHandler.PLCFBKL );
+    }
+
+    public void setLcbPlcfbkl( int length )
+    {
+        _fieldHandler.setFieldSize( FIBFieldHandler.PLCFBKL, length );
+    }
+
     public void setFcPlfLfo(int fcPlfLfo)
     {
       _fieldHandler.setFieldOffset(FIBFieldHandler.PLFLFO, fcPlfLfo);
@@ -782,5 +875,6 @@ public final class FileInformationBlock extends FIBAbstractType
 //        return null;
 //      }
 //    }
+
 }
 
index 91f8c5fb3c988dc8da61eefc3f5803210f4678ad..88d3256ab2119327aa391172138ffaa1c2912a67 100644 (file)
@@ -23,8 +23,6 @@ import java.util.Collections;
 import java.util.List;
 
 import org.apache.poi.hwpf.model.io.HWPFOutputStream;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
 
 /**
  * String table containing the history of the last few revisions ("saves") of the document.
@@ -34,10 +32,6 @@ import org.apache.poi.util.StringUtil;
  */
 public final class SavedByTable
 {
-  /**
-   * A value that I don't know what it does, but is maintained for accuracy.
-   */
-  private short unknownValue = -1;
 
   /**
    * Array of entries.
@@ -52,30 +46,40 @@ public final class SavedByTable
    * @param size the size of the table in the byte array.
    */
   public SavedByTable(byte[] tableStream, int offset, int size)
-  {
-    // Read the value that I don't know what it does. :-)
-    unknownValue = LittleEndian.getShort(tableStream, offset);
-    offset += 2;
-
-    // The stored int is the number of strings, and there are two strings per entry.
-    int numEntries = LittleEndian.getInt(tableStream, offset) / 2;
-    offset += 4;
-
-    entries = new SavedByEntry[numEntries];
-    for (int i = 0; i < numEntries; i++)
-    {
-      int len = LittleEndian.getShort(tableStream, offset);
-      offset += 2;
-      String userName = StringUtil.getFromUnicodeLE(tableStream, offset, len);
-      offset += len * 2;
-      len = LittleEndian.getShort(tableStream, offset);
-      offset += 2;
-      String saveLocation = StringUtil.getFromUnicodeLE(tableStream, offset, len);
-      offset += len * 2;
-
-      entries[i] = new SavedByEntry(userName, saveLocation);
+  {      
+//    // Read the value that I don't know what it does. :-)
+//    unknownValue = LittleEndian.getShort(tableStream, offset);
+//    offset += 2;
+//
+//    // The stored int is the number of strings, and there are two strings per entry.
+//    int numEntries = LittleEndian.getInt(tableStream, offset) / 2;
+//    offset += 4;
+//
+//    entries = new SavedByEntry[numEntries];
+//    for (int i = 0; i < numEntries; i++)
+//    {
+//      int len = LittleEndian.getShort(tableStream, offset);
+//      offset += 2;
+//      String userName = StringUtil.getFromUnicodeLE(tableStream, offset, len);
+//      offset += len * 2;
+//      len = LittleEndian.getShort(tableStream, offset);
+//      offset += 2;
+//      String saveLocation = StringUtil.getFromUnicodeLE(tableStream, offset, len);
+//      offset += len * 2;
+//
+//      entries[i] = new SavedByEntry(userName, saveLocation);
+//    }
+
+        // first value is mark for extended STTBF ;) -- sergey
+        String[] strings = SttbfUtils.read( tableStream, offset );
+
+        int numEntries = strings.length / 2;
+        entries = new SavedByEntry[numEntries];
+        for ( int i = 0; i < numEntries; i++ )
+        {
+            entries[i] = new SavedByEntry( strings[i * 2], strings[i * 2 + 1] );
+        }
     }
-  }
 
   /**
    * Gets the entries.  The returned list cannot be modified.
@@ -87,34 +91,24 @@ public final class SavedByTable
     return Collections.unmodifiableList(Arrays.asList(entries));
   }
 
-  /**
-   * Writes this table to the table stream.
-   *
-   * @param tableStream the table stream to write to.
-   * @throws IOException if an error occurs while writing.
-   */
-  public void writeTo(HWPFOutputStream tableStream)
-    throws IOException
-  {
-    byte[] header = new byte[6];
-    LittleEndian.putShort(header, 0, unknownValue);
-    LittleEndian.putInt(header, 2, entries.length * 2);
-    tableStream.write(header);
-
-    for (int i = 0; i < entries.length; i++)
+    /**
+     * Writes this table to the table stream.
+     * 
+     * @param tableStream
+     *            the table stream to write to.
+     * @throws IOException
+     *             if an error occurs while writing.
+     */
+    public void writeTo( HWPFOutputStream tableStream ) throws IOException
     {
-      writeStringValue(tableStream, entries[i].getUserName());
-      writeStringValue(tableStream, entries[i].getSaveLocation());
+        String[] toSave = new String[entries.length * 2];
+        int counter = 0;
+        for ( SavedByEntry entry : entries )
+        {
+            toSave[counter++] = entry.getUserName();
+            toSave[counter++] = entry.getSaveLocation();
+        }
+        SttbfUtils.write( tableStream, toSave );
     }
-  }
 
-  private void writeStringValue(HWPFOutputStream tableStream, String value)
-    throws IOException
-  {
-    byte[] buf = new byte[value.length() * 2 + 2];
-    LittleEndian.putShort(buf, 0, (short) value.length());
-    StringUtil.putUnicodeLE(value, buf, 2);
-    tableStream.write(buf);
-  }
 }
-
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java
new file mode 100644 (file)
index 0000000..de19edb
--- /dev/null
@@ -0,0 +1,74 @@
+package org.apache.poi.hwpf.model;
+
+import java.io.IOException;
+
+import org.apache.poi.hwpf.model.io.HWPFOutputStream;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * Utils for storing and reading "STring TaBle stored in File"
+ * 
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+class SttbfUtils
+{
+    public static String[] read( byte[] data, int startOffset )
+    {
+        short ffff = LittleEndian.getShort( data, startOffset );
+
+        if ( ffff != (short) 0xffff )
+        {
+            // Non-extended character Pascal strings
+            throw new UnsupportedOperationException(
+                    "Non-extended character Pascal strings are not supported right now. "
+                            + "Please, contact POI developers for update." );
+        }
+
+        // strings are extended character strings
+        int offset = startOffset + 2;
+        int numEntries = LittleEndian.getInt( data, offset );
+        offset += 4;
+
+        String[] entries = new String[numEntries];
+        for ( int i = 0; i < numEntries; i++ )
+        {
+            int len = LittleEndian.getShort( data, offset );
+            offset += 2;
+            String value = StringUtil.getFromUnicodeLE( data, offset, len );
+            offset += len * 2;
+            entries[i] = value;
+        }
+        return entries;
+    }
+
+    public static int write( HWPFOutputStream tableStream, String[] entries )
+            throws IOException
+    {
+        byte[] header = new byte[6];
+        LittleEndian.putShort( header, 0, (short) 0xffff );
+
+        if ( entries == null || entries.length == 0 )
+        {
+            LittleEndian.putInt( header, 2, 0 );
+            tableStream.write( header );
+            return 6;
+        }
+
+        LittleEndian.putInt( header, 2, entries.length );
+        tableStream.write( header );
+        int size = 6;
+
+        for ( String entry : entries )
+        {
+            byte[] buf = new byte[entry.length() * 2 + 2];
+            LittleEndian.putShort( buf, 0, (short) entry.length() );
+            StringUtil.putUnicodeLE( entry, buf, 2 );
+            tableStream.write( buf );
+            size += buf.length;
+        }
+
+        return size;
+    }
+
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/types/BKFAbstractType.java b/src/scratchpad/src/org/apache/poi/hwpf/model/types/BKFAbstractType.java
new file mode 100644 (file)
index 0000000..684797f
--- /dev/null
@@ -0,0 +1,174 @@
+\r
+package org.apache.poi.hwpf.model.types;\r
+\r
+import org.apache.poi.util.BitField;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * BooKmark First descriptor (BKF).\r
+ * <p>\r
+ * Class and fields descriptions are quoted from Microsoft Office Word 97-2007\r
+ * Binary File Format (.doc) Specification\r
+ * \r
+ * NOTE: This source is automatically generated please do not modify this file.\r
+ * Either subclass or remove the record in src/types/definitions.\r
+ * \r
+ * @author Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary\r
+ *         File Format (.doc) Specification\r
+ */\r
+public abstract class BKFAbstractType\r
+{\r
+\r
+    protected short field_1_ibkl;\r
+    protected short field_2_bkf_flags;\r
+    /**/private static BitField itcFirst = new BitField( 0x007F );\r
+    /**/private static BitField fPub = new BitField( 0x0080 );\r
+    /**/private static BitField itcLim = new BitField( 0x7F00 );\r
+    /**/private static BitField fCol = new BitField( 0x8000 );\r
+\r
+    protected BKFAbstractType()\r
+    {\r
+    }\r
+\r
+    protected void fillFields( byte[] data, int offset )\r
+    {\r
+        field_1_ibkl                   = LittleEndian.getShort(data, 0x0 + offset);\r
+        field_2_bkf_flags              = LittleEndian.getShort(data, 0x2 + offset);\r
+    }\r
+\r
+    public void serialize( byte[] data, int offset )\r
+    {\r
+        LittleEndian.putShort(data, 0x0 + offset, field_1_ibkl);\r
+        LittleEndian.putShort(data, 0x2 + offset, field_2_bkf_flags);\r
+    }\r
+\r
+    /**\r
+     * Size of record\r
+     */\r
+    public static int getSize()\r
+    {\r
+        return 0 + 2 + 2;\r
+    }\r
+\r
+    public String toString()\r
+    {\r
+        StringBuilder builder = new StringBuilder();\r
+        builder.append("[BKF]\n");\r
+        builder.append("    .ibkl                 = ");\r
+        builder.append(" (").append(getIbkl()).append(" )\n");\r
+        builder.append("    .bkf_flags            = ");\r
+        builder.append(" (").append(getBkf_flags()).append(" )\n");\r
+        builder.append("         .itcFirst                 = ").append(getItcFirst()).append('\n');\r
+        builder.append("         .fPub                     = ").append(isFPub()).append('\n');\r
+        builder.append("         .itcLim                   = ").append(getItcLim()).append('\n');\r
+        builder.append("         .fCol                     = ").append(isFCol()).append('\n');\r
+\r
+        builder.append("[/BKF]\n");\r
+        return builder.toString();\r
+    }\r
+\r
+    /**\r
+     * Index to BKL entry in plcfbkl that describes the ending position of this bookmark in the CP stream.\r
+     */\r
+    public short getIbkl()\r
+    {\r
+        return field_1_ibkl;\r
+    }\r
+\r
+    /**\r
+     * Index to BKL entry in plcfbkl that describes the ending position of this bookmark in the CP stream.\r
+     */\r
+    public void setIbkl( short field_1_ibkl )\r
+    {\r
+        this.field_1_ibkl = field_1_ibkl;\r
+    }\r
+\r
+    /**\r
+     * Get the bkf_flags field for the BKF record.\r
+     */\r
+    public short getBkf_flags()\r
+    {\r
+        return field_2_bkf_flags;\r
+    }\r
+\r
+    /**\r
+     * Set the bkf_flags field for the BKF record.\r
+     */\r
+    public void setBkf_flags( short field_2_bkf_flags )\r
+    {\r
+        this.field_2_bkf_flags = field_2_bkf_flags;\r
+    }\r
+\r
+    /**\r
+     * Sets the itcFirst field value.\r
+     * When bkf.fCol==1, this is the index to the first column of a table column bookmark\r
+     */\r
+    public void setItcFirst( byte value )\r
+    {\r
+        field_2_bkf_flags = (short)itcFirst.setValue(field_2_bkf_flags, value);\r
+    }\r
+\r
+    /**\r
+     * When bkf.fCol==1, this is the index to the first column of a table column bookmark\r
+     * @return  the itcFirst field value.\r
+     */\r
+    public byte getItcFirst()\r
+    {\r
+        return ( byte )itcFirst.getValue(field_2_bkf_flags);\r
+    }\r
+\r
+    /**\r
+     * Sets the fPub field value.\r
+     * When 1, this indicates that this bookmark is marking the range of a Macintosh Publisher section\r
+     */\r
+    public void setFPub( boolean value )\r
+    {\r
+        field_2_bkf_flags = (short)fPub.setBoolean(field_2_bkf_flags, value);\r
+    }\r
+\r
+    /**\r
+     * When 1, this indicates that this bookmark is marking the range of a Macintosh Publisher section\r
+     * @return  the fPub field value.\r
+     */\r
+    public boolean isFPub()\r
+    {\r
+        return fPub.isSet(field_2_bkf_flags);\r
+    }\r
+\r
+    /**\r
+     * Sets the itcLim field value.\r
+     * When bkf.fCol==1, this is the index to limit column of a table column bookmark\r
+     */\r
+    public void setItcLim( byte value )\r
+    {\r
+        field_2_bkf_flags = (short)itcLim.setValue(field_2_bkf_flags, value);\r
+    }\r
+\r
+    /**\r
+     * When bkf.fCol==1, this is the index to limit column of a table column bookmark\r
+     * @return  the itcLim field value.\r
+     */\r
+    public byte getItcLim()\r
+    {\r
+        return ( byte )itcLim.getValue(field_2_bkf_flags);\r
+    }\r
+\r
+    /**\r
+     * Sets the fCol field value.\r
+     * When 1, this bookmark marks a range of columns in a table specified by (bkf.itcFirst, bkf.itcLim)\r
+     */\r
+    public void setFCol( boolean value )\r
+    {\r
+        field_2_bkf_flags = (short)fCol.setBoolean(field_2_bkf_flags, value);\r
+    }\r
+\r
+    /**\r
+     * When 1, this bookmark marks a range of columns in a table specified by (bkf.itcFirst, bkf.itcLim)\r
+     * @return  the fCol field value.\r
+     */\r
+    public boolean isFCol()\r
+    {\r
+        return fCol.isSet(field_2_bkf_flags);\r
+    }\r
+\r
+}  // END OF CLASS\r
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmark.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Bookmark.java
new file mode 100644 (file)
index 0000000..9dfd6b6
--- /dev/null
@@ -0,0 +1,12 @@
+package org.apache.poi.hwpf.usermodel;
+
+public interface Bookmark
+{
+    public int getEnd();
+
+    public String getName();
+
+    public int getStart();
+
+    public void setName( String name );
+}
index b75ba423c8b7a62b660325abb80358b2eacd6775..0a72d7603136fcc0d4136d3789c4324191d18406 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.poi.hwpf.converter.TestWordToHtmlConverter;
 import org.apache.poi.hwpf.extractor.TestDifferentRoutes;
 import org.apache.poi.hwpf.extractor.TestWordExtractor;
 import org.apache.poi.hwpf.extractor.TestWordExtractorBugs;
+import org.apache.poi.hwpf.model.TestBookmarksTables;
 import org.apache.poi.hwpf.model.TestCHPBinTable;
 import org.apache.poi.hwpf.model.TestDocumentProperties;
 import org.apache.poi.hwpf.model.TestFileInformationBlock;
@@ -79,6 +80,7 @@ public final class AllHWPFTests
         suite.addTestSuite( TestWordExtractorBugs.class );
 
         // org.apache.poi.hwpf.model
+        suite.addTestSuite( TestBookmarksTables.class );
         suite.addTestSuite( TestCHPBinTable.class );
         suite.addTestSuite( TestDocumentProperties.class );
         suite.addTestSuite( TestFileInformationBlock.class );
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestBookmarksTables.java
new file mode 100644 (file)
index 0000000..7b0b9b0
--- /dev/null
@@ -0,0 +1,23 @@
+package org.apache.poi.hwpf.model;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.hwpf.HWPFTestDataSamples;
+import org.apache.poi.hwpf.usermodel.Bookmark;
+
+public class TestBookmarksTables extends TestCase
+{
+    public void test()
+    {
+        HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "pageref.doc" );
+        BookmarksTables bookmarksTables = doc.getBookmarksTables();
+
+        assertEquals( 1, bookmarksTables.getBookmarksCount() );
+
+        Bookmark bookmark = bookmarksTables.getBookmark( 0 );
+        assertEquals( "userref", bookmark.getName() );
+        assertEquals( 27, bookmark.getStart() );
+        assertEquals( 38, bookmark.getEnd() );
+    }
+}
diff --git a/src/types/definitions/bkf_type.xml b/src/types/definitions/bkf_type.xml
new file mode 100644 (file)
index 0000000..32a4b1c
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+    ====================================================================
+    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.
+    ====================================================================
+-->
+<record fromfile="true" name="BKF" package="org.apache.poi.hwpf.model.types">
+    <suffix>AbstractType</suffix>
+    <extends>HDFType</extends>
+    <description>BooKmark First descriptor (BKF). &lt;p&gt;Class and fields descriptions are
+        quoted
+        from Microsoft Office Word 97-2007 Binary File Format (.doc) Specification
+    </description>
+    <author>Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format (.doc)
+        Specification
+    </author>
+    <fields>
+        <field type="short" size="2" name="ibkl"
+            description="Index to BKL entry in plcfbkl that describes the ending position of this bookmark in the CP stream"/>
+        <field type="short" size="2" name="bkf_flags">
+            <bit number="0" mask="0x007F" name="itcFirst"
+                description="When bkf.fCol==1, this is the index to the first column of a table column bookmark"/>
+            <bit number="1" mask="0x0080" name="fPub"
+                description="When 1, this indicates that this bookmark is marking the range of a Macintosh Publisher section"/>
+            <bit number="2" mask="0x7F00" name="itcLim"
+                description="When bkf.fCol==1, this is the index to limit column of a table column bookmark"/>
+            <bit number="3" mask="0x8000" name="fCol"
+                description="When 1, this bookmark marks a range of columns in a table specified by (bkf.itcFirst, bkf.itcLim)"/>
+        </field>
+    </fields>
+</record>
index 5a235e6234a1aca007b4c714284c07ba37a650a9..fa21922840f844ee0fce5bf80a7e61a3bcbb20ff 100644 (file)
@@ -150,17 +150,25 @@ public abstract class </xsl:text><xsl:value-of select="@name"/><xsl:text>Abstrac
     <xsl:call-template name="indent"/>
     <xsl:text>}</xsl:text>
     <xsl:call-template name="linebreak"/>
+    <xsl:text>
     /**
-     * Size of record (exluding 4 byte header)
+     * Size of record
      */
     public static int getSize()
     {
-<xsl:variable name="fieldIterator" select="field:new()"/>
-<xsl:text>        return 4 + </xsl:text>
-<xsl:for-each select="//fields/field">
-    <xsl:value-of select="field:calcSize($fieldIterator,position(),@name,@size,@type)"/>
-</xsl:for-each>;
-    }
+</xsl:text>
+    <xsl:call-template name="indent"/>
+    <xsl:call-template name="indent"/>
+    <xsl:text>return 0</xsl:text>
+    <xsl:variable name="fieldIterator" select="field:new()"/>
+    <xsl:for-each select="//fields/field">
+        <xsl:value-of select="field:calcSize($fieldIterator,position(),@name,@size,@type)"/>
+    </xsl:for-each>
+    <xsl:text>;</xsl:text>
+    <xsl:call-template name="linebreak"/>
+    <xsl:call-template name="indent"/>
+    <xsl:text>}</xsl:text>
+    <xsl:call-template name="linebreak"/>
 </xsl:if>
 
     <xsl:call-template name="linebreak"/>
@@ -203,8 +211,6 @@ public abstract class </xsl:text><xsl:value-of select="@name"/><xsl:text>Abstrac
     public void set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( <xsl:value-of select="recutil:getBitFieldType(@name, @mask, ../@type)"/> value )
     {
         <xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/> = <xsl:value-of select="recutil:getBitFieldSet(@name, @mask, ../@type, recutil:getFieldName($fieldNum,../@name,0))"/>;
-
-        <!--<xsl:value-of select="recutil:getFieldName(@name,0)"/>.setValue(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>, value);-->
     }
 
     /**
@@ -214,13 +220,19 @@ public abstract class </xsl:text><xsl:value-of select="@name"/><xsl:text>Abstrac
     public <xsl:value-of select="recutil:getBitFieldFunction(@name,@mask,../@type, 'true')"/>()
     {
         return <xsl:value-of select="recutil:getBitFieldGet(@name, @mask,../@type, recutil:getFieldName($fieldNum,../@name,0))"/>
-        <!--return <xsl:value-of select="recutil:getFieldName(@name,0)"/>.isSet(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>);-->
     }
 </xsl:for-each>
 </xsl:template>
 
-<xsl:template match = "bit" >        private static BitField  <xsl:value-of select="@name"/> = new BitField(<xsl:value-of select="@mask"/>);
-</xsl:template>
+    <xsl:template match="bit">
+        <xsl:call-template name="indent"/>
+        <xsl:text>/**/private static BitField </xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text> = new BitField(</xsl:text>
+        <xsl:value-of select="@mask"/>
+        <xsl:text>);</xsl:text>
+        <xsl:call-template name="linebreak"/>
+    </xsl:template>
 
     <xsl:template match="const">
         <xsl:if test="@description">