]> source.dussan.org Git - jackcess.git/commitdiff
move Index internals to IndexImpl
authorJames Ahlborn <jtahlborn@yahoo.com>
Sun, 3 Mar 2013 04:26:09 +0000 (04:26 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Sun, 3 Mar 2013 04:26:09 +0000 (04:26 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/jackcess-2@670 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/Cursor.java
src/java/com/healthmarketscience/jackcess/CursorBuilder.java
src/java/com/healthmarketscience/jackcess/FKEnforcer.java
src/java/com/healthmarketscience/jackcess/Index.java
src/java/com/healthmarketscience/jackcess/IndexBuilder.java
src/java/com/healthmarketscience/jackcess/IndexCursor.java
src/java/com/healthmarketscience/jackcess/IndexData.java
src/java/com/healthmarketscience/jackcess/IndexImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/Joiner.java
src/java/com/healthmarketscience/jackcess/Table.java
src/java/com/healthmarketscience/jackcess/TableImpl.java

index 162e7993bf656f84150d5168c0b4362cad297701..2b6a9d2fc27388083a8251aeeca00915847e05f1 100644 (file)
@@ -118,7 +118,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
    * @param index index for the table which will define traversal order as
    *              well as enhance certain lookups
    */
-  public static Cursor createIndexCursor(TableImpl table, Index index)
+  public static Cursor createIndexCursor(TableImpl table, IndexImpl index)
     throws IOException
   {
     return IndexCursor.createCursor(table, index);
@@ -140,7 +140,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
    * @param endRow the last row of data for the cursor (inclusive), or
    *               {@code null} for the last entry
    */
-  public static Cursor createIndexCursor(TableImpl table, Index index,
+  public static Cursor createIndexCursor(TableImpl table, IndexImpl index,
                                          Object[] startRow, Object[] endRow)
     throws IOException
   {
@@ -165,7 +165,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
    *               the last entry
    * @param endInclusive whether or not endRow is inclusive or exclusive
    */
-  public static Cursor createIndexCursor(TableImpl table, Index index,
+  public static Cursor createIndexCursor(TableImpl table, IndexImpl index,
                                          Object[] startRow,
                                          boolean startInclusive,
                                          Object[] endRow,
@@ -240,7 +240,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
    * @param rowPattern pattern to be used to find the row
    * @return the matching row or {@code null} if a match could not be found.
    */
-  public static Map<String,Object> findRow(TableImpl table, Index index,
+  public static Map<String,Object> findRow(TableImpl table, IndexImpl index,
                                            Map<String,?> rowPattern)
     throws IOException
   {
@@ -269,7 +269,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
    *                     desired row
    * @return the matching row or {@code null} if a match could not be found.
    */
-  public static Object findValue(TableImpl table, Index index, Column column,
+  public static Object findValue(TableImpl table, IndexImpl index, Column column,
                                  Column columnPattern, Object valuePattern)
     throws IOException
   {
index f0d7348fdf558170fa35c8b4b006392dbcfb8f08..df9b3c30a37a626981f0d3a72e9c6c16073eb0b2 100644 (file)
@@ -44,9 +44,9 @@ import java.util.List;
  */
 public class CursorBuilder {
   /** the table which the cursor will traverse */
-  private final Table _table;
+  private final TableImpl _table;
   /** optional index to use in traversal */
-  private Index _index;
+  private IndexImpl _index;
   /** optional start row for an index cursor */
   private Object[] _startRow;
   /** whether or not start row for an index cursor is inclusive */
@@ -63,7 +63,7 @@ public class CursorBuilder {
   private ColumnMatcher _columnMatcher;
 
   public CursorBuilder(Table table) {
-    _table = table;
+    _table = (TableImpl)table;
   }
 
   /**
@@ -96,7 +96,7 @@ public class CursorBuilder {
    * Sets an index to use for the cursor.
    */
   public CursorBuilder setIndex(Index index) {
-    _index = index;
+    _index = (IndexImpl)index;
     return this;
   }
 
@@ -139,7 +139,7 @@ public class CursorBuilder {
    */
   private CursorBuilder setIndexByColumns(List<String> searchColumns) {
     boolean found = false;
-    for(Index index : _table.getIndexes()) {
+    for(IndexImpl index : _table.getIndexes()) {
       
       Collection<IndexData.ColumnDescriptor> indexColumns = index.getColumns();
       if(indexColumns.size() != searchColumns.size()) {
@@ -278,9 +278,9 @@ public class CursorBuilder {
   {
     Cursor cursor = null;
     if(_index == null) {
-      cursor = Cursor.createCursor((TableImpl)_table);
+      cursor = Cursor.createCursor(_table);
     } else {
-      cursor = Cursor.createIndexCursor((TableImpl)_table, _index,
+      cursor = Cursor.createIndexCursor(_table, _index,
                                         _startRow, _startRowInclusive,
                                         _endRow, _endRowInclusive);
     }
index 8aa9f01686591c920291020a277804e596ae05f6..9e148a61ddb7a9118ce59f605694e3ba89b8eb13 100644 (file)
@@ -57,8 +57,8 @@ final class FKEnforcer
 
     // at this point, only init the index columns
     Set<Column> cols = new TreeSet<Column>();
-    for(Index idx : _table.getIndexes()) {
-      Index.ForeignKeyReference ref = idx.getReference();
+    for(IndexImpl idx : _table.getIndexes()) {
+      IndexImpl.ForeignKeyReference ref = idx.getReference();
       if(ref != null) {
         // compile an ordered list of all columns in this table which are
         // involved in foreign key relationships with other tables
@@ -88,8 +88,8 @@ final class FKEnforcer
     _primaryJoinersDoDel = new ArrayList<Joiner>(1);
     _secondaryJoiners = new ArrayList<Joiner>(1);
 
-    for(Index idx : _table.getIndexes()) {
-      Index.ForeignKeyReference ref = idx.getReference();
+    for(IndexImpl idx : _table.getIndexes()) {
+      IndexImpl.ForeignKeyReference ref = idx.getReference();
       if(ref != null) {
 
         Joiner joiner = Joiner.create(idx);
index fec1bd9ef5fbc797e985cef5ecd38470ba6d7918..d27e7e15e8df9639bd455d607a101430c0d58ca8 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,207 +15,38 @@ 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;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 
 /**
- * Access table (logical) index.  Logical indexes are backed for IndexData,
- * where one or more logical indexes could be backed by the same data.
- * 
- * @author Tim McCune
+ *
+ * @author James Ahlborn
  */
-public class Index implements Comparable<Index> {
-  
-  protected static final Log LOG = LogFactory.getLog(Index.class);
-    
-  /** index type for primary key indexes */
-  static final byte PRIMARY_KEY_INDEX_TYPE = (byte)1;
-  
-  /** index type for foreign key indexes */
-  static final byte FOREIGN_KEY_INDEX_TYPE = (byte)2;
-
-  /** flag for indicating that updates should cascade in a foreign key index */
-  private static final byte CASCADE_UPDATES_FLAG = (byte)1;
-  /** flag for indicating that deletes should cascade in a foreign key index */
-  private static final byte CASCADE_DELETES_FLAG = (byte)1;
-
-  /** index table type for the "primary" table in a foreign key index */
-  private static final byte PRIMARY_TABLE_TYPE = (byte)1;
-
-  /** indicate an invalid index number for foreign key field */
-  private static final int INVALID_INDEX_NUMBER = -1;
-
-  /** the actual data backing this index (more than one index may be backed by
-      the same data */
-  private final IndexData _data;
-  /** 0-based index number */
-  private final int _indexNumber;
-  /** the type of the index */
-  private final byte _indexType;
-  /** Index name */
-  private String _name;
-  /** foreign key reference info, if any */
-  private final ForeignKeyReference _reference;
-  
-  protected Index(ByteBuffer tableBuffer, List<IndexData> indexDatas,
-                  JetFormat format) 
-    throws IOException
-  {
-
-    ByteUtil.forward(tableBuffer, format.SKIP_BEFORE_INDEX_SLOT); //Forward past Unknown
-    _indexNumber = tableBuffer.getInt();
-    int indexDataNumber = tableBuffer.getInt();
-      
-    // read foreign key reference info
-    byte relIndexType = tableBuffer.get();
-    int relIndexNumber = tableBuffer.getInt();
-    int relTablePageNumber = tableBuffer.getInt();
-    byte cascadeUpdatesFlag = tableBuffer.get();
-    byte cascadeDeletesFlag = tableBuffer.get();
-
-    _indexType = tableBuffer.get();
-    if((_indexType == FOREIGN_KEY_INDEX_TYPE) && 
-       (relIndexNumber != INVALID_INDEX_NUMBER)) {
-      _reference = new ForeignKeyReference(
-          relIndexType, relIndexNumber, relTablePageNumber,
-          (cascadeUpdatesFlag == CASCADE_UPDATES_FLAG),
-          (cascadeDeletesFlag == CASCADE_DELETES_FLAG));
-    } else {
-      _reference = null;
-    }
-
-    ByteUtil.forward(tableBuffer, format.SKIP_AFTER_INDEX_SLOT); //Skip past Unknown
-
-    _data = indexDatas.get(indexDataNumber);
+public abstract class Index 
+{
 
-    _data.addIndex(this);
-  }
+  public abstract Table getTable();
 
-  public IndexData getIndexData() {
-    return _data;
-  }
+  public abstract String getName();
 
-  public TableImpl getTable() {
-    return getIndexData().getTable();
-  }
-  
-  public JetFormat getFormat() {
-    return getTable().getFormat();
-  }
+  public abstract boolean isPrimaryKey();
 
-  public PageChannel getPageChannel() {
-    return getTable().getPageChannel();
-  }
-
-  public int getIndexNumber() {
-    return _indexNumber;
-  }
-
-  public byte getIndexFlags() {
-    return getIndexData().getIndexFlags();
-  }
-  
-  public int getUniqueEntryCount() {
-    return getIndexData().getUniqueEntryCount();
-  }
-
-  public int getUniqueEntryCountOffset() {
-    return getIndexData().getUniqueEntryCountOffset();
-  }
-
-  public String getName() {
-    return _name;
-  }
-  
-  public void setName(String name) {
-    _name = name;
-  }
-
-  public boolean isPrimaryKey() {
-    return _indexType == PRIMARY_KEY_INDEX_TYPE;
-  }
-
-  public boolean isForeignKey() {
-    return _indexType == FOREIGN_KEY_INDEX_TYPE;
-  }
-
-  public ForeignKeyReference getReference() {
-    return _reference;
-  }
+  public abstract boolean isForeignKey();
 
   /**
-   * @return the Index referenced by this Index's ForeignKeyReference (if it
-   *         has one), otherwise {@code null}.
+   * @return the Columns for this index (unmodifiable)
    */
-  public Index getReferencedIndex() throws IOException {
-
-    if(_reference == null) {
-      return null;
-    }
-
-    TableImpl refTable = getTable().getDatabase().getTable(
-        _reference.getOtherTablePageNumber());
-
-    if(refTable == null) {
-      throw new IOException("Reference to missing table " + 
-                            _reference.getOtherTablePageNumber());
-    }
-
-    Index refIndex = null;
-    int idxNumber = _reference.getOtherIndexNumber();
-    for(Index idx : refTable.getIndexes()) {
-      if(idx.getIndexNumber() == idxNumber) {
-        refIndex = idx;
-        break;
-      }
-    }
-    
-    if(refIndex == null) {
-      throw new IOException("Reference to missing index " + idxNumber + 
-                            " on table " + refTable.getName());
-    }
-
-    // finally verify that we found the expected index (should reference this
-    // index)
-    ForeignKeyReference otherRef = refIndex.getReference();
-    if((otherRef == null) ||
-       (otherRef.getOtherTablePageNumber() != 
-        getTable().getTableDefPageNumber()) ||
-       (otherRef.getOtherIndexNumber() != _indexNumber)) {
-      throw new IOException("Found unexpected index " + refIndex.getName() +
-                            " on table " + refTable.getName() +
-                            " with reference " + otherRef);
-    }
-
-    return refIndex;
-  }
+  public abstract List<? extends ColumnInfo> getColumns();
 
   /**
-   * Whether or not {@code null} values are actually recorded in the index.
+   * @return the Index referenced by this Index's ForeignKeyReference (if it
+   *         has one), otherwise {@code null}.
    */
-  public boolean shouldIgnoreNulls() {
-    return getIndexData().shouldIgnoreNulls();
-  }
+  public abstract Index getReferencedIndex() throws IOException;
 
   /**
    * Whether or not index entries must be unique.
@@ -229,250 +60,19 @@ public class Index implements Comparable<Index> {
    *     case <i>will violate</i> the unique constraint</li>
    * </ul>
    */
-  public boolean isUnique() {
-    return getIndexData().isUnique();
-  }
-  
-  /**
-   * Returns the Columns for this index (unmodifiable)
-   */
-  public List<IndexData.ColumnDescriptor> getColumns() {
-    return getIndexData().getColumns();
-  }
-
-  /**
-   * Whether or not the complete index state has been read.
-   */
-  public boolean isInitialized() {
-    return getIndexData().isInitialized();
-  }
-  
-  /**
-   * Forces initialization of this index (actual parsing of index pages).
-   * normally, the index will not be initialized until the entries are
-   * actually needed.
-   */
-  public void initialize() throws IOException {
-    getIndexData().initialize();
-  }
+  public abstract boolean isUnique();
 
   /**
-   * Writes the current index state to the database.
-   * <p>
-   * Forces index initialization.
+   * Information about a Column in an Index
    */
-  public void update() throws IOException {
-    getIndexData().update();
-  }
+  public interface ColumnInfo {
 
-  /**
-   * Adds a row to this index
-   * <p>
-   * Forces index initialization.
-   * 
-   * @param row Row to add
-   * @param rowId rowId of the row to be added
-   */
-  public void addRow(Object[] row, RowId rowId)
-    throws IOException
-  {
-    getIndexData().addRow(row, rowId);
-  }
-  
-  /**
-   * Removes a row from this index
-   * <p>
-   * Forces index initialization.
-   * 
-   * @param row Row to remove
-   * @param rowId rowId of the row to be removed
-   */
-  public void deleteRow(Object[] row, RowId rowId)
-    throws IOException
-  {
-    getIndexData().deleteRow(row, rowId);
-  }
-      
-  /**
-   * Gets a new cursor for this index.
-   * <p>
-   * Forces index initialization.
-   */
-  public IndexData.EntryCursor cursor()
-    throws IOException
-  {
-    return cursor(null, true, null, true);
-  }
-  
-  /**
-   * Gets a new cursor for this index, narrowed to the range defined by the
-   * given startRow and endRow.
-   * <p>
-   * Forces index initialization.
-   * 
-   * @param startRow the first row of data for the cursor, or {@code null} for
-   *                 the first entry
-   * @param startInclusive whether or not startRow is inclusive or exclusive
-   * @param endRow the last row of data for the cursor, or {@code null} for
-   *               the last entry
-   * @param endInclusive whether or not endRow is inclusive or exclusive
-   */
-  public IndexData.EntryCursor cursor(Object[] startRow,
-                                      boolean startInclusive,
-                                      Object[] endRow,
-                                      boolean endInclusive)
-    throws IOException
-  {
-    return getIndexData().cursor(startRow, startInclusive, endRow,
-                                 endInclusive);
-  }
+    public Column getColumn();
 
-  /**
-   * Constructs an array of values appropriate for this index from the given
-   * column values, expected to match the columns for this index.
-   * @return the appropriate sparse array of data
-   * @throws IllegalArgumentException if the wrong number of values are
-   *         provided
-   */
-  public Object[] constructIndexRowFromEntry(Object... values)
-  {
-    return getIndexData().constructIndexRowFromEntry(values);
-  }
-    
-  /**
-   * Constructs an array of values appropriate for this index from the given
-   * column value.
-   * @return the appropriate sparse array of data or {@code null} if not all
-   *         columns for this index were provided
-   */
-  public Object[] constructIndexRow(String colName, Object value)
-  {
-    return constructIndexRow(Collections.singletonMap(colName, value));
-  }
-  
-  /**
-   * Constructs an array of values appropriate for this index from the given
-   * column values.
-   * @return the appropriate sparse array of data or {@code null} if not all
-   *         columns for this index were provided
-   */
-  public Object[] constructIndexRow(Map<String,?> row)
-  {
-    return getIndexData().constructIndexRow(row);
-  }  
-
-  @Override
-  public String toString() {
-    StringBuilder rtn = new StringBuilder();
-    rtn.append("\tName: (").append(getTable().getName()).append(") ")
-      .append(_name);
-    rtn.append("\n\tNumber: ").append(_indexNumber);
-    rtn.append("\n\tIs Primary Key: ").append(isPrimaryKey());
-    rtn.append("\n\tIs Foreign Key: ").append(isForeignKey());
-    if(_reference != null) {
-      rtn.append("\n\tForeignKeyReference: ").append(_reference);
-    }
-    rtn.append(_data.toString());
-    rtn.append("\n\n");
-    return rtn.toString();
-  }
-  
-  public int compareTo(Index other) {
-    if (_indexNumber > other.getIndexNumber()) {
-      return 1;
-    } else if (_indexNumber < other.getIndexNumber()) {
-      return -1;
-    } else {
-      return 0;
-    }
-  }
+    public boolean isAscending();
 
-  /**
-   * Writes the logical index definitions into a table definition buffer.
-   * @param buffer Buffer to write to
-   * @param indexes List of IndexBuilders to write definitions for
-   */
-  protected static void writeDefinitions(
-      TableCreator creator, ByteBuffer buffer)
-    throws IOException
-  {
-    // write logical index information
-    for(IndexBuilder idx : creator.getIndexes()) {
-      TableCreator.IndexState idxState = creator.getIndexState(idx);
-      buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def
-      buffer.putInt(idxState.getIndexNumber()); // index num
-      buffer.putInt(idxState.getIndexDataNumber()); // index data num
-      buffer.put((byte)0); // related table type
-      buffer.putInt(INVALID_INDEX_NUMBER); // related index num
-      buffer.putInt(0); // related table definition page number
-      buffer.put((byte)0); // cascade updates flag
-      buffer.put((byte)0); // cascade deletes flag
-      buffer.put(idx.getType()); // index type flags
-      buffer.putInt(0); // unknown
-    }
-
-    // write index names
-    for(IndexBuilder idx : creator.getIndexes()) {
-      TableImpl.writeName(buffer, idx.getName(), creator.getCharset());
-    }
-  }
-
-  /**
-   * Information about a foreign key reference defined in an index (when
-   * referential integrity should be enforced).
-   */
-  public static class ForeignKeyReference
-  {
-    private final byte _tableType;
-    private final int _otherIndexNumber;
-    private final int _otherTablePageNumber;
-    private final boolean _cascadeUpdates;
-    private final boolean _cascadeDeletes;
+    public int getColumnIndex();
     
-    public ForeignKeyReference(
-        byte tableType, int otherIndexNumber, int otherTablePageNumber,
-        boolean cascadeUpdates, boolean cascadeDeletes)
-    {
-      _tableType = tableType;
-      _otherIndexNumber = otherIndexNumber;
-      _otherTablePageNumber = otherTablePageNumber;
-      _cascadeUpdates = cascadeUpdates;
-      _cascadeDeletes = cascadeDeletes;
-    }
-
-    public byte getTableType() {
-      return _tableType;
-    }
-
-    public boolean isPrimaryTable() {
-      return(getTableType() == PRIMARY_TABLE_TYPE);
-    }
-
-    public int getOtherIndexNumber() {
-      return _otherIndexNumber;
-    }
-
-    public int getOtherTablePageNumber() {
-      return _otherTablePageNumber;
-    }
-
-    public boolean isCascadeUpdates() {
-      return _cascadeUpdates;
-    }
-
-    public boolean isCascadeDeletes() {
-      return _cascadeDeletes;
-    }
-
-    @Override
-    public String toString() {
-      return new StringBuilder()
-        .append("\n\t\tOther Index Number: ").append(_otherIndexNumber)
-        .append("\n\t\tOther Table Page Num: ").append(_otherTablePageNumber)
-        .append("\n\t\tIs Primary Table: ").append(isPrimaryTable())
-        .append("\n\t\tIs Cascade Updates: ").append(isCascadeUpdates())
-        .append("\n\t\tIs Cascade Deletes: ").append(isCascadeDeletes())
-        .toString();
-    }
+    public String getName();
   }
 }
index 07ddd771663da6bb7d75f4886b2e58631fc8628e..18943150603dffba829d4affe1ebca1d545c6bef 100644 (file)
@@ -61,7 +61,7 @@ public class IndexBuilder
   }
 
   public boolean isPrimaryKey() {
-    return (getType() == Index.PRIMARY_KEY_INDEX_TYPE);
+    return (getType() == IndexImpl.PRIMARY_KEY_INDEX_TYPE);
   }
 
   public boolean isUnique() {
@@ -108,7 +108,7 @@ public class IndexBuilder
    * unique).
    */
   public IndexBuilder setPrimaryKey() {
-    _type = Index.PRIMARY_KEY_INDEX_TYPE;
+    _type = IndexImpl.PRIMARY_KEY_INDEX_TYPE;
     return setUnique();
   }
 
index 016a9c64f55294a74aa0eba1f92f1421b4f9be9b..b0b41c06515a951acb93f57a2f190d9a5487ff28 100644 (file)
@@ -47,13 +47,13 @@ public class IndexCursor extends Cursor
   private final IndexDirHandler _reverseDirHandler =
     new ReverseIndexDirHandler();
   /** logical index which this cursor is using */
-  private final Index _index;
+  private final IndexImpl _index;
   /** Cursor over the entries of the relevant index */
   private final IndexData.EntryCursor _entryCursor;
   /** column names for the index entry columns */
   private Set<String> _indexEntryPattern;
 
-  private IndexCursor(TableImpl table, Index index,
+  private IndexCursor(TableImpl table, IndexImpl index,
                       IndexData.EntryCursor entryCursor)
     throws IOException
   {
@@ -76,7 +76,7 @@ public class IndexCursor extends Cursor
    * @param index index for the table which will define traversal order as
    *              well as enhance certain lookups
    */
-  public static IndexCursor createCursor(TableImpl table, Index index)
+  public static IndexCursor createCursor(TableImpl table, IndexImpl index)
     throws IOException
   {
     return createCursor(table, index, null, null);
@@ -99,7 +99,7 @@ public class IndexCursor extends Cursor
    *               {@code null} for the last entry
    */
   public static IndexCursor createCursor(
-      TableImpl table, Index index, Object[] startRow, Object[] endRow)
+      TableImpl table, IndexImpl index, Object[] startRow, Object[] endRow)
     throws IOException
   {
     return createCursor(table, index, startRow, true, endRow, true);
@@ -123,7 +123,7 @@ public class IndexCursor extends Cursor
    *               the last entry
    * @param endInclusive whether or not endRow is inclusive or exclusive
    */
-  public static IndexCursor createCursor(TableImpl table, Index index,
+  public static IndexCursor createCursor(TableImpl table, IndexImpl index,
                                          Object[] startRow,
                                          boolean startInclusive,
                                          Object[] endRow,
@@ -152,7 +152,7 @@ public class IndexCursor extends Cursor
     return cursor;
   }  
 
-  public Index getIndex() {
+  public IndexImpl getIndex() {
     return _index;
   }
 
index ca22be4b387c4a7d21291bec8f3aec142ba7f209..d6b411f64dcf98cc0446b60a81024c17c51535bf 100644 (file)
@@ -1270,7 +1270,7 @@ public class IndexData {
    * Information about the columns in an index.  Also encodes new index
    * values.
    */
-  public static abstract class ColumnDescriptor
+  public static abstract class ColumnDescriptor implements Index.ColumnInfo
   {
     private final Column _column;
     private final byte _flags;
diff --git a/src/java/com/healthmarketscience/jackcess/IndexImpl.java b/src/java/com/healthmarketscience/jackcess/IndexImpl.java
new file mode 100644 (file)
index 0000000..c34bf89
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+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;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Access table (logical) index.  Logical indexes are backed for IndexData,
+ * where one or more logical indexes could be backed by the same data.
+ * 
+ * @author Tim McCune
+ */
+public class IndexImpl extends Index implements Comparable<IndexImpl> 
+{
+  
+  protected static final Log LOG = LogFactory.getLog(Index.class);
+    
+  /** index type for primary key indexes */
+  static final byte PRIMARY_KEY_INDEX_TYPE = (byte)1;
+  
+  /** index type for foreign key indexes */
+  static final byte FOREIGN_KEY_INDEX_TYPE = (byte)2;
+
+  /** flag for indicating that updates should cascade in a foreign key index */
+  private static final byte CASCADE_UPDATES_FLAG = (byte)1;
+  /** flag for indicating that deletes should cascade in a foreign key index */
+  private static final byte CASCADE_DELETES_FLAG = (byte)1;
+
+  /** index table type for the "primary" table in a foreign key index */
+  private static final byte PRIMARY_TABLE_TYPE = (byte)1;
+
+  /** indicate an invalid index number for foreign key field */
+  private static final int INVALID_INDEX_NUMBER = -1;
+
+  /** the actual data backing this index (more than one index may be backed by
+      the same data */
+  private final IndexData _data;
+  /** 0-based index number */
+  private final int _indexNumber;
+  /** the type of the index */
+  private final byte _indexType;
+  /** Index name */
+  private String _name;
+  /** foreign key reference info, if any */
+  private final ForeignKeyReference _reference;
+  
+  protected IndexImpl(ByteBuffer tableBuffer, List<IndexData> indexDatas,
+                      JetFormat format) 
+    throws IOException
+  {
+
+    ByteUtil.forward(tableBuffer, format.SKIP_BEFORE_INDEX_SLOT); //Forward past Unknown
+    _indexNumber = tableBuffer.getInt();
+    int indexDataNumber = tableBuffer.getInt();
+      
+    // read foreign key reference info
+    byte relIndexType = tableBuffer.get();
+    int relIndexNumber = tableBuffer.getInt();
+    int relTablePageNumber = tableBuffer.getInt();
+    byte cascadeUpdatesFlag = tableBuffer.get();
+    byte cascadeDeletesFlag = tableBuffer.get();
+
+    _indexType = tableBuffer.get();
+    if((_indexType == FOREIGN_KEY_INDEX_TYPE) && 
+       (relIndexNumber != INVALID_INDEX_NUMBER)) {
+      _reference = new ForeignKeyReference(
+          relIndexType, relIndexNumber, relTablePageNumber,
+          (cascadeUpdatesFlag == CASCADE_UPDATES_FLAG),
+          (cascadeDeletesFlag == CASCADE_DELETES_FLAG));
+    } else {
+      _reference = null;
+    }
+
+    ByteUtil.forward(tableBuffer, format.SKIP_AFTER_INDEX_SLOT); //Skip past Unknown
+
+    _data = indexDatas.get(indexDataNumber);
+
+    _data.addIndex(this);
+  }
+
+  public IndexData getIndexData() {
+    return _data;
+  }
+
+  @Override
+  public TableImpl getTable() {
+    return getIndexData().getTable();
+  }
+  
+  public JetFormat getFormat() {
+    return getTable().getFormat();
+  }
+
+  public PageChannel getPageChannel() {
+    return getTable().getPageChannel();
+  }
+
+  public int getIndexNumber() {
+    return _indexNumber;
+  }
+
+  public byte getIndexFlags() {
+    return getIndexData().getIndexFlags();
+  }
+  
+  public int getUniqueEntryCount() {
+    return getIndexData().getUniqueEntryCount();
+  }
+
+  public int getUniqueEntryCountOffset() {
+    return getIndexData().getUniqueEntryCountOffset();
+  }
+
+  @Override
+  public String getName() {
+    return _name;
+  }
+  
+  public void setName(String name) {
+    _name = name;
+  }
+
+  @Override
+  public boolean isPrimaryKey() {
+    return _indexType == PRIMARY_KEY_INDEX_TYPE;
+  }
+
+  @Override
+  public boolean isForeignKey() {
+    return _indexType == FOREIGN_KEY_INDEX_TYPE;
+  }
+
+  public ForeignKeyReference getReference() {
+    return _reference;
+  }
+
+  @Override
+  public IndexImpl getReferencedIndex() throws IOException {
+
+    if(_reference == null) {
+      return null;
+    }
+
+    TableImpl refTable = getTable().getDatabase().getTable(
+        _reference.getOtherTablePageNumber());
+
+    if(refTable == null) {
+      throw new IOException("Reference to missing table " + 
+                            _reference.getOtherTablePageNumber());
+    }
+
+    IndexImpl refIndex = null;
+    int idxNumber = _reference.getOtherIndexNumber();
+    for(IndexImpl idx : refTable.getIndexes()) {
+      if(idx.getIndexNumber() == idxNumber) {
+        refIndex = idx;
+        break;
+      }
+    }
+    
+    if(refIndex == null) {
+      throw new IOException("Reference to missing index " + idxNumber + 
+                            " on table " + refTable.getName());
+    }
+
+    // finally verify that we found the expected index (should reference this
+    // index)
+    ForeignKeyReference otherRef = refIndex.getReference();
+    if((otherRef == null) ||
+       (otherRef.getOtherTablePageNumber() != 
+        getTable().getTableDefPageNumber()) ||
+       (otherRef.getOtherIndexNumber() != _indexNumber)) {
+      throw new IOException("Found unexpected index " + refIndex.getName() +
+                            " on table " + refTable.getName() +
+                            " with reference " + otherRef);
+    }
+
+    return refIndex;
+  }
+
+  @Override
+  public boolean shouldIgnoreNulls() {
+    return getIndexData().shouldIgnoreNulls();
+  }
+
+  @Override
+  public boolean isUnique() {
+    return getIndexData().isUnique();
+  }
+  
+  @Override
+  public List<IndexData.ColumnDescriptor> getColumns() {
+    return getIndexData().getColumns();
+  }
+
+  /**
+   * Whether or not the complete index state has been read.
+   */
+  public boolean isInitialized() {
+    return getIndexData().isInitialized();
+  }
+  
+  /**
+   * Forces initialization of this index (actual parsing of index pages).
+   * normally, the index will not be initialized until the entries are
+   * actually needed.
+   */
+  public void initialize() throws IOException {
+    getIndexData().initialize();
+  }
+
+  /**
+   * Writes the current index state to the database.
+   * <p>
+   * Forces index initialization.
+   */
+  public void update() throws IOException {
+    getIndexData().update();
+  }
+
+  /**
+   * Adds a row to this index
+   * <p>
+   * Forces index initialization.
+   * 
+   * @param row Row to add
+   * @param rowId rowId of the row to be added
+   */
+  public void addRow(Object[] row, RowId rowId)
+    throws IOException
+  {
+    getIndexData().addRow(row, rowId);
+  }
+  
+  /**
+   * Removes a row from this index
+   * <p>
+   * Forces index initialization.
+   * 
+   * @param row Row to remove
+   * @param rowId rowId of the row to be removed
+   */
+  public void deleteRow(Object[] row, RowId rowId)
+    throws IOException
+  {
+    getIndexData().deleteRow(row, rowId);
+  }
+      
+  /**
+   * Gets a new cursor for this index.
+   * <p>
+   * Forces index initialization.
+   */
+  public IndexData.EntryCursor cursor()
+    throws IOException
+  {
+    return cursor(null, true, null, true);
+  }
+  
+  /**
+   * Gets a new cursor for this index, narrowed to the range defined by the
+   * given startRow and endRow.
+   * <p>
+   * Forces index initialization.
+   * 
+   * @param startRow the first row of data for the cursor, or {@code null} for
+   *                 the first entry
+   * @param startInclusive whether or not startRow is inclusive or exclusive
+   * @param endRow the last row of data for the cursor, or {@code null} for
+   *               the last entry
+   * @param endInclusive whether or not endRow is inclusive or exclusive
+   */
+  public IndexData.EntryCursor cursor(Object[] startRow,
+                                      boolean startInclusive,
+                                      Object[] endRow,
+                                      boolean endInclusive)
+    throws IOException
+  {
+    return getIndexData().cursor(startRow, startInclusive, endRow,
+                                 endInclusive);
+  }
+
+  /**
+   * Constructs an array of values appropriate for this index from the given
+   * column values, expected to match the columns for this index.
+   * @return the appropriate sparse array of data
+   * @throws IllegalArgumentException if the wrong number of values are
+   *         provided
+   */
+  public Object[] constructIndexRowFromEntry(Object... values)
+  {
+    return getIndexData().constructIndexRowFromEntry(values);
+  }
+    
+  /**
+   * Constructs an array of values appropriate for this index from the given
+   * column value.
+   * @return the appropriate sparse array of data or {@code null} if not all
+   *         columns for this index were provided
+   */
+  public Object[] constructIndexRow(String colName, Object value)
+  {
+    return constructIndexRow(Collections.singletonMap(colName, value));
+  }
+  
+  /**
+   * Constructs an array of values appropriate for this index from the given
+   * column values.
+   * @return the appropriate sparse array of data or {@code null} if not all
+   *         columns for this index were provided
+   */
+  public Object[] constructIndexRow(Map<String,?> row)
+  {
+    return getIndexData().constructIndexRow(row);
+  }  
+
+  @Override
+  public String toString() {
+    StringBuilder rtn = new StringBuilder();
+    rtn.append("\tName: (").append(getTable().getName()).append(") ")
+      .append(_name);
+    rtn.append("\n\tNumber: ").append(_indexNumber);
+    rtn.append("\n\tIs Primary Key: ").append(isPrimaryKey());
+    rtn.append("\n\tIs Foreign Key: ").append(isForeignKey());
+    if(_reference != null) {
+      rtn.append("\n\tForeignKeyReference: ").append(_reference);
+    }
+    rtn.append(_data.toString());
+    rtn.append("\n\n");
+    return rtn.toString();
+  }
+  
+  public int compareTo(IndexImpl other) {
+    if (_indexNumber > other.getIndexNumber()) {
+      return 1;
+    } else if (_indexNumber < other.getIndexNumber()) {
+      return -1;
+    } else {
+      return 0;
+    }
+  }
+
+  /**
+   * Writes the logical index definitions into a table definition buffer.
+   * @param buffer Buffer to write to
+   * @param indexes List of IndexBuilders to write definitions for
+   */
+  protected static void writeDefinitions(
+      TableCreator creator, ByteBuffer buffer)
+    throws IOException
+  {
+    // write logical index information
+    for(IndexBuilder idx : creator.getIndexes()) {
+      TableCreator.IndexState idxState = creator.getIndexState(idx);
+      buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def
+      buffer.putInt(idxState.getIndexNumber()); // index num
+      buffer.putInt(idxState.getIndexDataNumber()); // index data num
+      buffer.put((byte)0); // related table type
+      buffer.putInt(INVALID_INDEX_NUMBER); // related index num
+      buffer.putInt(0); // related table definition page number
+      buffer.put((byte)0); // cascade updates flag
+      buffer.put((byte)0); // cascade deletes flag
+      buffer.put(idx.getType()); // index type flags
+      buffer.putInt(0); // unknown
+    }
+
+    // write index names
+    for(IndexBuilder idx : creator.getIndexes()) {
+      TableImpl.writeName(buffer, idx.getName(), creator.getCharset());
+    }
+  }
+
+  /**
+   * Information about a foreign key reference defined in an index (when
+   * referential integrity should be enforced).
+   */
+  public static class ForeignKeyReference
+  {
+    private final byte _tableType;
+    private final int _otherIndexNumber;
+    private final int _otherTablePageNumber;
+    private final boolean _cascadeUpdates;
+    private final boolean _cascadeDeletes;
+    
+    public ForeignKeyReference(
+        byte tableType, int otherIndexNumber, int otherTablePageNumber,
+        boolean cascadeUpdates, boolean cascadeDeletes)
+    {
+      _tableType = tableType;
+      _otherIndexNumber = otherIndexNumber;
+      _otherTablePageNumber = otherTablePageNumber;
+      _cascadeUpdates = cascadeUpdates;
+      _cascadeDeletes = cascadeDeletes;
+    }
+
+    public byte getTableType() {
+      return _tableType;
+    }
+
+    public boolean isPrimaryTable() {
+      return(getTableType() == PRIMARY_TABLE_TYPE);
+    }
+
+    public int getOtherIndexNumber() {
+      return _otherIndexNumber;
+    }
+
+    public int getOtherTablePageNumber() {
+      return _otherTablePageNumber;
+    }
+
+    public boolean isCascadeUpdates() {
+      return _cascadeUpdates;
+    }
+
+    public boolean isCascadeDeletes() {
+      return _cascadeDeletes;
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder()
+        .append("\n\t\tOther Index Number: ").append(_otherIndexNumber)
+        .append("\n\t\tOther Table Page Num: ").append(_otherTablePageNumber)
+        .append("\n\t\tIs Primary Table: ").append(isPrimaryTable())
+        .append("\n\t\tIs Cascade Updates: ").append(isCascadeUpdates())
+        .append("\n\t\tIs Cascade Deletes: ").append(isCascadeDeletes())
+        .toString();
+    }
+  }
+}
index dc3f4bae4172ef2413b22119eb38b34a7548fcd9..cf25e29384bf327ef234adf56056937f63170719 100644 (file)
@@ -34,12 +34,12 @@ import java.util.Map;
  */
 public class Joiner 
 {
-  private final Index _fromIndex;
+  private final IndexImpl _fromIndex;
   private final List<IndexData.ColumnDescriptor> _fromCols;
   private final IndexCursor _toCursor;
   private final Object[] _entryValues;
   
-  private Joiner(Index fromIndex, IndexCursor toCursor)
+  private Joiner(IndexImpl fromIndex, IndexCursor toCursor)
   {
     _fromIndex = fromIndex;
     _fromCols = _fromIndex.getColumns();
@@ -56,7 +56,7 @@ public class Joiner
    * @throws IllegalArgumentException if there is no relationship between the
    *         given tables
    */
-  public static Joiner create(Table fromTable, Table toTable)
+  public static Joiner create(TableImpl fromTable, TableImpl toTable)
     throws IOException
   {
     return create(fromTable.getForeignKeyIndex(toTable));
@@ -69,10 +69,10 @@ public class Joiner
    *
    * @param fromIndex the index backing one side of a foreign-key relationship
    */
-  public static Joiner create(Index fromIndex)
+  public static Joiner create(IndexImpl fromIndex)
     throws IOException
   {
-    Index toIndex = fromIndex.getReferencedIndex();
+    IndexImpl toIndex = fromIndex.getReferencedIndex();
     IndexCursor toCursor = IndexCursor.createCursor(
         toIndex.getTable(), toIndex);
     // text lookups are always case-insensitive
@@ -90,19 +90,19 @@ public class Joiner
     return create(getToTable(), getFromTable());
   }
   
-  public Table getFromTable() {
+  public TableImpl getFromTable() {
     return getFromIndex().getTable();
   }
   
-  public Index getFromIndex() {
+  public IndexImpl getFromIndex() {
     return _fromIndex;
   }
   
-  public Table getToTable() {
+  public TableImpl getToTable() {
     return getToCursor().getTable();
   }
   
-  public Index getToIndex() {
+  public IndexImpl getToIndex() {
     return getToCursor().getIndex();
   }
   
index 2c0c3fde067d5f9c8cd97a5a3263bdb17061516d..1b7a359c4c77d3c92dac6a20b0ca8cb455fc970e 100644 (file)
@@ -102,7 +102,7 @@ public abstract class Table implements Iterable<Map<String, Object>>
    * @return All of the Indexes on this table (unmodifiable List)
    * @usage _intermediate_method_
    */
-  public abstract List<Index> getIndexes();
+  public abstract List<? extends Index> getIndexes();
 
   /**
    * @return the index with the given name
index 35a50673fa296a886eb15ca55fad3cd4f24147e9..5459001c98c23589b0f6dc040c7a711fac229704 100644 (file)
@@ -130,7 +130,7 @@ public class TableImpl extends Table
   private List<Column> _autoNumColumns;
   /** List of indexes on this table (multiple logical indexes may be backed by
       the same index data) */
-  private final List<Index> _indexes = new ArrayList<Index>();
+  private final List<IndexImpl> _indexes = new ArrayList<IndexImpl>();
   /** List of index datas on this table (the actual backing data for an
       index) */
   private final List<IndexData> _indexDatas = new ArrayList<IndexData>();
@@ -362,13 +362,13 @@ public class TableImpl extends Table
   }
   
   @Override
-  public List<Index> getIndexes() {
+  public List<IndexImpl> getIndexes() {
     return Collections.unmodifiableList(_indexes);
   }
 
   @Override
-  public Index getIndex(String name) {
-    for(Index index : _indexes) {
+  public IndexImpl getIndex(String name) {
+    for(IndexImpl index : _indexes) {
       if(index.getName().equalsIgnoreCase(name)) {
         return index;
       }
@@ -378,8 +378,8 @@ public class TableImpl extends Table
   }
 
   @Override
-  public Index getPrimaryKeyIndex() {
-    for(Index index : _indexes) {
+  public IndexImpl getPrimaryKeyIndex() {
+    for(IndexImpl index : _indexes) {
       if(index.isPrimaryKey()) {
         return index;
       }
@@ -389,8 +389,8 @@ public class TableImpl extends Table
   }
   
   @Override
-  public Index getForeignKeyIndex(Table otherTable) {
-    for(Index index : _indexes) {
+  public IndexImpl getForeignKeyIndex(Table otherTable) {
+    for(IndexImpl index : _indexes) {
       if(index.isForeignKey() && (index.getReference() != null) &&
          (index.getReference().getOtherTablePageNumber() ==
           ((TableImpl)otherTable).getTableDefPageNumber())) {
@@ -940,7 +940,7 @@ public class TableImpl extends Table
     if(creator.hasIndexes()) {
       // index and index data definitions
       IndexData.writeDefinitions(creator, buffer);
-      Index.writeDefinitions(creator, buffer);
+      IndexImpl.writeDefinitions(creator, buffer);
     }
 
     //End of tabledef
@@ -1246,7 +1246,7 @@ public class TableImpl extends Table
 
     // read logical index info (may be more logical indexes than index datas)
     for (int i = 0; i < _logicalIndexCount; i++) {
-      _indexes.add(new Index(tableBuffer, _indexDatas, getFormat()));
+      _indexes.add(new IndexImpl(tableBuffer, _indexDatas, getFormat()));
     }
 
     // read logical index names