]> source.dussan.org Git - jackcess.git/commitdiff
move usagemap implementations into UsageMap in prep for future changes; clean up...
authorJames Ahlborn <jtahlborn@yahoo.com>
Mon, 9 Jul 2007 06:25:53 +0000 (06:25 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Mon, 9 Jul 2007 06:25:53 +0000 (06:25 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@152 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/Database.java
src/java/com/healthmarketscience/jackcess/InlineUsageMap.java [deleted file]
src/java/com/healthmarketscience/jackcess/JetFormat.java
src/java/com/healthmarketscience/jackcess/PageChannel.java
src/java/com/healthmarketscience/jackcess/ReferenceUsageMap.java [deleted file]
src/java/com/healthmarketscience/jackcess/TempPageHolder.java
src/java/com/healthmarketscience/jackcess/UsageMap.java

index 1f93d46be94e9ae11adc1669ed124bf8f91870da..0a94e0af974c29c1b812881e38e20d4707756b0e 100644 (file)
@@ -423,14 +423,14 @@ public class Database
                                            column.getName());
       }
     }
-    
-    //We are creating a new page at the end of the db for the tdef.
-    int pageNumber = _pageChannel.getPageCount();
-    
+
+    // first, create the usage map page
+    int usageMapPageNumber = _pageChannel.writeNewPage(
+        createUsageMapDefinitionBuffer());
+
+    // now, create the table definition
     ByteBuffer buffer = _pageChannel.createPageBuffer();
-    
-    writeTableDefinition(buffer, columns, pageNumber);
-    
+    writeTableDefinition(buffer, columns, usageMapPageNumber);
     writeColumnDefinitions(buffer, columns); 
     
     //End of tabledef
@@ -441,17 +441,15 @@ public class Database
     buffer.putShort(2, (short)(_format.PAGE_SIZE - tableDefLen - 8)); // overwrite page free space
     buffer.putInt(8, tableDefLen);  //Overwrite length of data for this page
        
-    //Write the tdef and usage map pages to disk.
-    _pageChannel.writeNewPage(buffer);
-    _pageChannel.writeNewPage(createUsageMapDefinitionBuffer(pageNumber));
-    _pageChannel.writeNewPage(createUsageMapDataBuffer()); //Usage map
+    //Write the tdef page to disk.
+    int tdefPageNumber = _pageChannel.writeNewPage(buffer);
     
     //Add this table to our internal list.
-    addTable(name, new Integer(pageNumber));
+    addTable(name, new Integer(tdefPageNumber));
     
     //Add this table to system tables
-    addToSystemCatalog(name, pageNumber);
-    addToAccessControlEntries(pageNumber);    
+    addToSystemCatalog(name, tdefPageNumber);
+    addToAccessControlEntries(tdefPageNumber);    
   }
   
   /**
@@ -460,7 +458,7 @@ public class Database
    * @param pageNumber Page number that this table definition will be written to
    */
   private void writeTableDefinition(ByteBuffer buffer, List<Column> columns,
-      int pageNumber)
+      int usageMapPageNumber)
   throws IOException {
     //Start writing the tdef
     buffer.put(PageTypes.TABLE_DEF);  //Page type
@@ -484,10 +482,9 @@ public class Database
     buffer.putInt(0);  //Number of indexes in table
     buffer.putInt(0);  //Number of indexes in table
     buffer.put((byte) 0); //Usage map row number
-    int usageMapPage = pageNumber + 1;
-    ByteUtil.put3ByteInt(buffer, usageMapPage);  //Usage map page number
+    ByteUtil.put3ByteInt(buffer, usageMapPageNumber);  //Usage map page number
     buffer.put((byte) 1); //Free map row number
-    ByteUtil.put3ByteInt(buffer, usageMapPage);  //Free map page number
+    ByteUtil.put3ByteInt(buffer, usageMapPageNumber);  //Free map page number
     if (LOG.isDebugEnabled()) {
       int position = buffer.position();
       buffer.rewind();
@@ -573,12 +570,10 @@ public class Database
   }
   
   /**
-   * Create the usage map definition page buffer.  It will be stored on the page
-   * immediately after the tdef page.
-   * @param pageNumber Page number that the corresponding table definition will
-   *    be written to
+   * Create the usage map definition page buffer.
    */
-  private ByteBuffer createUsageMapDefinitionBuffer(int pageNumber) throws IOException {
+  private ByteBuffer createUsageMapDefinitionBuffer() throws IOException
+  {
     ByteBuffer rtn = _pageChannel.createPageBuffer();
     rtn.put(PageTypes.DATA);
     rtn.put((byte) 0x1);  //Unknown
@@ -590,23 +585,11 @@ public class Database
     rtn.putShort((short) _format.OFFSET_FREE_PAGES_USAGE_MAP_DEF);  //Second location
     rtn.position(_format.OFFSET_USED_PAGES_USAGE_MAP_DEF);
     rtn.put((byte) UsageMap.MAP_TYPE_REFERENCE);
-    rtn.putInt(pageNumber + 2);  //First referenced page number
     rtn.position(_format.OFFSET_FREE_PAGES_USAGE_MAP_DEF);
     rtn.put((byte) UsageMap.MAP_TYPE_INLINE);
     return rtn;
   }
   
-  /**
-   * Create a usage map data page buffer.
-   */
-  private ByteBuffer createUsageMapDataBuffer() throws IOException {
-    ByteBuffer rtn = _pageChannel.createPageBuffer();
-    rtn.put(PageTypes.USAGE_MAP);
-    rtn.put((byte) 0x01); //Unknown
-    rtn.putShort((short) 0);  //Unknown
-    return rtn;
-  }
-  
   /**
    * Add a new table to the system catalog
    * @param name Table name
diff --git a/src/java/com/healthmarketscience/jackcess/InlineUsageMap.java b/src/java/com/healthmarketscience/jackcess/InlineUsageMap.java
deleted file mode 100644 (file)
index da2459a..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
-Copyright (c) 2005 Health Market Science, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
-USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
-*/
-
-package com.healthmarketscience.jackcess;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Usage map whose map is written inline in the same page.  This type of map
- * can contain a maximum of 512 pages, and is always used for free space maps.
- * It has a start page, which all page numbers in its map are calculated as
- * starting from.
- * @author Tim McCune
- */
-public class InlineUsageMap extends UsageMap {
-  
-  /** Size in bytes of the map */
-  private static final int MAP_SIZE = 64;
-  
-  /**
-   * @param pageChannel Used to read in pages
-   * @param dataBuffer Buffer that contains this map's declaration
-   * @param pageNum Page number that this usage map is contained in
-   * @param format Format of the database that contains this usage map
-   * @param rowStart Offset at which the declaration starts in the buffer
-   */
-  public InlineUsageMap(PageChannel pageChannel, ByteBuffer dataBuffer,
-      int pageNum, JetFormat format, short rowStart)
-  throws IOException
-  {
-    super(pageChannel, dataBuffer, pageNum, format, rowStart);
-    int startPage = dataBuffer.getInt(rowStart + 1);
-    processMap(dataBuffer, 0, startPage);
-  }
-  
-  //Javadoc copied from UsageMap
-  protected void addOrRemovePageNumber(final int pageNumber, boolean add)
-  throws IOException
-  {
-    int startPage = getStartPage();
-    if (add && pageNumber < startPage) {
-      throw new IOException("Can't add page number " + pageNumber +
-          " because it is less than start page " + startPage);
-    }
-    int relativePageNumber = pageNumber - startPage;
-    ByteBuffer buffer = getDataBuffer();
-    if ((!add && !getPageNumbers().get(relativePageNumber)) ||
-        (add && (relativePageNumber > MAP_SIZE * 8 - 1)))
-    {
-      //Increase the start page to the current page and clear out the map.
-      startPage = pageNumber;
-      setStartPage(startPage);
-      buffer.position(getRowStart() + 1);
-      buffer.putInt(startPage);
-      getPageNumbers().clear();
-      if (!add) {
-        for (int j = 0; j < MAP_SIZE; j++) {
-          buffer.put((byte) 0xff); //Fill bitmap with 1s
-        }
-        getPageNumbers().set(0, (MAP_SIZE * 8)); //Fill our list with page numbers
-      }
-      getPageChannel().writePage(buffer, getDataPageNumber());
-      relativePageNumber = pageNumber - startPage;
-    }
-    updateMap(pageNumber, relativePageNumber, 1 << (relativePageNumber % 8), buffer, add);
-    //Write the updated map back to disk
-    getPageChannel().writePage(buffer, getDataPageNumber());
-  }
-  
-}
index c40bcc227ab5e8e572d9a2ce67214814646b5ec2..501d19bea990b1e77169df441eef24a21566a1d4 100644 (file)
@@ -27,8 +27,6 @@ King of Prussia, PA 19406
 
 package com.healthmarketscience.jackcess;
 
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
@@ -121,6 +119,7 @@ public abstract class JetFormat {
   public final int SIZE_INDEX_ENTRY_MASK;
   
   public final int PAGES_PER_USAGE_MAP_PAGE;
+  public final int USAGE_MAP_TABLE_BYTE_LENGTH;
   
   public final Charset CHARSET;
   
@@ -205,6 +204,7 @@ public abstract class JetFormat {
     SIZE_INDEX_ENTRY_MASK = defineSizeIndexEntryMask();
     
     PAGES_PER_USAGE_MAP_PAGE = definePagesPerUsageMapPage();
+    USAGE_MAP_TABLE_BYTE_LENGTH = defineUsageMapTableByteLength();
     
     CHARSET = defineCharset();
   }
@@ -267,6 +267,7 @@ public abstract class JetFormat {
   protected abstract int defineSizeIndexEntryMask();
   
   protected abstract int definePagesPerUsageMapPage();
+  protected abstract int defineUsageMapTableByteLength();
     
   protected abstract Charset defineCharset();
 
@@ -339,6 +340,7 @@ public abstract class JetFormat {
     protected int defineSizeIndexEntryMask() { return 453; }
     
     protected int definePagesPerUsageMapPage() { return 4092 * 8; }
+    protected int defineUsageMapTableByteLength() { return 64; }
       
     protected Charset defineCharset() { return Charset.forName("UTF-16LE"); }
   }
index 69b920631ebcac9864c953e61cd0817f69e75159..191bb490b262973674f2451dcb84848ca260feb0 100644 (file)
@@ -139,13 +139,6 @@ public class PageChannel implements Channel {
     return pageNumber;
   }
   
-  /**
-   * @return Number of pages in the database
-   */
-  public int getPageCount() throws IOException {
-    return (int) (_channel.size() / _format.PAGE_SIZE);
-  }
-  
   /**
    * @return A newly-allocated buffer that can be passed to readPage
    */
diff --git a/src/java/com/healthmarketscience/jackcess/ReferenceUsageMap.java b/src/java/com/healthmarketscience/jackcess/ReferenceUsageMap.java
deleted file mode 100644 (file)
index 35ff678..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-Copyright (c) 2005 Health Market Science, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
-USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
-*/
-
-package com.healthmarketscience.jackcess;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Usage map whose map is written across one or more entire separate pages of
- * page type USAGE_MAP.  This type of map can contain 32736 pages per reference
- * page, and a maximum of 16 reference map pages for a total maximum of 523776
- * pages (2 GB).
- * @author Tim McCune
- */
-public class ReferenceUsageMap extends UsageMap {
-  
-  /** Buffer that contains the current reference map page */ 
-  private final TempPageHolder _mapPageHolder =
-    TempPageHolder.newHolder(false);
-  
-  /**
-   * @param pageChannel Used to read in pages
-   * @param dataBuffer Buffer that contains this map's declaration
-   * @param pageNum Page number that this usage map is contained in
-   * @param format Format of the database that contains this usage map
-   * @param rowStart Offset at which the declaration starts in the buffer
-   */
-  public ReferenceUsageMap(PageChannel pageChannel, ByteBuffer dataBuffer,
-      int pageNum, JetFormat format, short rowStart)
-  throws IOException
-  {
-    super(pageChannel, dataBuffer, pageNum, format, rowStart);
-    for (int i = 0; i < 17; i++) {
-      int mapPageNum = dataBuffer.getInt(getRowStart() +
-          format.OFFSET_REFERENCE_MAP_PAGE_NUMBERS + (4 * i));
-      if (mapPageNum > 0) {
-        ByteBuffer mapPageBuffer =
-          _mapPageHolder.setPage(pageChannel, mapPageNum);
-        byte pageType = mapPageBuffer.get();
-        if (pageType != PageTypes.USAGE_MAP) {
-          throw new IOException("Looking for usage map at page " + mapPageNum +
-              ", but page type is " + pageType);
-        }
-        mapPageBuffer.position(format.OFFSET_USAGE_MAP_PAGE_DATA);
-        setStartOffset(mapPageBuffer.position());
-        processMap(mapPageBuffer, i, 0);
-      }
-    }
-  }
-  
-  //Javadoc copied from UsageMap
-  protected void addOrRemovePageNumber(final int pageNumber, boolean add)
-  throws IOException
-  {
-    int pageIndex = (int) Math.floor(pageNumber / getFormat().PAGES_PER_USAGE_MAP_PAGE);
-    int mapPageNum = getDataBuffer().getInt(calculateMapPagePointerOffset(pageIndex));
-    if(mapPageNum <= 0) {
-      //Need to create a new usage map page
-      mapPageNum  = createNewUsageMapPage(pageIndex);
-    }
-    ByteBuffer mapPageBuffer = _mapPageHolder.setPage(getPageChannel(),
-                                                      mapPageNum);
-    updateMap(pageNumber, pageNumber - (getFormat().PAGES_PER_USAGE_MAP_PAGE * pageIndex),
-        1 << ((pageNumber - (getFormat().PAGES_PER_USAGE_MAP_PAGE * pageIndex)) % 8),
-        mapPageBuffer, add);
-    getPageChannel().writePage(mapPageBuffer, mapPageNum);
-  }
-  
-  /**
-   * Create a new usage map page and update the map declaration with a pointer
-   *    to it.
-   * @param pageIndex Index of the page reference within the map declaration 
-   */
-  private int createNewUsageMapPage(int pageIndex) throws IOException {
-    ByteBuffer mapPageBuffer = _mapPageHolder.startNewPage(getPageChannel());
-    mapPageBuffer.put(PageTypes.USAGE_MAP);
-    mapPageBuffer.put((byte) 0x01);  //Unknown
-    mapPageBuffer.putShort((short) 0); //Unknown
-    int mapPageNum = getPageChannel().writeNewPage(mapPageBuffer);
-    _mapPageHolder.finishNewPage(mapPageNum);
-    getDataBuffer().putInt(calculateMapPagePointerOffset(pageIndex),
-                           mapPageNum);
-    getPageChannel().writePage(getDataBuffer(), getDataPageNumber());
-    return mapPageNum;
-  }
-  
-  private int calculateMapPagePointerOffset(int pageIndex) {
-    return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS + (pageIndex * 4);
-  }
-  
-}
index 91367c68a87e8319182e5dfe6f36b4ed6958e241..d2a59a6dbbc5de63aac92ba36e61b308c7236b27 100644 (file)
@@ -95,9 +95,10 @@ public abstract class TempPageHolder {
    * the PageChannel as a new page and assigned a number.
    */
   public void finishNewPage(int pageNumber)
+    throws IOException
   {
     if(_pageNumber != PageChannel.INVALID_PAGE_NUMBER) {
-      throw new IllegalStateException("page number should not be set");
+      throw new IOException("page number should not be set");
     }
     _pageNumber = pageNumber;
   }
index 1d2fcd4be1dbb399fffef41f34bb1fa801133af4..e2bf967e81b4868a09b73e4761cc1adf18633c4d 100644 (file)
@@ -32,6 +32,7 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.List;
+import java.util.logging.Handler;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -40,7 +41,8 @@ import org.apache.commons.logging.LogFactory;
  * Describes which database pages a particular table uses
  * @author Tim McCune
  */
-public abstract class UsageMap {
+public class UsageMap
+{
   
   private static final Log LOG = LogFactory.getLog(UsageMap.class);
   
@@ -68,34 +70,9 @@ public abstract class UsageMap {
   /** modification count on the usage map, used to keep the iterators in
       sync */
   private int _modCount = 0;
-  
-  /**
-   * @param pageChannel Used to read in pages
-   * @param pageNum Page number that this usage map is contained in
-   * @param rowNum Number of the row on the page that contains this usage map
-   * @param format Format of the database that contains this usage map
-   * @return Either an InlineUsageMap or a ReferenceUsageMap, depending on which
-   *    type of map is found
-   */
-  public static UsageMap read(PageChannel pageChannel, int pageNum, byte rowNum, JetFormat format)
-  throws IOException
-  {
-    ByteBuffer dataBuffer = pageChannel.createPageBuffer();
-    pageChannel.readPage(dataBuffer, pageNum);
-    short rowStart = Table.findRowStart(dataBuffer, rowNum, format);
-    int rowEnd = Table.findRowEnd(dataBuffer, rowNum, format);
-    dataBuffer.limit(rowEnd);    
-    byte mapType = dataBuffer.get(rowStart);
-    UsageMap rtn;
-    if (mapType == MAP_TYPE_INLINE) {
-      rtn = new InlineUsageMap(pageChannel, dataBuffer, pageNum, format, rowStart);   
-    } else if (mapType == MAP_TYPE_REFERENCE) {
-      rtn = new ReferenceUsageMap(pageChannel, dataBuffer, pageNum, format, rowStart);
-    } else {
-      throw new IOException("Unrecognized map type: " + mapType);
-    }
-    return rtn;
-  }
+  /** the current handler implementation for reading/writing the specific
+      usage map type.  note, this may change over time. */
+  private Handler _handler;
   
   /**
    * @param pageChannel Used to read in pages
@@ -104,8 +81,8 @@ public abstract class UsageMap {
    * @param format Format of the database that contains this usage map
    * @param rowStart Offset at which the declaration starts in the buffer
    */
-  protected UsageMap(PageChannel pageChannel, ByteBuffer dataBuffer,
-                     int pageNum, JetFormat format, short rowStart)
+  private UsageMap(PageChannel pageChannel, ByteBuffer dataBuffer,
+                   int pageNum, JetFormat format, short rowStart)
   throws IOException
   {
     _pageChannel = pageChannel;
@@ -121,6 +98,42 @@ public abstract class UsageMap {
     }
   }
 
+  /**
+   * @param pageChannel Used to read in pages
+   * @param pageNum Page number that this usage map is contained in
+   * @param rowNum Number of the row on the page that contains this usage map
+   * @param format Format of the database that contains this usage map
+   * @return Either an InlineUsageMap or a ReferenceUsageMap, depending on
+   *         which type of map is found
+   */
+  public static UsageMap read(PageChannel pageChannel, int pageNum,
+                              byte rowNum, JetFormat format)
+    throws IOException
+  {
+    ByteBuffer dataBuffer = pageChannel.createPageBuffer();
+    pageChannel.readPage(dataBuffer, pageNum);
+    short rowStart = Table.findRowStart(dataBuffer, rowNum, format);
+    int rowEnd = Table.findRowEnd(dataBuffer, rowNum, format);
+    dataBuffer.limit(rowEnd);    
+    byte mapType = dataBuffer.get(rowStart);
+    UsageMap rtn = new UsageMap(pageChannel, dataBuffer, pageNum, format,
+                                rowStart);
+    rtn.initHandler(mapType);
+    return rtn;
+  }
+
+  private void initHandler(byte mapType)
+    throws IOException
+  {
+    if (mapType == MAP_TYPE_INLINE) {
+      _handler = new InlineHandler();
+    } else if (mapType == MAP_TYPE_REFERENCE) {
+      _handler = new ReferenceHandler();
+    } else {
+      throw new IOException("Unrecognized map type: " + mapType);
+    }
+  }
+  
   public PageIterator iterator() {
     return new ForwardPageIterator();
   }
@@ -177,11 +190,13 @@ public abstract class UsageMap {
     _startPage = startPage;
     while (buffer.hasRemaining()) {
       byte b = buffer.get();
-      for (int i = 0; i < 8; i++) {
-        if ((b & (1 << i)) != 0) {
-          int pageNumberOffset = (byteCount * 8 + i) +
-            (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE);
-          _pageNumbers.set(pageNumberOffset);
+      if(b != (byte)0) {
+        for (int i = 0; i < 8; i++) {
+          if ((b & (1 << i)) != 0) {
+            int pageNumberOffset = (byteCount * 8 + i) +
+              (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE);
+            _pageNumbers.set(pageNumberOffset);
+          }
         }
       }
       byteCount++;
@@ -202,7 +217,7 @@ public abstract class UsageMap {
       }
     }
     ++_modCount;
-    addOrRemovePageNumber(pageNumber, true);
+    _handler.addOrRemovePageNumber(pageNumber, true);
   }
   
   /**
@@ -210,7 +225,7 @@ public abstract class UsageMap {
    */
   public void removePageNumber(int pageNumber) throws IOException {
     ++_modCount;
-    addOrRemovePageNumber(pageNumber, false);
+    _handler.addOrRemovePageNumber(pageNumber, false);
   }
   
   protected void updateMap(int absolutePageNumber, int relativePageNumber,
@@ -231,6 +246,17 @@ public abstract class UsageMap {
     buffer.put(_startOffset + offset, b);
   }
   
+  private void promoteInlineHandlerToReferenceHandler(int pageNumber)
+    throws IOException
+  {
+    // FIXME writeme
+//     int startPage = _startPage;
+//     BitSet curPageNumbers = (BitSet)_pageNumbers.clone();
+//     _pageNumbers.clear();
+//     _startPage = 0;
+    
+  }
+  
   public String toString() {
     StringBuilder builder = new StringBuilder("page numbers: [");
     for(PageIterator iter = iterator(); iter.hasNextPage(); ) {
@@ -243,12 +269,162 @@ public abstract class UsageMap {
     return builder.toString();
   }
   
+  private abstract class Handler
+  {
+    protected Handler() {
+    }
+    
+    /**
+     * @param pageNumber Page number to add or remove from this map
+     * @param add True to add it, false to remove it
+     */
+    public abstract void addOrRemovePageNumber(int pageNumber, boolean add)
+      throws IOException;
+  }
+
   /**
-   * @param pageNumber Page number to add or remove from this map
-   * @param add True to add it, false to remove it
+   * Usage map whose map is written inline in the same page.  This type of map
+   * can contain a maximum of 512 pages, and is always used for free space
+   * maps.  It has a start page, which all page numbers in its map are
+   * calculated as starting from.
+   * @author Tim McCune
    */
-  protected abstract void addOrRemovePageNumber(int pageNumber, boolean add) throws IOException;
+  private class InlineHandler extends Handler
+  {
+    private InlineHandler()
+      throws IOException
+    {
+      int startPage = getDataBuffer().getInt(getRowStart() + 1);
+      processMap(getDataBuffer(), 0, startPage);
+    }
+  
+    @Override
+    public void addOrRemovePageNumber(int pageNumber, boolean add)
+      throws IOException
+    {
+      int startPage = getStartPage();
+      if (add && pageNumber < startPage) {
+        throw new IOException("Can't add page number " + pageNumber +
+                              " because it is less than start page " + startPage);
+      }
+      int relativePageNumber = pageNumber - startPage;
+      ByteBuffer buffer = getDataBuffer();
+      if ((!add && !getPageNumbers().get(relativePageNumber)) ||
+          (add && (relativePageNumber >
+                   (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8 - 1))))
+      {
+        // FIXME writeme
+//         if(add) {
+//           promoteInlineHandlerToReferenceHandler(pageNumber);
+//           return;
+//         }
+        //Increase the start page to the current page and clear out the map.
+        startPage = pageNumber;
+        setStartPage(startPage);
+        buffer.position(getRowStart() + 1);
+        buffer.putInt(startPage);
+        getPageNumbers().clear();
+        if (!add) {
+          for (int j = 0; j < getFormat().USAGE_MAP_TABLE_BYTE_LENGTH; j++) {
+            buffer.put((byte) 0xff); //Fill bitmap with 1s
+          }
+          getPageNumbers().set(0, (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8)); //Fill our list with page numbers
+        }
+        getPageChannel().writePage(buffer, getDataPageNumber());
+        relativePageNumber = pageNumber - startPage;
+      }
+      updateMap(pageNumber, relativePageNumber, 1 << (relativePageNumber % 8), buffer, add);
+      //Write the updated map back to disk
+      getPageChannel().writePage(buffer, getDataPageNumber());
+    }
+  }
 
+  /**
+   * Usage map whose map is written across one or more entire separate pages
+   * of page type USAGE_MAP.  This type of map can contain 32736 pages per
+   * reference page, and a maximum of 16 reference map pages for a total
+   * maximum of 523776 pages (2 GB).
+   * @author Tim McCune
+   */
+  private class ReferenceHandler extends Handler
+  {
+    /** Buffer that contains the current reference map page */ 
+    private final TempPageHolder _mapPageHolder =
+      TempPageHolder.newHolder(false);
+  
+    private ReferenceHandler()
+      throws IOException
+    {
+      setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
+      // there is no "start page" for a reference usage map, so we get an
+      // extra page reference on top of the number of page references that fit
+      // in the table
+      int numPages = (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH / 4) + 1;
+      for (int i = 0; i < numPages; i++) {
+        int mapPageNum = getDataBuffer().getInt(
+            getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
+            (4 * i));
+        if (mapPageNum > 0) {
+          ByteBuffer mapPageBuffer =
+            _mapPageHolder.setPage(getPageChannel(), mapPageNum);
+          byte pageType = mapPageBuffer.get();
+          if (pageType != PageTypes.USAGE_MAP) {
+            throw new IOException("Looking for usage map at page " +
+                                  mapPageNum + ", but page type is " +
+                                  pageType);
+          }
+          mapPageBuffer.position(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
+          processMap(mapPageBuffer, i, 0);
+        }
+      }
+    }
+  
+    @Override
+    public void addOrRemovePageNumber(int pageNumber, boolean add)
+      throws IOException
+    {
+      int pageIndex = (int) Math.floor(pageNumber / getFormat().PAGES_PER_USAGE_MAP_PAGE);
+      int mapPageNum = getDataBuffer().getInt(calculateMapPagePointerOffset(pageIndex));
+      if(mapPageNum <= 0) {
+        //Need to create a new usage map page
+        mapPageNum  = createNewUsageMapPage(pageIndex);
+      }
+      ByteBuffer mapPageBuffer = _mapPageHolder.setPage(getPageChannel(),
+                                                        mapPageNum);
+      updateMap(pageNumber, pageNumber - (getFormat().PAGES_PER_USAGE_MAP_PAGE * pageIndex),
+                1 << ((pageNumber - (getFormat().PAGES_PER_USAGE_MAP_PAGE * pageIndex)) % 8),
+                mapPageBuffer, add);
+      getPageChannel().writePage(mapPageBuffer, mapPageNum);
+    }
+  
+    /**
+     * Create a new usage map page and update the map declaration with a
+     * pointer to it.
+     * @param pageIndex Index of the page reference within the map declaration 
+     */
+    private int createNewUsageMapPage(int pageIndex) throws IOException {
+      ByteBuffer mapPageBuffer = _mapPageHolder.startNewPage(getPageChannel());
+      mapPageBuffer.put(PageTypes.USAGE_MAP);
+      mapPageBuffer.put((byte) 0x01);  //Unknown
+      mapPageBuffer.putShort((short) 0); //Unknown
+      for(int i = 0; i < mapPageBuffer.limit(); ++i) {
+        byte b = mapPageBuffer.get(i);
+      }
+      int mapPageNum = getPageChannel().writeNewPage(mapPageBuffer);
+      _mapPageHolder.finishNewPage(mapPageNum);
+      getDataBuffer().putInt(calculateMapPagePointerOffset(pageIndex),
+                             mapPageNum);
+      getPageChannel().writePage(getDataBuffer(), getDataPageNumber());
+      return mapPageNum;
+    }
+  
+    private int calculateMapPagePointerOffset(int pageIndex) {
+      return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
+        (pageIndex * 4);
+    }
+  }
+  
+  
   /**
    * Utility class to iterate over the pages in the UsageMap.
    */
@@ -314,9 +490,8 @@ public abstract class UsageMap {
         _prevSetBit = _nextSetBit;
         _nextSetBit = _pageNumbers.nextSetBit(_nextSetBit + 1);
         return _prevSetBit + _startPage;
-      } else {
-        return PageChannel.INVALID_PAGE_NUMBER;
       }
+      return PageChannel.INVALID_PAGE_NUMBER;
     }
 
     @Override
@@ -346,9 +521,8 @@ public abstract class UsageMap {
           --_nextSetBit;
         }
         return _prevSetBit + _startPage;
-      } else {
-        return PageChannel.INVALID_PAGE_NUMBER;
       }
+      return PageChannel.INVALID_PAGE_NUMBER;
     }
 
     @Override