From: James Ahlborn Date: Thu, 21 Mar 2013 01:36:28 +0000 (+0000) Subject: move complex type impls to impl X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2e1973dae17b318ea8594c98b7ea0acceae1984f;p=jackcess.git move complex type impls to impl git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/jackcess-2@699 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/java/com/healthmarketscience/jackcess/complex/AttachmentColumnInfo.java b/src/java/com/healthmarketscience/jackcess/complex/AttachmentColumnInfo.java index f715d7c..f2f605a 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/AttachmentColumnInfo.java +++ b/src/java/com/healthmarketscience/jackcess/complex/AttachmentColumnInfo.java @@ -1,5 +1,5 @@ /* -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 @@ -19,256 +19,12 @@ USA package com.healthmarketscience.jackcess.complex; -import java.io.IOException; -import java.util.Date; - -import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.Row; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.impl.ByteUtil; - - /** * Complex column info for a column holding 0 or more attachments per row. * * @author James Ahlborn */ -public class AttachmentColumnInfo extends ComplexColumnInfo +public interface AttachmentColumnInfo extends ComplexColumnInfo { - private static final String FILE_NAME_COL_NAME = "FileName"; - private static final String FILE_TYPE_COL_NAME = "FileType"; - - private final Column _fileUrlCol; - private final Column _fileNameCol; - private final Column _fileTypeCol; - private final Column _fileDataCol; - private final Column _fileTimeStampCol; - private final Column _fileFlagsCol; - - public AttachmentColumnInfo(Column column, int complexId, - Table typeObjTable, Table flatTable) - throws IOException - { - super(column, complexId, typeObjTable, flatTable); - - Column fileUrlCol = null; - Column fileNameCol = null; - Column fileTypeCol = null; - Column fileDataCol = null; - Column fileTimeStampCol = null; - Column fileFlagsCol = null; - - for(Column col : getTypeColumns()) { - switch(col.getType()) { - case TEXT: - if(FILE_NAME_COL_NAME.equalsIgnoreCase(col.getName())) { - fileNameCol = col; - } else if(FILE_TYPE_COL_NAME.equalsIgnoreCase(col.getName())) { - fileTypeCol = col; - } else { - // if names don't match, assign in order: name, type - if(fileNameCol == null) { - fileNameCol = col; - } else if(fileTypeCol == null) { - fileTypeCol = col; - } - } - break; - case LONG: - fileFlagsCol = col; - break; - case SHORT_DATE_TIME: - fileTimeStampCol = col; - break; - case OLE: - fileDataCol = col; - break; - case MEMO: - fileUrlCol = col; - break; - default: - // ignore - } - } - - _fileUrlCol = fileUrlCol; - _fileNameCol = fileNameCol; - _fileTypeCol = fileTypeCol; - _fileDataCol = fileDataCol; - _fileTimeStampCol = fileTimeStampCol; - _fileFlagsCol = fileFlagsCol; - } - - public Column getFileUrlColumn() { - return _fileUrlCol; - } - - public Column getFileNameColumn() { - return _fileNameCol; - } - - public Column getFileTypeColumn() { - return _fileTypeCol; - } - - public Column getFileDataColumn() { - return _fileDataCol; - } - - public Column getFileTimeStampColumn() { - return _fileTimeStampCol; - } - - public Column getFileFlagsColumn() { - return _fileFlagsCol; - } - - @Override - public ComplexDataType getType() - { - return ComplexDataType.ATTACHMENT; - } - - @Override - protected AttachmentImpl toValue(ComplexValueForeignKey complexValueFk, - Row rawValue) { - ComplexValue.Id id = getValueId(rawValue); - String url = (String)getFileUrlColumn().getRowValue(rawValue); - String name = (String)getFileNameColumn().getRowValue(rawValue); - String type = (String)getFileTypeColumn().getRowValue(rawValue); - Integer flags = (Integer)getFileFlagsColumn().getRowValue(rawValue); - Date ts = (Date)getFileTimeStampColumn().getRowValue(rawValue); - byte[] data = (byte[])getFileDataColumn().getRowValue(rawValue); - - return new AttachmentImpl(id, complexValueFk, url, name, type, data, - ts, flags); - } - - @Override - protected Object[] asRow(Object[] row, Attachment attachment) { - super.asRow(row, attachment); - getFileUrlColumn().setRowValue(row, attachment.getFileUrl()); - getFileNameColumn().setRowValue(row, attachment.getFileName()); - getFileTypeColumn().setRowValue(row, attachment.getFileType()); - getFileFlagsColumn().setRowValue(row, attachment.getFileFlags()); - getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp()); - getFileDataColumn().setRowValue(row, attachment.getFileData()); - return row; - } - - public static Attachment newAttachment(byte[] data) { - return newAttachment(INVALID_FK, data); - } - - public static Attachment newAttachment(ComplexValueForeignKey complexValueFk, - byte[] data) { - return newAttachment(complexValueFk, null, null, null, data, null, null); - } - - public static Attachment newAttachment( - String url, String name, String type, byte[] data, - Date timeStamp, Integer flags) - { - return newAttachment(INVALID_FK, url, name, type, data, - timeStamp, flags); - } - - public static Attachment newAttachment( - ComplexValueForeignKey complexValueFk, String url, String name, - String type, byte[] data, Date timeStamp, Integer flags) - { - return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type, - data, timeStamp, flags); - } - - - private static class AttachmentImpl extends ComplexValueImpl - implements Attachment - { - private String _url; - private String _name; - private String _type; - private byte[] _data; - private Date _timeStamp; - private Integer _flags; - - private AttachmentImpl(Id id, ComplexValueForeignKey complexValueFk, - String url, String name, String type, byte[] data, - Date timeStamp, Integer flags) - { - super(id, complexValueFk); - _url = url; - _name = name; - _type = type; - _data = data; - _timeStamp = timeStamp; - _flags = flags; - } - - public byte[] getFileData() { - return _data; - } - - public void setFileData(byte[] data) { - _data = data; - } - - public String getFileName() { - return _name; - } - - public void setFileName(String fileName) { - _name = fileName; - } - - public String getFileUrl() { - return _url; - } - - public void setFileUrl(String fileUrl) { - _url = fileUrl; - } - - public String getFileType() { - return _type; - } - - public void setFileType(String fileType) { - _type = fileType; - } - - public Date getFileTimeStamp() { - return _timeStamp; - } - - public void setFileTimeStamp(Date fileTimeStamp) { - _timeStamp = fileTimeStamp; - } - - public Integer getFileFlags() { - return _flags; - } - - public void setFileFlags(Integer fileFlags) { - _flags = fileFlags; - } - public void update() throws IOException { - getComplexValueForeignKey().updateAttachment(this); - } - - public void delete() throws IOException { - getComplexValueForeignKey().deleteAttachment(this); - } - - @Override - public String toString() - { - return "Attachment(" + getComplexValueForeignKey() + "," + getId() + - ") " + getFileUrl() + ", " + getFileName() + ", " + getFileType() - + ", " + getFileTimeStamp() + ", " + getFileFlags() + ", " + - ByteUtil.toHexString(getFileData()); - } - } - } diff --git a/src/java/com/healthmarketscience/jackcess/complex/ComplexColumnInfo.java b/src/java/com/healthmarketscience/jackcess/complex/ComplexColumnInfo.java index 3b9ab66..14851f6 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/ComplexColumnInfo.java +++ b/src/java/com/healthmarketscience/jackcess/complex/ComplexColumnInfo.java @@ -1,5 +1,5 @@ /* -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 @@ -20,441 +20,55 @@ USA package com.healthmarketscience.jackcess.complex; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; -import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.CursorBuilder; -import com.healthmarketscience.jackcess.DataType; -import com.healthmarketscience.jackcess.Database; -import com.healthmarketscience.jackcess.IndexCursor; import com.healthmarketscience.jackcess.Row; -import com.healthmarketscience.jackcess.RowId; -import com.healthmarketscience.jackcess.RuntimeIOException; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.impl.ColumnImpl; -import com.healthmarketscience.jackcess.impl.TableImpl; /** * Base class for the additional information tracked for complex columns. * * @author James Ahlborn */ -public abstract class ComplexColumnInfo +public interface ComplexColumnInfo { - private static final int INVALID_ID_VALUE = -1; - public static final ComplexValue.Id INVALID_ID = new ComplexValueIdImpl( - INVALID_ID_VALUE, null); - public static final ComplexValueForeignKey INVALID_FK = - new ComplexValueForeignKey(null, INVALID_ID_VALUE); + public ComplexDataType getType(); - private final Column _column; - private final int _complexTypeId; - private final Table _flatTable; - private final List _typeCols; - private final Column _pkCol; - private final Column _complexValFkCol; - private IndexCursor _complexValIdCursor; - - protected ComplexColumnInfo(Column column, int complexTypeId, - Table typeObjTable, Table flatTable) - throws IOException - { - _column = column; - _complexTypeId = complexTypeId; - _flatTable = flatTable; - - // the flat table has all the "value" columns and 2 extra columns, a - // primary key for each row, and a LONG value which is essentially a - // foreign key to the main table. - List typeCols = new ArrayList(); - List otherCols = new ArrayList(); - diffFlatColumns(typeObjTable, flatTable, typeCols, otherCols); + public int countValues(int complexValueFk) throws IOException; - _typeCols = Collections.unmodifiableList(typeCols); - - Column pkCol = null; - Column complexValFkCol = null; - for(Column col : otherCols) { - if(col.isAutoNumber()) { - pkCol = col; - } else if(col.getType() == DataType.LONG) { - complexValFkCol = col; - } - } - - if((pkCol == null) || (complexValFkCol == null)) { - throw new IOException("Could not find expected columns in flat table " + - flatTable.getName() + " for complex column with id " - + complexTypeId); - } - _pkCol = pkCol; - _complexValFkCol = complexValFkCol; - } - - public void postTableLoadInit() throws IOException { - // nothing to do in base class - } - - public Column getColumn() { - return _column; - } - - public Database getDatabase() { - return getColumn().getDatabase(); - } - - public Column getPrimaryKeyColumn() { - return _pkCol; - } - - public Column getComplexValueForeignKeyColumn() { - return _complexValFkCol; - } - - protected List getTypeColumns() { - return _typeCols; - } - - public int countValues(int complexValueFk) throws IOException { - return getRawValues(complexValueFk, - Collections.singleton(_complexValFkCol.getName())) - .size(); - } - public List getRawValues(int complexValueFk) - throws IOException - { - return getRawValues(complexValueFk, null); - } - - private Iterator getComplexValFkIter( - int complexValueFk, Collection columnNames) - throws IOException - { - if(_complexValIdCursor == null) { - _complexValIdCursor = new CursorBuilder(_flatTable) - .setIndexByColumns(_complexValFkCol) - .toIndexCursor(); - } + throws IOException; - return _complexValIdCursor.entryIterator(columnNames, complexValueFk); - } - public List getRawValues(int complexValueFk, Collection columnNames) - throws IOException - { - Iterator entryIter = - getComplexValFkIter(complexValueFk, columnNames); - if(!entryIter.hasNext()) { - return Collections.emptyList(); - } - - List values = new ArrayList(); - while(entryIter.hasNext()) { - values.add(entryIter.next()); - } - - return values; - } + throws IOException; public List getValues(ComplexValueForeignKey complexValueFk) - throws IOException - { - List rawValues = getRawValues(complexValueFk.get()); - if(rawValues.isEmpty()) { - return Collections.emptyList(); - } - - return toValues(complexValueFk, rawValues); - } - - protected List toValues(ComplexValueForeignKey complexValueFk, - List rawValues) - throws IOException - { - List values = new ArrayList(); - for(Row rawValue : rawValues) { - values.add(toValue(complexValueFk, rawValue)); - } - - return values; - } + throws IOException; public ComplexValue.Id addRawValue(Map rawValue) - throws IOException - { - Object[] row = ((TableImpl)_flatTable).asRowWithRowId(rawValue); - _flatTable.addRow(row); - return getValueId(row); - } - - public ComplexValue.Id addValue(V value) throws IOException { - Object[] row = asRow(newRowArray(), value); - _flatTable.addRow(row); - ComplexValue.Id id = getValueId(row); - value.setId(id); - return id; - } - - public void addValues(Collection values) throws IOException { - for(V value : values) { - addValue(value); - } - } - - public ComplexValue.Id updateRawValue(Row rawValue) throws IOException { - _flatTable.updateRow(rawValue); - return getValueId(rawValue); - } - - public ComplexValue.Id updateValue(V value) throws IOException { - ComplexValue.Id id = value.getId(); - updateRow(id, asRow(newRowArray(), value)); - return id; - } - - public void updateValues(Collection values) throws IOException { - for(V value : values) { - updateValue(value); - } - } - - public void deleteRawValue(Row rawValue) throws IOException { - deleteRow(rawValue.getId()); - } - - public void deleteValue(V value) throws IOException { - deleteRow(value.getId().getRowId()); - } - - public void deleteValues(Collection values) throws IOException { - for(V value : values) { - deleteValue(value); - } - } - - public void deleteAllValues(int complexValueFk) throws IOException { - Iterator entryIter = - getComplexValFkIter(complexValueFk, Collections.emptySet()); - try { - while(entryIter.hasNext()) { - entryIter.next(); - entryIter.remove(); - } - } catch(RuntimeIOException e) { - throw (IOException)e.getCause(); - } - } - - public void deleteAllValues(ComplexValueForeignKey complexValueFk) - throws IOException - { - deleteAllValues(complexValueFk.get()); - } - - private void updateRow(ComplexValue.Id id, Object[] row) throws IOException { - ((TableImpl)_flatTable).updateRow(id.getRowId(), row); - } - - private void deleteRow(RowId rowId) throws IOException { - ((TableImpl)_flatTable).deleteRow(rowId); - } - - protected ComplexValueIdImpl getValueId(Row row) { - int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row); - return new ComplexValueIdImpl(idVal, row.getId()); - } - - protected ComplexValueIdImpl getValueId(Object[] row) { - int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row); - return new ComplexValueIdImpl(idVal, - ((TableImpl)_flatTable).getRowId(row)); - } - - protected Object[] asRow(Object[] row, V value) { - ComplexValue.Id id = value.getId(); - _pkCol.setRowValue( - row, ((id != INVALID_ID) ? id : Column.AUTO_NUMBER)); - ComplexValueForeignKey cFk = value.getComplexValueForeignKey(); - _complexValFkCol.setRowValue( - row, ((cFk != INVALID_FK) ? cFk : Column.AUTO_NUMBER)); - return row; - } - - private Object[] newRowArray() { - Object[] row = new Object[_flatTable.getColumnCount() + 1]; - row[row.length - 1] = ColumnImpl.RETURN_ROW_ID; - return row; - } - - @Override - public String toString() { - StringBuilder rtn = new StringBuilder(); - rtn.append("\n\t\tComplexType: " + getType()); - rtn.append("\n\t\tComplexTypeId: " + _complexTypeId); - return rtn.toString(); - } - - protected static void diffFlatColumns(Table typeObjTable, - Table flatTable, - List typeCols, - List otherCols) - { - // each "flat"" table has the columns from the "type" table, plus some - // others. separate the "flat" columns into these 2 buckets - for(Column col : flatTable.getColumns()) { - if(((TableImpl)typeObjTable).hasColumn(col.getName())) { - typeCols.add(col); - } else { - otherCols.add(col); - } - } - } - - public abstract ComplexDataType getType(); - - protected abstract V toValue( - ComplexValueForeignKey complexValueFk, - Row rawValues) throws IOException; - - protected static abstract class ComplexValueImpl implements ComplexValue - { - private Id _id; - private ComplexValueForeignKey _complexValueFk; - protected ComplexValueImpl(Id id, ComplexValueForeignKey complexValueFk) { - _id = id; - _complexValueFk = complexValueFk; - } + public ComplexValue.Id addValue(V value) throws IOException; - public Id getId() { - return _id; - } + public void addValues(Collection values) throws IOException; - public void setId(Id id) { - if(_id == id) { - // harmless, ignore - return; - } - if(_id != INVALID_ID) { - throw new IllegalStateException("id may not be reset"); - } - _id = id; - } - - public ComplexValueForeignKey getComplexValueForeignKey() { - return _complexValueFk; - } + public ComplexValue.Id updateRawValue(Row rawValue) throws IOException; - public void setComplexValueForeignKey(ComplexValueForeignKey complexValueFk) - { - if(_complexValueFk == complexValueFk) { - // harmless, ignore - return; - } - if(_complexValueFk != INVALID_FK) { - throw new IllegalStateException("complexValueFk may not be reset"); - } - _complexValueFk = complexValueFk; - } + public ComplexValue.Id updateValue(V value) throws IOException; - public Column getColumn() { - return _complexValueFk.getColumn(); - } - - @Override - public int hashCode() { - return ((_id.get() * 37) ^ _complexValueFk.hashCode()); - } + public void updateValues(Collection values) throws IOException; - @Override - public boolean equals(Object o) { - return ((this == o) || - ((o != null) && (getClass() == o.getClass()) && - (_id == ((ComplexValueImpl)o)._id) && - _complexValueFk.equals(((ComplexValueImpl)o)._complexValueFk))); - } - } + public void deleteRawValue(Row rawValue) throws IOException; - /** - * Implementation of ComplexValue.Id. - */ - private static final class ComplexValueIdImpl extends ComplexValue.Id - { - private static final long serialVersionUID = 20130318L; + public void deleteValue(V value) throws IOException; - private final int _value; - private final RowId _rowId; + public void deleteValues(Collection values) throws IOException; - protected ComplexValueIdImpl(int value, RowId rowId) { - _value = value; - _rowId = rowId; - } - - @Override - public int get() { - return _value; - } + public void deleteAllValues(int complexValueFk) throws IOException; - @Override - public RowId getRowId() { - return _rowId; - } - - @Override - public byte byteValue() { - return (byte)get(); - } - - @Override - public short shortValue() { - return (short)get(); - } - - @Override - public int intValue() { - return get(); - } - - @Override - public long longValue() { - return get(); - } - - @Override - public float floatValue() { - return get(); - } - - @Override - public double doubleValue() { - return get(); - } - - @Override - public int hashCode() { - return _value; - } - - @Override - public boolean equals(Object o) { - return ((this == o) || - ((o != null) && (getClass() == o.getClass()) && - (_value == ((ComplexValueIdImpl)o)._value))); - } + public void deleteAllValues(ComplexValueForeignKey complexValueFk) + throws IOException; - @Override - public String toString() - { - return String.valueOf(_value); - } - } - } diff --git a/src/java/com/healthmarketscience/jackcess/complex/ComplexValue.java b/src/java/com/healthmarketscience/jackcess/complex/ComplexValue.java index 4513f07..cd77554 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/ComplexValue.java +++ b/src/java/com/healthmarketscience/jackcess/complex/ComplexValue.java @@ -20,6 +20,7 @@ USA package com.healthmarketscience.jackcess.complex; import java.io.IOException; +import java.io.ObjectStreamException; import com.healthmarketscience.jackcess.Column; import com.healthmarketscience.jackcess.RowId; @@ -83,6 +84,60 @@ public interface ComplexValue { private static final long serialVersionUID = 20130318L; + @Override + public byte byteValue() { + return (byte)get(); + } + + @Override + public short shortValue() { + return (short)get(); + } + + @Override + public int intValue() { + return get(); + } + + @Override + public long longValue() { + return get(); + } + + @Override + public float floatValue() { + return get(); + } + + @Override + public double doubleValue() { + return get(); + } + + @Override + public int hashCode() { + return get(); + } + + @Override + public boolean equals(Object o) { + return ((this == o) || + ((o != null) && (getClass() == o.getClass()) && + (get() == ((Id)o).get()))); + } + + @Override + public String toString() { + return String.valueOf(get()); + } + + protected final Object writeReplace() throws ObjectStreamException { + // if we are going to serialize this ComplexValue.Id, convert it back to + // a normal Integer (in case it is restored outside of the context of + // jackcess) + return Integer.valueOf(get()); + } + /** * Returns the unique identifier of this complex value (this value is unique * among all values in all rows of the main table for the complex column). diff --git a/src/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java b/src/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java index a4b4d02..1b651cd 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java +++ b/src/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java @@ -1,5 +1,5 @@ /* -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 @@ -24,9 +24,8 @@ import java.io.ObjectStreamException; import java.util.Date; import java.util.List; import java.util.Map; - import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.Row; + /** * Value which is returned for a complex column. This value corresponds to a @@ -42,27 +41,10 @@ import com.healthmarketscience.jackcess.Row; * * @author James Ahlborn */ -public class ComplexValueForeignKey extends Number +public abstract class ComplexValueForeignKey extends Number { - private static final long serialVersionUID = 20110805L; - - private transient final Column _column; - private final int _value; - private transient List _values; - - public ComplexValueForeignKey(Column column, int value) { - _column = column; - _value = value; - } - - public int get() { - return _value; - } + private static final long serialVersionUID = 20130319L; - public Column getColumn() { - return _column; - } - @Override public byte byteValue() { return (byte)get(); @@ -93,225 +75,94 @@ public class ComplexValueForeignKey extends Number return get(); } - public ComplexDataType getComplexType() { - return getComplexInfo().getType(); - } - - protected ComplexColumnInfo getComplexInfo() { - return _column.getComplexInfo(); - } - - protected VersionHistoryColumnInfo getVersionInfo() { - return (VersionHistoryColumnInfo)getComplexInfo(); - } - - protected AttachmentColumnInfo getAttachmentInfo() { - return (AttachmentColumnInfo)getComplexInfo(); - } - - protected MultiValueColumnInfo getMultiValueInfo() { - return (MultiValueColumnInfo)getComplexInfo(); - } - - protected UnsupportedColumnInfo getUnsupportedInfo() { - return (UnsupportedColumnInfo)getComplexInfo(); - } - - public int countValues() - throws IOException - { - return getComplexInfo().countValues(get()); - } - - public List getRawValues() throws IOException - { - return getComplexInfo().getRawValues(get()); - } - - public List getValues() - throws IOException - { - if(_values == null) { - _values = getComplexInfo().getValues(this); - } - return _values; - } - - @SuppressWarnings("unchecked") - public List getVersions() - throws IOException - { - if(getComplexType() != ComplexDataType.VERSION_HISTORY) { - throw new UnsupportedOperationException(); - } - return (List)getValues(); - } - - @SuppressWarnings("unchecked") - public List getAttachments() - throws IOException - { - if(getComplexType() != ComplexDataType.ATTACHMENT) { - throw new UnsupportedOperationException(); - } - return (List)getValues(); - } - - @SuppressWarnings("unchecked") - public List getMultiValues() - throws IOException - { - if(getComplexType() != ComplexDataType.MULTI_VALUE) { - throw new UnsupportedOperationException(); - } - return (List)getValues(); - } - - @SuppressWarnings("unchecked") - public List getUnsupportedValues() - throws IOException - { - if(getComplexType() != ComplexDataType.UNSUPPORTED) { - throw new UnsupportedOperationException(); - } - return (List)getValues(); - } - - public void reset() { - // discard any cached values - _values = null; - } - - public Version addVersion(String value) - throws IOException - { - return addVersion(value, new Date()); - } - - public Version addVersion(String value, Date modifiedDate) - throws IOException - { - reset(); - Version v = VersionHistoryColumnInfo.newVersion(this, value, modifiedDate); - getVersionInfo().addValue(v); - return v; - } - - public Attachment addAttachment(byte[] data) - throws IOException - { - return addAttachment(null, null, null, data, null, null); - } - - public Attachment addAttachment( - String url, String name, String type, byte[] data, - Date timeStamp, Integer flags) - throws IOException - { - reset(); - Attachment a = AttachmentColumnInfo.newAttachment( - this, url, name, type, data, timeStamp, flags); - getAttachmentInfo().addValue(a); - return a; - } - - public Attachment updateAttachment(Attachment attachment) - throws IOException - { - reset(); - getAttachmentInfo().updateValue(attachment); - return attachment; - } - - public Attachment deleteAttachment(Attachment attachment) - throws IOException - { - reset(); - getAttachmentInfo().deleteValue(attachment); - return attachment; - } - - public SingleValue addMultiValue(Object value) - throws IOException - { - reset(); - SingleValue v = MultiValueColumnInfo.newSingleValue(this, value); - getMultiValueInfo().addValue(v); - return v; - } - - public SingleValue updateMultiValue(SingleValue value) - throws IOException - { - reset(); - getMultiValueInfo().updateValue(value); - return value; - } - - public SingleValue deleteMultiValue(SingleValue value) - throws IOException - { - reset(); - getMultiValueInfo().deleteValue(value); - return value; - } - - public UnsupportedValue addUnsupportedValue(Map values) - throws IOException - { - reset(); - UnsupportedValue v = UnsupportedColumnInfo.newValue(this, values); - getUnsupportedInfo().addValue(v); - return v; - } - - public UnsupportedValue updateUnsupportedValue(UnsupportedValue value) - throws IOException - { - reset(); - getUnsupportedInfo().updateValue(value); - return value; - } - - public UnsupportedValue deleteUnsupportedValue(UnsupportedValue value) - throws IOException - { - reset(); - getUnsupportedInfo().deleteValue(value); - return value; - } - - public void deleteAllValues() - throws IOException - { - reset(); - getComplexInfo().deleteAllValues(this); - } - - private Object writeReplace() throws ObjectStreamException { + protected final Object writeReplace() throws ObjectStreamException { // if we are going to serialize this ComplexValueForeignKey, convert it // back to a normal Integer (in case it is restored outside of the context // of jackcess) - return Integer.valueOf(_value); + return Integer.valueOf(get()); } @Override public int hashCode() { - return _value; + return get(); } @Override public boolean equals(Object o) { return ((this == o) || ((o != null) && (getClass() == o.getClass()) && - (_value == ((ComplexValueForeignKey)o)._value) && - (_column == ((ComplexValueForeignKey)o)._column))); + (get() == ((ComplexValueForeignKey)o).get()))); } @Override public String toString() { - return String.valueOf(_value); + return String.valueOf(get()); } + + public abstract int get(); + + public abstract Column getColumn(); + + public abstract ComplexDataType getComplexType(); + + public abstract int countValues() throws IOException; + + public abstract List getValues() throws IOException; + + public abstract List getVersions() throws IOException; + + public abstract List getAttachments() + throws IOException; + + public abstract List getMultiValues() + throws IOException; + + public abstract List getUnsupportedValues() + throws IOException; + + public abstract void reset(); + + public abstract Version addVersion(String value) + throws IOException; + + public abstract Version addVersion(String value, Date modifiedDate) + throws IOException; + + public abstract Attachment addAttachment(byte[] data) + throws IOException; + + public abstract Attachment addAttachment( + String url, String name, String type, byte[] data, + Date timeStamp, Integer flags) + throws IOException; + + public abstract Attachment updateAttachment(Attachment attachment) + throws IOException; + + public abstract Attachment deleteAttachment(Attachment attachment) + throws IOException; + + public abstract SingleValue addMultiValue(Object value) + throws IOException; + + public abstract SingleValue updateMultiValue(SingleValue value) + throws IOException; + + public abstract SingleValue deleteMultiValue(SingleValue value) + throws IOException; + + public abstract UnsupportedValue addUnsupportedValue(Map values) + throws IOException; + + public abstract UnsupportedValue updateUnsupportedValue(UnsupportedValue value) + throws IOException; + + public abstract UnsupportedValue deleteUnsupportedValue(UnsupportedValue value) + throws IOException; + + public abstract void deleteAllValues() + throws IOException; + } diff --git a/src/java/com/healthmarketscience/jackcess/complex/MultiValueColumnInfo.java b/src/java/com/healthmarketscience/jackcess/complex/MultiValueColumnInfo.java index 4089153..406908e 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/MultiValueColumnInfo.java +++ b/src/java/com/healthmarketscience/jackcess/complex/MultiValueColumnInfo.java @@ -1,5 +1,5 @@ /* -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 @@ -19,101 +19,12 @@ USA package com.healthmarketscience.jackcess.complex; -import java.io.IOException; - -import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.Row; - /** - * Complex column info for a column holding multiple values per row. + * Complex column info for a column holding multiple simple values per row. * * @author James Ahlborn */ -public class MultiValueColumnInfo extends ComplexColumnInfo +public interface MultiValueColumnInfo extends ComplexColumnInfo { - private final Column _valueCol; - - public MultiValueColumnInfo(Column column, int complexId, - Table typeObjTable, Table flatTable) - throws IOException - { - super(column, complexId, typeObjTable, flatTable); - - _valueCol = getTypeColumns().get(0); - } - - @Override - public ComplexDataType getType() - { - return ComplexDataType.MULTI_VALUE; - } - - public Column getValueColumn() { - return _valueCol; - } - - @Override - protected SingleValueImpl toValue( - ComplexValueForeignKey complexValueFk, - Row rawValue) - { - ComplexValue.Id id = getValueId(rawValue); - Object value = getValueColumn().getRowValue(rawValue); - - return new SingleValueImpl(id, complexValueFk, value); - } - - @Override - protected Object[] asRow(Object[] row, SingleValue value) { - super.asRow(row, value); - getValueColumn().setRowValue(row, value.get()); - return row; - } - - public static SingleValue newSingleValue(Object value) { - return newSingleValue(INVALID_FK, value); - } - - public static SingleValue newSingleValue( - ComplexValueForeignKey complexValueFk, Object value) { - return new SingleValueImpl(INVALID_ID, complexValueFk, value); - } - - - private static class SingleValueImpl extends ComplexValueImpl - implements SingleValue - { - private Object _value; - - private SingleValueImpl(Id id, ComplexValueForeignKey complexValueFk, - Object value) - { - super(id, complexValueFk); - _value = value; - } - - public Object get() { - return _value; - } - - public void set(Object value) { - _value = value; - } - public void update() throws IOException { - getComplexValueForeignKey().updateMultiValue(this); - } - - public void delete() throws IOException { - getComplexValueForeignKey().deleteMultiValue(this); - } - - @Override - public String toString() - { - return "SingleValue(" + getComplexValueForeignKey() + "," + getId() + - ") " + get(); - } - } } diff --git a/src/java/com/healthmarketscience/jackcess/complex/UnsupportedColumnInfo.java b/src/java/com/healthmarketscience/jackcess/complex/UnsupportedColumnInfo.java index b0b6924..646ecfc 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/UnsupportedColumnInfo.java +++ b/src/java/com/healthmarketscience/jackcess/complex/UnsupportedColumnInfo.java @@ -1,5 +1,5 @@ /* -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 @@ -19,114 +19,12 @@ USA package com.healthmarketscience.jackcess.complex; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.Row; - /** * Complex column info for an unsupported complex type. * * @author James Ahlborn */ -public class UnsupportedColumnInfo extends ComplexColumnInfo +public interface UnsupportedColumnInfo extends ComplexColumnInfo { - public UnsupportedColumnInfo(Column column, int complexId, Table typeObjTable, - Table flatTable) - throws IOException - { - super(column, complexId, typeObjTable, flatTable); - } - - public List getValueColumns() { - return getTypeColumns(); - } - - @Override - public ComplexDataType getType() - { - return ComplexDataType.UNSUPPORTED; - } - - @Override - protected UnsupportedValueImpl toValue( - ComplexValueForeignKey complexValueFk, - Row rawValue) - { - ComplexValue.Id id = getValueId(rawValue); - - Map values = new LinkedHashMap(); - for(Column col : getValueColumns()) { - col.setRowValue(values, col.getRowValue(rawValue)); - } - - return new UnsupportedValueImpl(id, complexValueFk, values); - } - - @Override - protected Object[] asRow(Object[] row, UnsupportedValue value) { - super.asRow(row, value); - - Map values = value.getValues(); - for(Column col : getValueColumns()) { - col.setRowValue(row, col.getRowValue(values)); - } - - return row; - } - - public static UnsupportedValue newValue(Map values) { - return newValue(INVALID_FK, values); - } - - public static UnsupportedValue newValue( - ComplexValueForeignKey complexValueFk, Map values) { - return new UnsupportedValueImpl(INVALID_ID, complexValueFk, - new LinkedHashMap(values)); - } - - private static class UnsupportedValueImpl extends ComplexValueImpl - implements UnsupportedValue - { - private Map _values; - - private UnsupportedValueImpl(Id id, ComplexValueForeignKey complexValueFk, - Map values) - { - super(id, complexValueFk); - _values = values; - } - - public Map getValues() { - return _values; - } - - public Object get(String columnName) { - return getValues().get(columnName); - } - - public void set(String columnName, Object value) { - getValues().put(columnName, value); - } - - public void update() throws IOException { - getComplexValueForeignKey().updateUnsupportedValue(this); - } - - public void delete() throws IOException { - getComplexValueForeignKey().deleteUnsupportedValue(this); - } - - @Override - public String toString() - { - return "UnsupportedValue(" + getComplexValueForeignKey() + "," + getId() + - ") " + getValues(); - } - } } diff --git a/src/java/com/healthmarketscience/jackcess/complex/VersionHistoryColumnInfo.java b/src/java/com/healthmarketscience/jackcess/complex/VersionHistoryColumnInfo.java index 60c6fb7..db1f1cf 100644 --- a/src/java/com/healthmarketscience/jackcess/complex/VersionHistoryColumnInfo.java +++ b/src/java/com/healthmarketscience/jackcess/complex/VersionHistoryColumnInfo.java @@ -1,5 +1,5 @@ /* -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 @@ -19,16 +19,6 @@ USA package com.healthmarketscience.jackcess.complex; -import java.io.IOException; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import com.healthmarketscience.jackcess.Column; -import com.healthmarketscience.jackcess.Row; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.impl.ColumnImpl; - /** * Complex column info for a column which tracking the version history of an * "append only" memo column. @@ -40,179 +30,7 @@ import com.healthmarketscience.jackcess.impl.ColumnImpl; * * @author James Ahlborn */ -public class VersionHistoryColumnInfo extends ComplexColumnInfo +public interface VersionHistoryColumnInfo extends ComplexColumnInfo { - private final Column _valueCol; - private final Column _modifiedCol; - - public VersionHistoryColumnInfo(Column column, int complexId, - Table typeObjTable, Table flatTable) - throws IOException - { - super(column, complexId, typeObjTable, flatTable); - - Column valueCol = null; - Column modifiedCol = null; - for(Column col : getTypeColumns()) { - switch(col.getType()) { - case SHORT_DATE_TIME: - modifiedCol = col; - break; - case MEMO: - valueCol = col; - break; - default: - // ignore - } - } - - _valueCol = valueCol; - _modifiedCol = modifiedCol; - } - - @Override - public void postTableLoadInit() throws IOException { - super.postTableLoadInit(); - - // link up with the actual versioned column. it should have the same name - // as the "value" column in the type table. - Column versionedCol = getColumn().getTable().getColumn( - getValueColumn().getName()); - ((ColumnImpl)versionedCol).setVersionHistoryColumn((ColumnImpl)getColumn()); - } - - public Column getValueColumn() { - return _valueCol; - } - - public Column getModifiedDateColumn() { - return _modifiedCol; - } - - @Override - public ComplexDataType getType() { - return ComplexDataType.VERSION_HISTORY; - } - - @Override - public ComplexValue.Id updateValue(Version value) throws IOException { - throw new UnsupportedOperationException( - "This column does not support value updates"); - } - - @Override - public void deleteValue(Version value) throws IOException { - throw new UnsupportedOperationException( - "This column does not support value deletes"); - } - - @Override - public void deleteAllValues(int complexValueFk) throws IOException { - throw new UnsupportedOperationException( - "This column does not support value deletes"); - } - - @Override - protected List toValues(ComplexValueForeignKey complexValueFk, - List rawValues) - throws IOException - { - List versions = super.toValues(complexValueFk, rawValues); - - // order versions newest to oldest - Collections.sort(versions); - - return versions; - } - - @Override - protected VersionImpl toValue(ComplexValueForeignKey complexValueFk, - Row rawValue) { - ComplexValue.Id id = getValueId(rawValue); - String value = (String)getValueColumn().getRowValue(rawValue); - Date modifiedDate = (Date)getModifiedDateColumn().getRowValue(rawValue); - - return new VersionImpl(id, complexValueFk, value, modifiedDate); - } - - @Override - protected Object[] asRow(Object[] row, Version version) { - super.asRow(row, version); - getValueColumn().setRowValue(row, version.getValue()); - getModifiedDateColumn().setRowValue(row, version.getModifiedDate()); - return row; - } - - public static Version newVersion(String value, Date modifiedDate) { - return newVersion(INVALID_FK, value, modifiedDate); - } - - public static Version newVersion(ComplexValueForeignKey complexValueFk, - String value, Date modifiedDate) { - return new VersionImpl(INVALID_ID, complexValueFk, value, modifiedDate); - } - - - private static class VersionImpl extends ComplexValueImpl implements Version - { - private final String _value; - private final Date _modifiedDate; - - private VersionImpl(Id id, ComplexValueForeignKey complexValueFk, - String value, Date modifiedDate) - { - super(id, complexValueFk); - _value = value; - _modifiedDate = modifiedDate; - } - - public String getValue() { - return _value; - } - - public Date getModifiedDate() { - return _modifiedDate; - } - - public int compareTo(Version o) { - Date d1 = getModifiedDate(); - Date d2 = o.getModifiedDate(); - - // sort by descending date (newest/greatest first) - int cmp = d2.compareTo(d1); - if(cmp != 0) { - return cmp; - } - - // use id, then complexValueFk to break ties (although we really - // shouldn't be comparing across different columns) - int id1 = getId().get(); - int id2 = o.getId().get(); - if(id1 != id2) { - return ((id1 > id2) ? -1 : 1); - } - id1 = getComplexValueForeignKey().get(); - id2 = o.getComplexValueForeignKey().get(); - return ((id1 > id2) ? -1 : - ((id1 < id2) ? 1 : 0)); - } - - public void update() throws IOException { - throw new UnsupportedOperationException( - "This column does not support value updates"); - } - - public void delete() throws IOException { - throw new UnsupportedOperationException( - "This column does not support value deletes"); - } - @Override - public String toString() - { - return "Version(" + getComplexValueForeignKey() + "," + getId() + ") " + - getModifiedDate() + ", " + getValue(); - } - } - } diff --git a/src/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index 2038556..e940919 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -54,6 +54,8 @@ import java.util.regex.Pattern; import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; import com.healthmarketscience.jackcess.complex.ComplexValue; import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.impl.complex.ComplexColumnInfoImpl; +import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl; import com.healthmarketscience.jackcess.impl.scsu.Compress; import com.healthmarketscience.jackcess.impl.scsu.EndOfInputException; import com.healthmarketscience.jackcess.impl.scsu.Expand; @@ -327,7 +329,8 @@ public class ColumnImpl implements Column, Comparable { */ void postTableLoadInit() throws IOException { if(_complexInfo != null) { - _complexInfo.postTableLoadInit(); + ((ComplexColumnInfoImpl)_complexInfo) + .postTableLoadInit(); } } @@ -619,7 +622,7 @@ public class ColumnImpl implements Column, Comparable { // treat like "binary" data return data; } else if (_type == DataType.COMPLEX_TYPE) { - return new ComplexValueForeignKey(this, buffer.getInt()); + return new ComplexValueForeignKeyImpl(this, buffer.getInt()); } else if(_type.isUnsupported()) { return rawDataWrapper(data); } else { @@ -2057,7 +2060,7 @@ public class ColumnImpl implements Column, Comparable { getTable().getNextComplexTypeAutoNumber() : // same value is shared across all ComplexType values in a row ((ComplexValueForeignKey)prevRowValue).get()); - return new ComplexValueForeignKey(ColumnImpl.this, nextComplexAutoNum); + return new ComplexValueForeignKeyImpl(ColumnImpl.this, nextComplexAutoNum); } @Override diff --git a/src/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java b/src/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java index f427247..9cf9b68 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java +++ b/src/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java @@ -38,6 +38,10 @@ import com.healthmarketscience.jackcess.complex.ComplexValue; import com.healthmarketscience.jackcess.complex.MultiValueColumnInfo; import com.healthmarketscience.jackcess.complex.UnsupportedColumnInfo; import com.healthmarketscience.jackcess.complex.VersionHistoryColumnInfo; +import com.healthmarketscience.jackcess.impl.complex.AttachmentColumnInfoImpl; +import com.healthmarketscience.jackcess.impl.complex.MultiValueColumnInfoImpl; +import com.healthmarketscience.jackcess.impl.complex.UnsupportedColumnInfoImpl; +import com.healthmarketscience.jackcess.impl.complex.VersionHistoryColumnInfoImpl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -101,18 +105,18 @@ public class ComplexColumnSupport // we inspect the structore of the "type table" to determine what kind of // complex info we are dealing with if(isMultiValueColumn(typeObjTable)) { - return new MultiValueColumnInfo(column, complexTypeId, typeObjTable, + return new MultiValueColumnInfoImpl(column, complexTypeId, typeObjTable, flatTable); } else if(isAttachmentColumn(typeObjTable)) { - return new AttachmentColumnInfo(column, complexTypeId, typeObjTable, + return new AttachmentColumnInfoImpl(column, complexTypeId, typeObjTable, flatTable); } else if(isVersionHistoryColumn(typeObjTable)) { - return new VersionHistoryColumnInfo(column, complexTypeId, typeObjTable, + return new VersionHistoryColumnInfoImpl(column, complexTypeId, typeObjTable, flatTable); } LOG.warn("Unsupported complex column type " + typeObjTable.getName()); - return new UnsupportedColumnInfo(column, complexTypeId, typeObjTable, + return new UnsupportedColumnInfoImpl(column, complexTypeId, typeObjTable, flatTable); } diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java new file mode 100644 index 0000000..c0e5646 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java @@ -0,0 +1,280 @@ +/* +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.complex; + +import java.io.IOException; +import java.util.Date; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.complex.Attachment; +import com.healthmarketscience.jackcess.complex.AttachmentColumnInfo; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.impl.ByteUtil; + + +/** + * Complex column info for a column holding 0 or more attachments per row. + * + * @author James Ahlborn + */ +public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl + implements AttachmentColumnInfo +{ + private static final String FILE_NAME_COL_NAME = "FileName"; + private static final String FILE_TYPE_COL_NAME = "FileType"; + + private final Column _fileUrlCol; + private final Column _fileNameCol; + private final Column _fileTypeCol; + private final Column _fileDataCol; + private final Column _fileTimeStampCol; + private final Column _fileFlagsCol; + + public AttachmentColumnInfoImpl(Column column, int complexId, + Table typeObjTable, Table flatTable) + throws IOException + { + super(column, complexId, typeObjTable, flatTable); + + Column fileUrlCol = null; + Column fileNameCol = null; + Column fileTypeCol = null; + Column fileDataCol = null; + Column fileTimeStampCol = null; + Column fileFlagsCol = null; + + for(Column col : getTypeColumns()) { + switch(col.getType()) { + case TEXT: + if(FILE_NAME_COL_NAME.equalsIgnoreCase(col.getName())) { + fileNameCol = col; + } else if(FILE_TYPE_COL_NAME.equalsIgnoreCase(col.getName())) { + fileTypeCol = col; + } else { + // if names don't match, assign in order: name, type + if(fileNameCol == null) { + fileNameCol = col; + } else if(fileTypeCol == null) { + fileTypeCol = col; + } + } + break; + case LONG: + fileFlagsCol = col; + break; + case SHORT_DATE_TIME: + fileTimeStampCol = col; + break; + case OLE: + fileDataCol = col; + break; + case MEMO: + fileUrlCol = col; + break; + default: + // ignore + } + } + + _fileUrlCol = fileUrlCol; + _fileNameCol = fileNameCol; + _fileTypeCol = fileTypeCol; + _fileDataCol = fileDataCol; + _fileTimeStampCol = fileTimeStampCol; + _fileFlagsCol = fileFlagsCol; + } + + public Column getFileUrlColumn() { + return _fileUrlCol; + } + + public Column getFileNameColumn() { + return _fileNameCol; + } + + public Column getFileTypeColumn() { + return _fileTypeCol; + } + + public Column getFileDataColumn() { + return _fileDataCol; + } + + public Column getFileTimeStampColumn() { + return _fileTimeStampCol; + } + + public Column getFileFlagsColumn() { + return _fileFlagsCol; + } + + @Override + public ComplexDataType getType() + { + return ComplexDataType.ATTACHMENT; + } + + @Override + protected AttachmentImpl toValue(ComplexValueForeignKey complexValueFk, + Row rawValue) { + ComplexValue.Id id = getValueId(rawValue); + String url = (String)getFileUrlColumn().getRowValue(rawValue); + String name = (String)getFileNameColumn().getRowValue(rawValue); + String type = (String)getFileTypeColumn().getRowValue(rawValue); + Integer flags = (Integer)getFileFlagsColumn().getRowValue(rawValue); + Date ts = (Date)getFileTimeStampColumn().getRowValue(rawValue); + byte[] data = (byte[])getFileDataColumn().getRowValue(rawValue); + + return new AttachmentImpl(id, complexValueFk, url, name, type, data, + ts, flags); + } + + @Override + protected Object[] asRow(Object[] row, Attachment attachment) { + super.asRow(row, attachment); + getFileUrlColumn().setRowValue(row, attachment.getFileUrl()); + getFileNameColumn().setRowValue(row, attachment.getFileName()); + getFileTypeColumn().setRowValue(row, attachment.getFileType()); + getFileFlagsColumn().setRowValue(row, attachment.getFileFlags()); + getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp()); + getFileDataColumn().setRowValue(row, attachment.getFileData()); + return row; + } + + public static Attachment newAttachment(byte[] data) { + return newAttachment(INVALID_FK, data); + } + + public static Attachment newAttachment(ComplexValueForeignKey complexValueFk, + byte[] data) { + return newAttachment(complexValueFk, null, null, null, data, null, null); + } + + public static Attachment newAttachment( + String url, String name, String type, byte[] data, + Date timeStamp, Integer flags) + { + return newAttachment(INVALID_FK, url, name, type, data, + timeStamp, flags); + } + + public static Attachment newAttachment( + ComplexValueForeignKey complexValueFk, String url, String name, + String type, byte[] data, Date timeStamp, Integer flags) + { + return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type, + data, timeStamp, flags); + } + + + private static class AttachmentImpl extends ComplexValueImpl + implements Attachment + { + private String _url; + private String _name; + private String _type; + private byte[] _data; + private Date _timeStamp; + private Integer _flags; + + private AttachmentImpl(Id id, ComplexValueForeignKey complexValueFk, + String url, String name, String type, byte[] data, + Date timeStamp, Integer flags) + { + super(id, complexValueFk); + _url = url; + _name = name; + _type = type; + _data = data; + _timeStamp = timeStamp; + _flags = flags; + } + + public byte[] getFileData() { + return _data; + } + + public void setFileData(byte[] data) { + _data = data; + } + + public String getFileName() { + return _name; + } + + public void setFileName(String fileName) { + _name = fileName; + } + + public String getFileUrl() { + return _url; + } + + public void setFileUrl(String fileUrl) { + _url = fileUrl; + } + + public String getFileType() { + return _type; + } + + public void setFileType(String fileType) { + _type = fileType; + } + + public Date getFileTimeStamp() { + return _timeStamp; + } + + public void setFileTimeStamp(Date fileTimeStamp) { + _timeStamp = fileTimeStamp; + } + + public Integer getFileFlags() { + return _flags; + } + + public void setFileFlags(Integer fileFlags) { + _flags = fileFlags; + } + + public void update() throws IOException { + getComplexValueForeignKey().updateAttachment(this); + } + + public void delete() throws IOException { + getComplexValueForeignKey().deleteAttachment(this); + } + + @Override + public String toString() + { + return "Attachment(" + getComplexValueForeignKey() + "," + getId() + + ") " + getFileUrl() + ", " + getFileName() + ", " + getFileType() + + ", " + getFileTimeStamp() + ", " + getFileFlags() + ", " + + ByteUtil.toHexString(getFileData()); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java new file mode 100644 index 0000000..10d4ef5 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java @@ -0,0 +1,417 @@ +/* +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.complex; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.CursorBuilder; +import com.healthmarketscience.jackcess.DataType; +import com.healthmarketscience.jackcess.Database; +import com.healthmarketscience.jackcess.IndexCursor; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.RowId; +import com.healthmarketscience.jackcess.RuntimeIOException; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.impl.ColumnImpl; +import com.healthmarketscience.jackcess.impl.TableImpl; + +/** + * Base class for the additional information tracked for complex columns. + * + * @author James Ahlborn + */ +public abstract class ComplexColumnInfoImpl + implements ComplexColumnInfo +{ + private static final int INVALID_ID_VALUE = -1; + public static final ComplexValue.Id INVALID_ID = new ComplexValueIdImpl( + INVALID_ID_VALUE, null); + public static final ComplexValueForeignKey INVALID_FK = + new ComplexValueForeignKeyImpl(null, INVALID_ID_VALUE); + + private final Column _column; + private final int _complexTypeId; + private final Table _flatTable; + private final List _typeCols; + private final Column _pkCol; + private final Column _complexValFkCol; + private IndexCursor _complexValIdCursor; + + protected ComplexColumnInfoImpl(Column column, int complexTypeId, + Table typeObjTable, Table flatTable) + throws IOException + { + _column = column; + _complexTypeId = complexTypeId; + _flatTable = flatTable; + + // the flat table has all the "value" columns and 2 extra columns, a + // primary key for each row, and a LONG value which is essentially a + // foreign key to the main table. + List typeCols = new ArrayList(); + List otherCols = new ArrayList(); + diffFlatColumns(typeObjTable, flatTable, typeCols, otherCols); + + _typeCols = Collections.unmodifiableList(typeCols); + + Column pkCol = null; + Column complexValFkCol = null; + for(Column col : otherCols) { + if(col.isAutoNumber()) { + pkCol = col; + } else if(col.getType() == DataType.LONG) { + complexValFkCol = col; + } + } + + if((pkCol == null) || (complexValFkCol == null)) { + throw new IOException("Could not find expected columns in flat table " + + flatTable.getName() + " for complex column with id " + + complexTypeId); + } + _pkCol = pkCol; + _complexValFkCol = complexValFkCol; + } + + public void postTableLoadInit() throws IOException { + // nothing to do in base class + } + + public Column getColumn() { + return _column; + } + + public Database getDatabase() { + return getColumn().getDatabase(); + } + + public Column getPrimaryKeyColumn() { + return _pkCol; + } + + public Column getComplexValueForeignKeyColumn() { + return _complexValFkCol; + } + + protected List getTypeColumns() { + return _typeCols; + } + + public int countValues(int complexValueFk) throws IOException { + return getRawValues(complexValueFk, + Collections.singleton(_complexValFkCol.getName())) + .size(); + } + + public List getRawValues(int complexValueFk) + throws IOException + { + return getRawValues(complexValueFk, null); + } + + private Iterator getComplexValFkIter( + int complexValueFk, Collection columnNames) + throws IOException + { + if(_complexValIdCursor == null) { + _complexValIdCursor = new CursorBuilder(_flatTable) + .setIndexByColumns(_complexValFkCol) + .toIndexCursor(); + } + + return _complexValIdCursor.entryIterator(columnNames, complexValueFk); + } + + public List getRawValues(int complexValueFk, + Collection columnNames) + throws IOException + { + Iterator entryIter = + getComplexValFkIter(complexValueFk, columnNames); + if(!entryIter.hasNext()) { + return Collections.emptyList(); + } + + List values = new ArrayList(); + while(entryIter.hasNext()) { + values.add(entryIter.next()); + } + + return values; + } + + public List getValues(ComplexValueForeignKey complexValueFk) + throws IOException + { + List rawValues = getRawValues(complexValueFk.get()); + if(rawValues.isEmpty()) { + return Collections.emptyList(); + } + + return toValues(complexValueFk, rawValues); + } + + protected List toValues(ComplexValueForeignKey complexValueFk, + List rawValues) + throws IOException + { + List values = new ArrayList(); + for(Row rawValue : rawValues) { + values.add(toValue(complexValueFk, rawValue)); + } + + return values; + } + + public ComplexValue.Id addRawValue(Map rawValue) + throws IOException + { + Object[] row = ((TableImpl)_flatTable).asRowWithRowId(rawValue); + _flatTable.addRow(row); + return getValueId(row); + } + + public ComplexValue.Id addValue(V value) throws IOException { + Object[] row = asRow(newRowArray(), value); + _flatTable.addRow(row); + ComplexValue.Id id = getValueId(row); + value.setId(id); + return id; + } + + public void addValues(Collection values) throws IOException { + for(V value : values) { + addValue(value); + } + } + + public ComplexValue.Id updateRawValue(Row rawValue) throws IOException { + _flatTable.updateRow(rawValue); + return getValueId(rawValue); + } + + public ComplexValue.Id updateValue(V value) throws IOException { + ComplexValue.Id id = value.getId(); + updateRow(id, asRow(newRowArray(), value)); + return id; + } + + public void updateValues(Collection values) throws IOException { + for(V value : values) { + updateValue(value); + } + } + + public void deleteRawValue(Row rawValue) throws IOException { + deleteRow(rawValue.getId()); + } + + public void deleteValue(V value) throws IOException { + deleteRow(value.getId().getRowId()); + } + + public void deleteValues(Collection values) throws IOException { + for(V value : values) { + deleteValue(value); + } + } + + public void deleteAllValues(int complexValueFk) throws IOException { + Iterator entryIter = + getComplexValFkIter(complexValueFk, Collections.emptySet()); + try { + while(entryIter.hasNext()) { + entryIter.next(); + entryIter.remove(); + } + } catch(RuntimeIOException e) { + throw (IOException)e.getCause(); + } + } + + public void deleteAllValues(ComplexValueForeignKey complexValueFk) + throws IOException + { + deleteAllValues(complexValueFk.get()); + } + + private void updateRow(ComplexValue.Id id, Object[] row) throws IOException { + ((TableImpl)_flatTable).updateRow(id.getRowId(), row); + } + + private void deleteRow(RowId rowId) throws IOException { + ((TableImpl)_flatTable).deleteRow(rowId); + } + + protected ComplexValueIdImpl getValueId(Row row) { + int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row); + return new ComplexValueIdImpl(idVal, row.getId()); + } + + protected ComplexValueIdImpl getValueId(Object[] row) { + int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row); + return new ComplexValueIdImpl(idVal, + ((TableImpl)_flatTable).getRowId(row)); + } + + protected Object[] asRow(Object[] row, V value) { + ComplexValue.Id id = value.getId(); + _pkCol.setRowValue( + row, ((id != INVALID_ID) ? id : Column.AUTO_NUMBER)); + ComplexValueForeignKey cFk = value.getComplexValueForeignKey(); + _complexValFkCol.setRowValue( + row, ((cFk != INVALID_FK) ? cFk : Column.AUTO_NUMBER)); + return row; + } + + private Object[] newRowArray() { + Object[] row = new Object[_flatTable.getColumnCount() + 1]; + row[row.length - 1] = ColumnImpl.RETURN_ROW_ID; + return row; + } + + @Override + public String toString() { + StringBuilder rtn = new StringBuilder(); + rtn.append("\n\t\tComplexType: " + getType()); + rtn.append("\n\t\tComplexTypeId: " + _complexTypeId); + return rtn.toString(); + } + + protected static void diffFlatColumns(Table typeObjTable, + Table flatTable, + List typeCols, + List otherCols) + { + // each "flat"" table has the columns from the "type" table, plus some + // others. separate the "flat" columns into these 2 buckets + for(Column col : flatTable.getColumns()) { + if(((TableImpl)typeObjTable).hasColumn(col.getName())) { + typeCols.add(col); + } else { + otherCols.add(col); + } + } + } + + public abstract ComplexDataType getType(); + + protected abstract V toValue( + ComplexValueForeignKey complexValueFk, + Row rawValues) + throws IOException; + + protected static abstract class ComplexValueImpl implements ComplexValue + { + private Id _id; + private ComplexValueForeignKey _complexValueFk; + + protected ComplexValueImpl(Id id, ComplexValueForeignKey complexValueFk) { + _id = id; + _complexValueFk = complexValueFk; + } + + public Id getId() { + return _id; + } + + public void setId(Id id) { + if(_id == id) { + // harmless, ignore + return; + } + if(_id != INVALID_ID) { + throw new IllegalStateException("id may not be reset"); + } + _id = id; + } + + public ComplexValueForeignKey getComplexValueForeignKey() { + return _complexValueFk; + } + + public void setComplexValueForeignKey(ComplexValueForeignKey complexValueFk) + { + if(_complexValueFk == complexValueFk) { + // harmless, ignore + return; + } + if(_complexValueFk != INVALID_FK) { + throw new IllegalStateException("complexValueFk may not be reset"); + } + _complexValueFk = complexValueFk; + } + + public Column getColumn() { + return _complexValueFk.getColumn(); + } + + @Override + public int hashCode() { + return ((_id.get() * 37) ^ _complexValueFk.hashCode()); + } + + @Override + public boolean equals(Object o) { + return ((this == o) || + ((o != null) && (getClass() == o.getClass()) && + (_id == ((ComplexValueImpl)o)._id) && + _complexValueFk.equals(((ComplexValueImpl)o)._complexValueFk))); + } + } + + /** + * Implementation of ComplexValue.Id. + */ + private static final class ComplexValueIdImpl extends ComplexValue.Id + { + private static final long serialVersionUID = 20130318L; + + private final int _value; + private final RowId _rowId; + + protected ComplexValueIdImpl(int value, RowId rowId) { + _value = value; + _rowId = rowId; + } + + @Override + public int get() { + return _value; + } + + @Override + public RowId getRowId() { + return _rowId; + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java new file mode 100644 index 0000000..d196ae5 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java @@ -0,0 +1,248 @@ +/* +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.complex; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.complex.Attachment; +import com.healthmarketscience.jackcess.complex.AttachmentColumnInfo; +import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.complex.MultiValueColumnInfo; +import com.healthmarketscience.jackcess.complex.SingleValue; +import com.healthmarketscience.jackcess.complex.UnsupportedColumnInfo; +import com.healthmarketscience.jackcess.complex.UnsupportedValue; +import com.healthmarketscience.jackcess.complex.Version; +import com.healthmarketscience.jackcess.complex.VersionHistoryColumnInfo; + +/** + * Value which is returned for a complex column. This value corresponds to a + * foreign key in a secondary table which contains the actual complex data for + * this row (which could be 0 or more complex values for a given row). This + * class contains various convenience methods for interacting with the actual + * complex values. + *

+ * This class will cache the associated complex values returned from one of + * the lookup methods. The various modification methods will clear this cache + * automatically. The {@link #reset} method may be called manually to clear + * this internal cache. + * + * @author James Ahlborn + */ +public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey +{ + private static final long serialVersionUID = 20110805L; + + private transient final Column _column; + private final int _value; + private transient List _values; + + public ComplexValueForeignKeyImpl(Column column, int value) { + _column = column; + _value = value; + } + + @Override + public int get() { + return _value; + } + + public Column getColumn() { + return _column; + } + + @Override + public ComplexDataType getComplexType() { + return getComplexInfo().getType(); + } + + protected ComplexColumnInfo getComplexInfo() { + return _column.getComplexInfo(); + } + + protected VersionHistoryColumnInfo getVersionInfo() { + return (VersionHistoryColumnInfo)getComplexInfo(); + } + + protected AttachmentColumnInfo getAttachmentInfo() { + return (AttachmentColumnInfo)getComplexInfo(); + } + + protected MultiValueColumnInfo getMultiValueInfo() { + return (MultiValueColumnInfo)getComplexInfo(); + } + + protected UnsupportedColumnInfo getUnsupportedInfo() { + return (UnsupportedColumnInfo)getComplexInfo(); + } + + public int countValues() throws IOException { + return getComplexInfo().countValues(get()); + } + + public List getRawValues() throws IOException { + return getComplexInfo().getRawValues(get()); + } + + public List getValues() throws IOException { + if(_values == null) { + _values = getComplexInfo().getValues(this); + } + return _values; + } + + @SuppressWarnings("unchecked") + public List getVersions() throws IOException { + if(getComplexType() != ComplexDataType.VERSION_HISTORY) { + throw new UnsupportedOperationException(); + } + return (List)getValues(); + } + + @SuppressWarnings("unchecked") + public List getAttachments() throws IOException { + if(getComplexType() != ComplexDataType.ATTACHMENT) { + throw new UnsupportedOperationException(); + } + return (List)getValues(); + } + + @SuppressWarnings("unchecked") + public List getMultiValues() throws IOException { + if(getComplexType() != ComplexDataType.MULTI_VALUE) { + throw new UnsupportedOperationException(); + } + return (List)getValues(); + } + + @SuppressWarnings("unchecked") + public List getUnsupportedValues() throws IOException { + if(getComplexType() != ComplexDataType.UNSUPPORTED) { + throw new UnsupportedOperationException(); + } + return (List)getValues(); + } + + public void reset() { + // discard any cached values + _values = null; + } + + public Version addVersion(String value) throws IOException { + return addVersion(value, new Date()); + } + + public Version addVersion(String value, Date modifiedDate) throws IOException { + reset(); + Version v = VersionHistoryColumnInfoImpl.newVersion(this, value, modifiedDate); + getVersionInfo().addValue(v); + return v; + } + + public Attachment addAttachment(byte[] data) throws IOException { + return addAttachment(null, null, null, data, null, null); + } + + public Attachment addAttachment( + String url, String name, String type, byte[] data, + Date timeStamp, Integer flags) + throws IOException + { + reset(); + Attachment a = AttachmentColumnInfoImpl.newAttachment( + this, url, name, type, data, timeStamp, flags); + getAttachmentInfo().addValue(a); + return a; + } + + public Attachment updateAttachment(Attachment attachment) throws IOException { + reset(); + getAttachmentInfo().updateValue(attachment); + return attachment; + } + + public Attachment deleteAttachment(Attachment attachment) throws IOException { + reset(); + getAttachmentInfo().deleteValue(attachment); + return attachment; + } + + public SingleValue addMultiValue(Object value) throws IOException { + reset(); + SingleValue v = MultiValueColumnInfoImpl.newSingleValue(this, value); + getMultiValueInfo().addValue(v); + return v; + } + + public SingleValue updateMultiValue(SingleValue value) throws IOException { + reset(); + getMultiValueInfo().updateValue(value); + return value; + } + + public SingleValue deleteMultiValue(SingleValue value) throws IOException { + reset(); + getMultiValueInfo().deleteValue(value); + return value; + } + + public UnsupportedValue addUnsupportedValue(Map values) + throws IOException + { + reset(); + UnsupportedValue v = UnsupportedColumnInfoImpl.newValue(this, values); + getUnsupportedInfo().addValue(v); + return v; + } + + public UnsupportedValue updateUnsupportedValue(UnsupportedValue value) + throws IOException + { + reset(); + getUnsupportedInfo().updateValue(value); + return value; + } + + public UnsupportedValue deleteUnsupportedValue(UnsupportedValue value) + throws IOException + { + reset(); + getUnsupportedInfo().deleteValue(value); + return value; + } + + public void deleteAllValues() throws IOException { + reset(); + getComplexInfo().deleteAllValues(this); + } + + @Override + public boolean equals(Object o) { + return(super.equals(o) && + (_column == ((ComplexValueForeignKeyImpl)o)._column)); + } +} diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/MultiValueColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/MultiValueColumnInfoImpl.java new file mode 100644 index 0000000..16b84e3 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/MultiValueColumnInfoImpl.java @@ -0,0 +1,125 @@ +/* +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.complex; + +import java.io.IOException; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.complex.MultiValueColumnInfo; +import com.healthmarketscience.jackcess.complex.SingleValue; + +/** + * Complex column info for a column holding multiple simple values per row. + * + * @author James Ahlborn + */ +public class MultiValueColumnInfoImpl extends ComplexColumnInfoImpl + implements MultiValueColumnInfo +{ + private final Column _valueCol; + + public MultiValueColumnInfoImpl(Column column, int complexId, + Table typeObjTable, Table flatTable) + throws IOException + { + super(column, complexId, typeObjTable, flatTable); + + _valueCol = getTypeColumns().get(0); + } + + @Override + public ComplexDataType getType() + { + return ComplexDataType.MULTI_VALUE; + } + + public Column getValueColumn() { + return _valueCol; + } + + @Override + protected SingleValueImpl toValue( + ComplexValueForeignKey complexValueFk, + Row rawValue) + { + ComplexValue.Id id = getValueId(rawValue); + Object value = getValueColumn().getRowValue(rawValue); + + return new SingleValueImpl(id, complexValueFk, value); + } + + @Override + protected Object[] asRow(Object[] row, SingleValue value) { + super.asRow(row, value); + getValueColumn().setRowValue(row, value.get()); + return row; + } + + public static SingleValue newSingleValue(Object value) { + return newSingleValue(INVALID_FK, value); + } + + public static SingleValue newSingleValue( + ComplexValueForeignKey complexValueFk, Object value) { + return new SingleValueImpl(INVALID_ID, complexValueFk, value); + } + + + private static class SingleValueImpl extends ComplexValueImpl + implements SingleValue + { + private Object _value; + + private SingleValueImpl(Id id, ComplexValueForeignKey complexValueFk, + Object value) + { + super(id, complexValueFk); + _value = value; + } + + public Object get() { + return _value; + } + + public void set(Object value) { + _value = value; + } + + public void update() throws IOException { + getComplexValueForeignKey().updateMultiValue(this); + } + + public void delete() throws IOException { + getComplexValueForeignKey().deleteMultiValue(this); + } + + @Override + public String toString() + { + return "SingleValue(" + getComplexValueForeignKey() + "," + getId() + + ") " + get(); + } + } +} diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/UnsupportedColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/UnsupportedColumnInfoImpl.java new file mode 100644 index 0000000..84dbcdb --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/UnsupportedColumnInfoImpl.java @@ -0,0 +1,139 @@ +/* +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.complex; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.complex.UnsupportedColumnInfo; +import com.healthmarketscience.jackcess.complex.UnsupportedValue; + +/** + * Complex column info for an unsupported complex type. + * + * @author James Ahlborn + */ +public class UnsupportedColumnInfoImpl + extends ComplexColumnInfoImpl + implements UnsupportedColumnInfo +{ + + public UnsupportedColumnInfoImpl(Column column, int complexId, + Table typeObjTable, Table flatTable) + throws IOException + { + super(column, complexId, typeObjTable, flatTable); + } + + public List getValueColumns() { + return getTypeColumns(); + } + + @Override + public ComplexDataType getType() + { + return ComplexDataType.UNSUPPORTED; + } + + @Override + protected UnsupportedValueImpl toValue( + ComplexValueForeignKey complexValueFk, + Row rawValue) + { + ComplexValue.Id id = getValueId(rawValue); + + Map values = new LinkedHashMap(); + for(Column col : getValueColumns()) { + col.setRowValue(values, col.getRowValue(rawValue)); + } + + return new UnsupportedValueImpl(id, complexValueFk, values); + } + + @Override + protected Object[] asRow(Object[] row, UnsupportedValue value) { + super.asRow(row, value); + + Map values = value.getValues(); + for(Column col : getValueColumns()) { + col.setRowValue(row, col.getRowValue(values)); + } + + return row; + } + + public static UnsupportedValue newValue(Map values) { + return newValue(INVALID_FK, values); + } + + public static UnsupportedValue newValue( + ComplexValueForeignKey complexValueFk, Map values) { + return new UnsupportedValueImpl(INVALID_ID, complexValueFk, + new LinkedHashMap(values)); + } + + private static class UnsupportedValueImpl extends ComplexValueImpl + implements UnsupportedValue + { + private Map _values; + + private UnsupportedValueImpl(Id id, ComplexValueForeignKey complexValueFk, + Map values) + { + super(id, complexValueFk); + _values = values; + } + + public Map getValues() { + return _values; + } + + public Object get(String columnName) { + return getValues().get(columnName); + } + + public void set(String columnName, Object value) { + getValues().put(columnName, value); + } + + public void update() throws IOException { + getComplexValueForeignKey().updateUnsupportedValue(this); + } + + public void delete() throws IOException { + getComplexValueForeignKey().deleteUnsupportedValue(this); + } + + @Override + public String toString() + { + return "UnsupportedValue(" + getComplexValueForeignKey() + "," + getId() + + ") " + getValues(); + } + } +} diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java new file mode 100644 index 0000000..c1ebbfd --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java @@ -0,0 +1,224 @@ +/* +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.complex; + +import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.complex.ComplexDataType; +import com.healthmarketscience.jackcess.complex.ComplexValue; +import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; +import com.healthmarketscience.jackcess.complex.Version; +import com.healthmarketscience.jackcess.complex.VersionHistoryColumnInfo; +import com.healthmarketscience.jackcess.impl.ColumnImpl; + +/** + * Complex column info for a column which tracking the version history of an + * "append only" memo column. + *

+ * Note, the strongly typed update/delete methods are not supported for + * version history columns (the data is supposed to be immutable). That said, + * the "raw" update/delete methods are supported for those that really + * want to muck with the version history data. + * + * @author James Ahlborn + */ +public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl + implements VersionHistoryColumnInfo +{ + private final Column _valueCol; + private final Column _modifiedCol; + + public VersionHistoryColumnInfoImpl(Column column, int complexId, + Table typeObjTable, Table flatTable) + throws IOException + { + super(column, complexId, typeObjTable, flatTable); + + Column valueCol = null; + Column modifiedCol = null; + for(Column col : getTypeColumns()) { + switch(col.getType()) { + case SHORT_DATE_TIME: + modifiedCol = col; + break; + case MEMO: + valueCol = col; + break; + default: + // ignore + } + } + + _valueCol = valueCol; + _modifiedCol = modifiedCol; + } + + @Override + public void postTableLoadInit() throws IOException { + super.postTableLoadInit(); + + // link up with the actual versioned column. it should have the same name + // as the "value" column in the type table. + Column versionedCol = getColumn().getTable().getColumn( + getValueColumn().getName()); + ((ColumnImpl)versionedCol).setVersionHistoryColumn((ColumnImpl)getColumn()); + } + + public Column getValueColumn() { + return _valueCol; + } + + public Column getModifiedDateColumn() { + return _modifiedCol; + } + + @Override + public ComplexDataType getType() { + return ComplexDataType.VERSION_HISTORY; + } + + @Override + public ComplexValue.Id updateValue(Version value) throws IOException { + throw new UnsupportedOperationException( + "This column does not support value updates"); + } + + @Override + public void deleteValue(Version value) throws IOException { + throw new UnsupportedOperationException( + "This column does not support value deletes"); + } + + @Override + public void deleteAllValues(int complexValueFk) throws IOException { + throw new UnsupportedOperationException( + "This column does not support value deletes"); + } + + @Override + protected List toValues(ComplexValueForeignKey complexValueFk, + List rawValues) + throws IOException + { + List versions = super.toValues(complexValueFk, rawValues); + + // order versions newest to oldest + Collections.sort(versions); + + return versions; + } + + @Override + protected VersionImpl toValue(ComplexValueForeignKey complexValueFk, + Row rawValue) { + ComplexValue.Id id = getValueId(rawValue); + String value = (String)getValueColumn().getRowValue(rawValue); + Date modifiedDate = (Date)getModifiedDateColumn().getRowValue(rawValue); + + return new VersionImpl(id, complexValueFk, value, modifiedDate); + } + + @Override + protected Object[] asRow(Object[] row, Version version) { + super.asRow(row, version); + getValueColumn().setRowValue(row, version.getValue()); + getModifiedDateColumn().setRowValue(row, version.getModifiedDate()); + return row; + } + + public static Version newVersion(String value, Date modifiedDate) { + return newVersion(INVALID_FK, value, modifiedDate); + } + + public static Version newVersion(ComplexValueForeignKey complexValueFk, + String value, Date modifiedDate) { + return new VersionImpl(INVALID_ID, complexValueFk, value, modifiedDate); + } + + + private static class VersionImpl extends ComplexValueImpl implements Version + { + private final String _value; + private final Date _modifiedDate; + + private VersionImpl(Id id, ComplexValueForeignKey complexValueFk, + String value, Date modifiedDate) + { + super(id, complexValueFk); + _value = value; + _modifiedDate = modifiedDate; + } + + public String getValue() { + return _value; + } + + public Date getModifiedDate() { + return _modifiedDate; + } + + public int compareTo(Version o) { + Date d1 = getModifiedDate(); + Date d2 = o.getModifiedDate(); + + // sort by descending date (newest/greatest first) + int cmp = d2.compareTo(d1); + if(cmp != 0) { + return cmp; + } + + // use id, then complexValueFk to break ties (although we really + // shouldn't be comparing across different columns) + int id1 = getId().get(); + int id2 = o.getId().get(); + if(id1 != id2) { + return ((id1 > id2) ? -1 : 1); + } + id1 = getComplexValueForeignKey().get(); + id2 = o.getComplexValueForeignKey().get(); + return ((id1 > id2) ? -1 : + ((id1 < id2) ? 1 : 0)); + } + + public void update() throws IOException { + throw new UnsupportedOperationException( + "This column does not support value updates"); + } + + public void delete() throws IOException { + throw new UnsupportedOperationException( + "This column does not support value deletes"); + } + + @Override + public String toString() + { + return "Version(" + getComplexValueForeignKey() + "," + getId() + ") " + + getModifiedDate() + ", " + getValue(); + } + } + +}