Refactor goals:
- simplify public API (separate "internal" and "external" api)
-- separate table creation objects from existing metadata objects
-- remove "simple" index support?
-- remove "table traversal methods" from Table?
-- enable integrity by default?
-- remove import/export methods from Database?
-- move database open/create options to DBBuilder
-- tweak how import filters work to make them more flexible?
+* separate table creation objects from existing metadata objects
+* remove "simple" index support?
+* remove "table traversal methods" from Table?
+* enable integrity by default?
+* remove import/export methods from Database?
+* move database open/create options to DBBuilder
+* tweak how import filters work to make them more flexible?
- tweak lookup apis (specify column vs column name)
- separate classes into more packages (api,builder,util,impl)
-- remove debug log blocks
+* remove debug log blocks
- add Row interface
- change savepoint to use table number instead of name?
-- don't use columnimpl for creating tables
- - clean up columnimpl/tableimpl constructors
+* don't use columnimpl for creating tables
+ * clean up columnimpl/tableimpl constructors
- add updateCurrentRow(Map), add updateRow(Row)
- sort out query types
import com.healthmarketscience.jackcess.complex.ComplexValue;
/**
+ * Access database column definition
*
* @author James Ahlborn
*/
/*
-Copyright (c) 2011 James Ahlborn
+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
package com.healthmarketscience.jackcess;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import com.healthmarketscience.jackcess.impl.DatabaseImpl;
-import com.healthmarketscience.jackcess.impl.PropertyMaps;
-import com.healthmarketscience.jackcess.impl.ByteUtil;
-
/**
- * Map of properties for a given database object.
+ * Map of properties for a database object.
*
* @author James Ahlborn
*/
-public class PropertyMap implements Iterable<PropertyMap.Property>
+public interface PropertyMap extends Iterable<PropertyMap.Property>
{
public static final String ACCESS_VERSION_PROP = "AccessVersion";
public static final String TITLE_PROP = "Title";
public static final String GUID_PROP = "GUID";
public static final String DESCRIPTION_PROP = "Description";
- private final String _mapName;
- private final short _mapType;
- private final Map<String,Property> _props =
- new LinkedHashMap<String,Property>();
-
- public PropertyMap(String name, short type) {
- _mapName = name;
- _mapType = type;
- }
-
- public String getName() {
- return _mapName;
- }
- public short getType() {
- return _mapType;
- }
+ public String getName();
- public int getSize() {
- return _props.size();
- }
+ public int getSize();
- public boolean isEmpty() {
- return _props.isEmpty();
- }
+ public boolean isEmpty();
/**
* @return the property with the given name, if any
*/
- public Property get(String name) {
- return _props.get(DatabaseImpl.toLookupName(name));
- }
+ public Property get(String name);
/**
* @return the value of the property with the given name, if any
*/
- public Object getValue(String name) {
- return getValue(name, null);
- }
+ public Object getValue(String name);
/**
* @return the value of the property with the given name, if any, otherwise
* the given defaultValue
*/
- public Object getValue(String name, Object defaultValue) {
- Property prop = get(name);
- Object value = defaultValue;
- if((prop != null) && (prop.getValue() != null)) {
- value = prop.getValue();
- }
- return value;
- }
-
- /**
- * Puts a property into this map with the given information.
- */
- public void put(String name, DataType type, byte flag, Object value) {
- _props.put(DatabaseImpl.toLookupName(name),
- new Property(name, type, flag, value));
- }
-
- public Iterator<Property> iterator() {
- return _props.values().iterator();
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(PropertyMaps.DEFAULT_NAME.equals(getName()) ?
- "<DEFAULT>" : getName())
- .append(" {");
- for(Iterator<Property> iter = iterator(); iter.hasNext(); ) {
- sb.append(iter.next());
- if(iter.hasNext()) {
- sb.append(",");
- }
- }
- sb.append("}");
- return sb.toString();
- }
+ public Object getValue(String name, Object defaultValue);
/**
* Info about a property defined in a PropertyMap.
*/
- public static final class Property
+ public interface Property
{
- private final String _name;
- private final DataType _type;
- private final byte _flag;
- private final Object _value;
-
- private Property(String name, DataType type, byte flag, Object value) {
- _name = name;
- _type = type;
- _flag = flag;
- _value = value;
- }
-
- public String getName() {
- return _name;
- }
-
- public DataType getType() {
- return _type;
- }
-
- public Object getValue() {
- return _value;
- }
-
- @Override
- public String toString() {
- Object val = getValue();
- if(val instanceof byte[]) {
- val = ByteUtil.toHexString((byte[])val);
- }
- return getName() + "[" + getType() + ":" + _flag + "]=" + val;
- }
- }
+ public String getName();
+
+ public DataType getType();
+
+ public Object getValue();
+
+ }
}
/*
-Copyright (c) 2008 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
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.util.Collections;
import java.util.List;
-import java.util.ArrayList;
/**
* Information about a relationship between two tables in the database.
*
* @author James Ahlborn
*/
-public class Relationship {
-
- /** flag indicating one-to-one relationship */
- private static final int ONE_TO_ONE_FLAG = 0x00000001;
- /** flag indicating no referential integrity */
- private static final int NO_REFERENTIAL_INTEGRITY_FLAG = 0x00000002;
- /** flag indicating cascading updates (requires referential integrity) */
- private static final int CASCADE_UPDATES_FLAG = 0x00000100;
- /** flag indicating cascading deletes (requires referential integrity) */
- private static final int CASCADE_DELETES_FLAG = 0x00001000;
- /** flag indicating left outer join */
- private static final int LEFT_OUTER_JOIN_FLAG = 0x01000000;
- /** flag indicating right outer join */
- private static final int RIGHT_OUTER_JOIN_FLAG = 0x02000000;
-
- /** the name of this relationship */
- private final String _name;
- /** the "from" table in this relationship */
- private final Table _fromTable;
- /** the "to" table in this relationship */
- private final Table _toTable;
- /** the columns in the "from" table in this relationship (aligned w/
- toColumns list) */
- private final List<Column> _toColumns;
- /** the columns in the "to" table in this relationship (aligned w/
- toColumns list) */
- private final List<Column> _fromColumns;
- /** the various flags describing this relationship */
- private final int _flags;
-
- public Relationship(String name, Table fromTable, Table toTable, int flags,
- int numCols)
- {
- _name = name;
- _fromTable = fromTable;
- _fromColumns = new ArrayList<Column>(
- Collections.nCopies(numCols, (Column)null));
- _toTable = toTable;
- _toColumns = new ArrayList<Column>(
- Collections.nCopies(numCols, (Column)null));
- _flags = flags;
- }
-
- public String getName() {
- return _name;
- }
+public interface Relationship
+{
+ public String getName();
- public Table getFromTable() {
- return _fromTable;
- }
-
- public List<Column> getFromColumns() {
- return _fromColumns;
- }
+ public Table getFromTable();
- public Table getToTable() {
- return _toTable;
- }
+ public List<Column> getFromColumns();
- public List<Column> getToColumns() {
- return _toColumns;
- }
+ public Table getToTable();
- public int getFlags() {
- return _flags;
- }
+ public List<Column> getToColumns();
- public boolean isOneToOne() {
- return hasFlag(ONE_TO_ONE_FLAG);
- }
+ public boolean isOneToOne();
- public boolean hasReferentialIntegrity() {
- return !hasFlag(NO_REFERENTIAL_INTEGRITY_FLAG);
- }
+ public boolean hasReferentialIntegrity();
- public boolean cascadeUpdates() {
- return hasFlag(CASCADE_UPDATES_FLAG);
- }
+ public boolean cascadeUpdates();
- public boolean cascadeDeletes() {
- return hasFlag(CASCADE_DELETES_FLAG);
- }
+ public boolean cascadeDeletes();
- public boolean isLeftOuterJoin() {
- return hasFlag(LEFT_OUTER_JOIN_FLAG);
- }
+ public boolean isLeftOuterJoin();
- public boolean isRightOuterJoin() {
- return hasFlag(RIGHT_OUTER_JOIN_FLAG);
- }
-
- private boolean hasFlag(int flagMask) {
- return((getFlags() & flagMask) != 0);
- }
-
- @Override
- public String toString() {
- StringBuilder rtn = new StringBuilder();
- rtn.append("\tName: " + _name);
- rtn.append("\n\tFromTable: " + _fromTable.getName());
- rtn.append("\n\tFromColumns: " + _fromColumns);
- rtn.append("\n\tToTable: " + _toTable.getName());
- rtn.append("\n\tToColumns: " + _toColumns);
- rtn.append("\n\tFlags: " + Integer.toHexString(_flags));
- rtn.append("\n\n");
- return rtn.toString();
- }
-
+ public boolean isRightOuterJoin();
}
package com.healthmarketscience.jackcess;
/**
- * Uniquely identifies a row of data within the access database.
+ * Uniquely identifies a row of data within the access database. While RowIds
+ * are largely opaque identifiers, they are comparable to each other (within
+ * the same table) and have valid {@link #equals}, {@link #hashCode} and
+ * {@link #toString} methods.
*
* @author James Ahlborn
*/
import com.healthmarketscience.jackcess.util.ErrorHandler;
/**
+ * A single database table
+ * <p>
+ * Is not thread-safe.
*
* @author James Ahlborn
* @usage _general_class_
// new relationship
int numCols = (Integer)row.get(REL_COL_COLUMN_COUNT);
int flags = (Integer)row.get(REL_COL_FLAGS);
- rel = new Relationship(relName, fromTable, toTable,
- flags, numCols);
+ rel = new RelationshipImpl(relName, fromTable, toTable,
+ flags, numCols);
relationships.add(rel);
}
--- /dev/null
+/*
+Copyright (c) 2011 James Ahlborn
+
+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
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.PropertyMap;
+
+/**
+ * Map of properties for a database object.
+ *
+ * @author James Ahlborn
+ */
+public class PropertyMapImpl implements PropertyMap
+{
+ private final String _mapName;
+ private final short _mapType;
+ private final Map<String,Property> _props =
+ new LinkedHashMap<String,Property>();
+
+ public PropertyMapImpl(String name, short type) {
+ _mapName = name;
+ _mapType = type;
+ }
+
+ public String getName() {
+ return _mapName;
+ }
+
+ public short getType() {
+ return _mapType;
+ }
+
+ public int getSize() {
+ return _props.size();
+ }
+
+ public boolean isEmpty() {
+ return _props.isEmpty();
+ }
+
+ public Property get(String name) {
+ return _props.get(DatabaseImpl.toLookupName(name));
+ }
+
+ public Object getValue(String name) {
+ return getValue(name, null);
+ }
+
+ public Object getValue(String name, Object defaultValue) {
+ Property prop = get(name);
+ Object value = defaultValue;
+ if((prop != null) && (prop.getValue() != null)) {
+ value = prop.getValue();
+ }
+ return value;
+ }
+
+ /**
+ * Puts a property into this map with the given information.
+ */
+ public void put(String name, DataType type, byte flag, Object value) {
+ _props.put(DatabaseImpl.toLookupName(name),
+ new PropertyImpl(name, type, flag, value));
+ }
+
+ public Iterator<Property> iterator() {
+ return _props.values().iterator();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(PropertyMaps.DEFAULT_NAME.equals(getName()) ?
+ "<DEFAULT>" : getName())
+ .append(" {");
+ for(Iterator<Property> iter = iterator(); iter.hasNext(); ) {
+ sb.append(iter.next());
+ if(iter.hasNext()) {
+ sb.append(",");
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Info about a property defined in a PropertyMap.
+ */
+ private static final class PropertyImpl implements PropertyMap.Property
+ {
+ private final String _name;
+ private final DataType _type;
+ private final byte _flag;
+ private final Object _value;
+
+ private PropertyImpl(String name, DataType type, byte flag, Object value) {
+ _name = name;
+ _type = type;
+ _flag = flag;
+ _value = value;
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public DataType getType() {
+ return _type;
+ }
+
+ public Object getValue() {
+ return _value;
+ }
+
+ @Override
+ public String toString() {
+ Object val = getValue();
+ if(val instanceof byte[]) {
+ val = ByteUtil.toHexString((byte[])val);
+ }
+ return getName() + "[" + getType() + ":" + _flag + "]=" + val;
+ }
+ }
+
+}
*
* @author James Ahlborn
*/
-public class PropertyMaps implements Iterable<PropertyMap>
+public class PropertyMaps implements Iterable<PropertyMapImpl>
{
/** the name of the "default" properties for a PropertyMaps instance */
public static final String DEFAULT_NAME = "";
/** maps the PropertyMap name (case-insensitive) to the PropertyMap
instance */
- private final Map<String,PropertyMap> _maps =
- new LinkedHashMap<String,PropertyMap>();
+ private final Map<String,PropertyMapImpl> _maps =
+ new LinkedHashMap<String,PropertyMapImpl>();
private final int _objectId;
public PropertyMaps(int objectId) {
* @return the unnamed "default" PropertyMap in this group, creating if
* necessary.
*/
- public PropertyMap getDefault() {
+ public PropertyMapImpl getDefault() {
return get(DEFAULT_NAME, DEFAULT_PROPERTY_VALUE_LIST);
}
* @return the PropertyMap with the given name in this group, creating if
* necessary
*/
- public PropertyMap get(String name) {
+ public PropertyMapImpl get(String name) {
return get(name, COLUMN_PROPERTY_VALUE_LIST);
}
* @return the PropertyMap with the given name and type in this group,
* creating if necessary
*/
- private PropertyMap get(String name, short type) {
+ private PropertyMapImpl get(String name, short type) {
String lookupName = DatabaseImpl.toLookupName(name);
- PropertyMap map = _maps.get(lookupName);
+ PropertyMapImpl map = _maps.get(lookupName);
if(map == null) {
- map = new PropertyMap(name, type);
+ map = new PropertyMapImpl(name, type);
_maps.put(lookupName, map);
}
return map;
/**
* Adds the given PropertyMap to this group.
*/
- public void put(PropertyMap map) {
+ public void put(PropertyMapImpl map) {
_maps.put(DatabaseImpl.toLookupName(map.getName()), map);
}
- public Iterator<PropertyMap> iterator() {
+ public Iterator<PropertyMapImpl> iterator() {
return _maps.values().iterator();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- for(Iterator<PropertyMap> iter = iterator(); iter.hasNext(); ) {
+ for(Iterator<PropertyMapImpl> iter = iterator(); iter.hasNext(); ) {
sb.append(iter.next());
if(iter.hasNext()) {
sb.append("\n");
* @return the PropertyMap created from the values parsed from the given
* data chunk combined with the given property names
*/
- private PropertyMap readPropertyValues(
+ private PropertyMapImpl readPropertyValues(
ByteBuffer bbBlock, List<String> propNames, short blockType)
throws IOException
{
bbBlock.position(endPos);
}
- PropertyMap map = new PropertyMap(mapName, blockType);
+ PropertyMapImpl map = new PropertyMapImpl(mapName, blockType);
// read the values
while(bbBlock.hasRemaining()) {
return col;
}
- private boolean isPseudoGuidColumn(DataType dataType, String propName,
- int dataSize) {
+ private static boolean isPseudoGuidColumn(
+ DataType dataType, String propName, int dataSize) {
// guids seem to be marked as "binary" fields
return((dataType == DataType.BINARY) &&
(dataSize == DataType.GUID.getFixedSize()) &&
--- /dev/null
+/*
+Copyright (c) 2008 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.impl;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+
+import com.healthmarketscience.jackcess.Column;
+import com.healthmarketscience.jackcess.Relationship;
+import com.healthmarketscience.jackcess.Table;
+
+/**
+ * Information about a relationship between two tables in the database.
+ *
+ * @author James Ahlborn
+ */
+public class RelationshipImpl implements Relationship
+{
+
+ /** flag indicating one-to-one relationship */
+ private static final int ONE_TO_ONE_FLAG = 0x00000001;
+ /** flag indicating no referential integrity */
+ private static final int NO_REFERENTIAL_INTEGRITY_FLAG = 0x00000002;
+ /** flag indicating cascading updates (requires referential integrity) */
+ private static final int CASCADE_UPDATES_FLAG = 0x00000100;
+ /** flag indicating cascading deletes (requires referential integrity) */
+ private static final int CASCADE_DELETES_FLAG = 0x00001000;
+ /** flag indicating left outer join */
+ private static final int LEFT_OUTER_JOIN_FLAG = 0x01000000;
+ /** flag indicating right outer join */
+ private static final int RIGHT_OUTER_JOIN_FLAG = 0x02000000;
+
+ /** the name of this relationship */
+ private final String _name;
+ /** the "from" table in this relationship */
+ private final Table _fromTable;
+ /** the "to" table in this relationship */
+ private final Table _toTable;
+ /** the columns in the "from" table in this relationship (aligned w/
+ toColumns list) */
+ private final List<Column> _toColumns;
+ /** the columns in the "to" table in this relationship (aligned w/
+ toColumns list) */
+ private final List<Column> _fromColumns;
+ /** the various flags describing this relationship */
+ private final int _flags;
+
+ public RelationshipImpl(String name, Table fromTable, Table toTable, int flags,
+ int numCols)
+ {
+ _name = name;
+ _fromTable = fromTable;
+ _fromColumns = new ArrayList<Column>(
+ Collections.nCopies(numCols, (Column)null));
+ _toTable = toTable;
+ _toColumns = new ArrayList<Column>(
+ Collections.nCopies(numCols, (Column)null));
+ _flags = flags;
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public Table getFromTable() {
+ return _fromTable;
+ }
+
+ public List<Column> getFromColumns() {
+ return _fromColumns;
+ }
+
+ public Table getToTable() {
+ return _toTable;
+ }
+
+ public List<Column> getToColumns() {
+ return _toColumns;
+ }
+
+ public int getFlags() {
+ return _flags;
+ }
+
+ public boolean isOneToOne() {
+ return hasFlag(ONE_TO_ONE_FLAG);
+ }
+
+ public boolean hasReferentialIntegrity() {
+ return !hasFlag(NO_REFERENTIAL_INTEGRITY_FLAG);
+ }
+
+ public boolean cascadeUpdates() {
+ return hasFlag(CASCADE_UPDATES_FLAG);
+ }
+
+ public boolean cascadeDeletes() {
+ return hasFlag(CASCADE_DELETES_FLAG);
+ }
+
+ public boolean isLeftOuterJoin() {
+ return hasFlag(LEFT_OUTER_JOIN_FLAG);
+ }
+
+ public boolean isRightOuterJoin() {
+ return hasFlag(RIGHT_OUTER_JOIN_FLAG);
+ }
+
+ private boolean hasFlag(int flagMask) {
+ return((getFlags() & flagMask) != 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder rtn = new StringBuilder();
+ rtn.append("\tName: " + _name);
+ rtn.append("\n\tFromTable: " + _fromTable.getName());
+ rtn.append("\n\tFromColumns: " + _fromColumns);
+ rtn.append("\n\tToTable: " + _toTable.getName());
+ rtn.append("\n\tToColumns: " + _toColumns);
+ rtn.append("\n\tFlags: " + Integer.toHexString(_flags));
+ rtn.append("\n\n");
+ return rtn.toString();
+ }
+
+}
import java.util.Map;
import junit.framework.TestCase;
+import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
import com.healthmarketscience.jackcess.impl.PropertyMaps;
import static com.healthmarketscience.jackcess.Database.*;
import static com.healthmarketscience.jackcess.DatabaseTest.*;
assertFalse(maps.iterator().hasNext());
assertEquals(10, maps.getObjectId());
- PropertyMap defMap = maps.getDefault();
+ PropertyMapImpl defMap = maps.getDefault();
assertTrue(defMap.isEmpty());
assertEquals(0, defMap.getSize());
assertFalse(defMap.iterator().hasNext());
- PropertyMap colMap = maps.get("testcol");
+ PropertyMapImpl colMap = maps.get("testcol");
assertTrue(colMap.isEmpty());
assertEquals(0, colMap.getSize());
assertFalse(colMap.iterator().hasNext());
import static com.healthmarketscience.jackcess.DatabaseTest.*;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
+import com.healthmarketscience.jackcess.impl.RelationshipImpl;
/**
* @author James Ahlborn
assertEquals(Arrays.asList(t1.getColumn("otherfk1")),
rel.getToColumns());
assertTrue(rel.hasReferentialIntegrity());
- assertEquals(4096, rel.getFlags());
+ assertEquals(4096, ((RelationshipImpl)rel).getFlags());
assertTrue(rel.cascadeDeletes());
assertSameRelationships(rels, db.getRelationships(t2, t1));
assertEquals(Arrays.asList(t1.getColumn("otherfk2")),
rel.getToColumns());
assertTrue(rel.hasReferentialIntegrity());
- assertEquals(256, rel.getFlags());
+ assertEquals(256, ((RelationshipImpl)rel).getFlags());
assertTrue(rel.cascadeUpdates());
assertSameRelationships(rels, db.getRelationships(t3, t1));