/*
-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.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<Attachment>
+public interface AttachmentColumnInfo extends ComplexColumnInfo<Attachment>
{
- 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());
- }
- }
-
}
/*
-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.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<V extends ComplexValue>
+public interface ComplexColumnInfo<V extends ComplexValue>
{
- 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<Column> _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<Column> typeCols = new ArrayList<Column>();
- List<Column> otherCols = new ArrayList<Column>();
- 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<Column> getTypeColumns() {
- return _typeCols;
- }
-
- public int countValues(int complexValueFk) throws IOException {
- return getRawValues(complexValueFk,
- Collections.singleton(_complexValFkCol.getName()))
- .size();
- }
-
public List<Row> getRawValues(int complexValueFk)
- throws IOException
- {
- return getRawValues(complexValueFk, null);
- }
-
- private Iterator<Row> getComplexValFkIter(
- int complexValueFk, Collection<String> columnNames)
- throws IOException
- {
- if(_complexValIdCursor == null) {
- _complexValIdCursor = new CursorBuilder(_flatTable)
- .setIndexByColumns(_complexValFkCol)
- .toIndexCursor();
- }
+ throws IOException;
- return _complexValIdCursor.entryIterator(columnNames, complexValueFk);
- }
-
public List<Row> getRawValues(int complexValueFk,
Collection<String> columnNames)
- throws IOException
- {
- Iterator<Row> entryIter =
- getComplexValFkIter(complexValueFk, columnNames);
- if(!entryIter.hasNext()) {
- return Collections.emptyList();
- }
-
- List<Row> values = new ArrayList<Row>();
- while(entryIter.hasNext()) {
- values.add(entryIter.next());
- }
-
- return values;
- }
+ throws IOException;
public List<V> getValues(ComplexValueForeignKey complexValueFk)
- throws IOException
- {
- List<Row> rawValues = getRawValues(complexValueFk.get());
- if(rawValues.isEmpty()) {
- return Collections.emptyList();
- }
-
- return toValues(complexValueFk, rawValues);
- }
-
- protected List<V> toValues(ComplexValueForeignKey complexValueFk,
- List<Row> rawValues)
- throws IOException
- {
- List<V> values = new ArrayList<V>();
- for(Row rawValue : rawValues) {
- values.add(toValue(complexValueFk, rawValue));
- }
-
- return values;
- }
+ throws IOException;
public ComplexValue.Id addRawValue(Map<String,?> 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<? extends V> 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<? extends V> 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<? extends V> values) throws IOException {
- for(V value : values) {
- deleteValue(value);
- }
- }
-
- public void deleteAllValues(int complexValueFk) throws IOException {
- Iterator<Row> entryIter =
- getComplexValFkIter(complexValueFk, Collections.<String>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<Column> typeCols,
- List<Column> 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<? extends V> 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<? extends V> 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<? extends V> 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);
- }
- }
-
}
package com.healthmarketscience.jackcess.complex;
import java.io.IOException;
+import java.io.ObjectStreamException;
import com.healthmarketscience.jackcess.Column;
import com.healthmarketscience.jackcess.RowId;
{
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).
/*
-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
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
*
* @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<? extends ComplexValue> _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();
return get();
}
- public ComplexDataType getComplexType() {
- return getComplexInfo().getType();
- }
-
- protected ComplexColumnInfo<? extends ComplexValue> 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<Row> getRawValues() throws IOException
- {
- return getComplexInfo().getRawValues(get());
- }
-
- public List<? extends ComplexValue> getValues()
- throws IOException
- {
- if(_values == null) {
- _values = getComplexInfo().getValues(this);
- }
- return _values;
- }
-
- @SuppressWarnings("unchecked")
- public List<Version> getVersions()
- throws IOException
- {
- if(getComplexType() != ComplexDataType.VERSION_HISTORY) {
- throw new UnsupportedOperationException();
- }
- return (List<Version>)getValues();
- }
-
- @SuppressWarnings("unchecked")
- public List<Attachment> getAttachments()
- throws IOException
- {
- if(getComplexType() != ComplexDataType.ATTACHMENT) {
- throw new UnsupportedOperationException();
- }
- return (List<Attachment>)getValues();
- }
-
- @SuppressWarnings("unchecked")
- public List<SingleValue> getMultiValues()
- throws IOException
- {
- if(getComplexType() != ComplexDataType.MULTI_VALUE) {
- throw new UnsupportedOperationException();
- }
- return (List<SingleValue>)getValues();
- }
-
- @SuppressWarnings("unchecked")
- public List<UnsupportedValue> getUnsupportedValues()
- throws IOException
- {
- if(getComplexType() != ComplexDataType.UNSUPPORTED) {
- throw new UnsupportedOperationException();
- }
- return (List<UnsupportedValue>)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<String,?> 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<? extends ComplexValue> getValues() throws IOException;
+
+ public abstract List<Version> getVersions() throws IOException;
+
+ public abstract List<Attachment> getAttachments()
+ throws IOException;
+
+ public abstract List<SingleValue> getMultiValues()
+ throws IOException;
+
+ public abstract List<UnsupportedValue> 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<String,?> 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;
+
}
/*
-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.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<SingleValue>
+public interface MultiValueColumnInfo extends ComplexColumnInfo<SingleValue>
{
- 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();
- }
- }
}
/*
-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.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<UnsupportedValue>
+public interface UnsupportedColumnInfo extends ComplexColumnInfo<UnsupportedValue>
{
- public UnsupportedColumnInfo(Column column, int complexId, Table typeObjTable,
- Table flatTable)
- throws IOException
- {
- super(column, complexId, typeObjTable, flatTable);
- }
-
- public List<Column> getValueColumns() {
- return getTypeColumns();
- }
-
- @Override
- public ComplexDataType getType()
- {
- return ComplexDataType.UNSUPPORTED;
- }
-
- @Override
- protected UnsupportedValueImpl toValue(
- ComplexValueForeignKey complexValueFk,
- Row rawValue)
- {
- ComplexValue.Id id = getValueId(rawValue);
-
- Map<String,Object> values = new LinkedHashMap<String,Object>();
- 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<String,Object> values = value.getValues();
- for(Column col : getValueColumns()) {
- col.setRowValue(row, col.getRowValue(values));
- }
-
- return row;
- }
-
- public static UnsupportedValue newValue(Map<String,?> values) {
- return newValue(INVALID_FK, values);
- }
-
- public static UnsupportedValue newValue(
- ComplexValueForeignKey complexValueFk, Map<String,?> values) {
- return new UnsupportedValueImpl(INVALID_ID, complexValueFk,
- new LinkedHashMap<String,Object>(values));
- }
-
- private static class UnsupportedValueImpl extends ComplexValueImpl
- implements UnsupportedValue
- {
- private Map<String,Object> _values;
-
- private UnsupportedValueImpl(Id id, ComplexValueForeignKey complexValueFk,
- Map<String,Object> values)
- {
- super(id, complexValueFk);
- _values = values;
- }
-
- public Map<String,Object> 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();
- }
- }
}
/*
-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.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.
*
* @author James Ahlborn
*/
-public class VersionHistoryColumnInfo extends ComplexColumnInfo<Version>
+public interface VersionHistoryColumnInfo extends ComplexColumnInfo<Version>
{
- 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<Version> toValues(ComplexValueForeignKey complexValueFk,
- List<Row> rawValues)
- throws IOException
- {
- List<Version> 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();
- }
- }
-
}
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;
*/
void postTableLoadInit() throws IOException {
if(_complexInfo != null) {
- _complexInfo.postTableLoadInit();
+ ((ComplexColumnInfoImpl<? extends ComplexValue>)_complexInfo)
+ .postTableLoadInit();
}
}
// 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 {
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
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;
// 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);
}
--- /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.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<Attachment>
+ 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());
+ }
+ }
+
+}
--- /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.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<V extends ComplexValue>
+ implements ComplexColumnInfo<V>
+{
+ 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<Column> _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<Column> typeCols = new ArrayList<Column>();
+ List<Column> otherCols = new ArrayList<Column>();
+ 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<Column> getTypeColumns() {
+ return _typeCols;
+ }
+
+ public int countValues(int complexValueFk) throws IOException {
+ return getRawValues(complexValueFk,
+ Collections.singleton(_complexValFkCol.getName()))
+ .size();
+ }
+
+ public List<Row> getRawValues(int complexValueFk)
+ throws IOException
+ {
+ return getRawValues(complexValueFk, null);
+ }
+
+ private Iterator<Row> getComplexValFkIter(
+ int complexValueFk, Collection<String> columnNames)
+ throws IOException
+ {
+ if(_complexValIdCursor == null) {
+ _complexValIdCursor = new CursorBuilder(_flatTable)
+ .setIndexByColumns(_complexValFkCol)
+ .toIndexCursor();
+ }
+
+ return _complexValIdCursor.entryIterator(columnNames, complexValueFk);
+ }
+
+ public List<Row> getRawValues(int complexValueFk,
+ Collection<String> columnNames)
+ throws IOException
+ {
+ Iterator<Row> entryIter =
+ getComplexValFkIter(complexValueFk, columnNames);
+ if(!entryIter.hasNext()) {
+ return Collections.emptyList();
+ }
+
+ List<Row> values = new ArrayList<Row>();
+ while(entryIter.hasNext()) {
+ values.add(entryIter.next());
+ }
+
+ return values;
+ }
+
+ public List<V> getValues(ComplexValueForeignKey complexValueFk)
+ throws IOException
+ {
+ List<Row> rawValues = getRawValues(complexValueFk.get());
+ if(rawValues.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ return toValues(complexValueFk, rawValues);
+ }
+
+ protected List<V> toValues(ComplexValueForeignKey complexValueFk,
+ List<Row> rawValues)
+ throws IOException
+ {
+ List<V> values = new ArrayList<V>();
+ for(Row rawValue : rawValues) {
+ values.add(toValue(complexValueFk, rawValue));
+ }
+
+ return values;
+ }
+
+ public ComplexValue.Id addRawValue(Map<String,?> 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<? extends V> 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<? extends V> 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<? extends V> values) throws IOException {
+ for(V value : values) {
+ deleteValue(value);
+ }
+ }
+
+ public void deleteAllValues(int complexValueFk) throws IOException {
+ Iterator<Row> entryIter =
+ getComplexValFkIter(complexValueFk, Collections.<String>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<Column> typeCols,
+ List<Column> 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;
+ }
+ }
+
+}
--- /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.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.
+ * <p>
+ * 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<? extends ComplexValue> _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<? extends ComplexValue> 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<Row> getRawValues() throws IOException {
+ return getComplexInfo().getRawValues(get());
+ }
+
+ public List<? extends ComplexValue> getValues() throws IOException {
+ if(_values == null) {
+ _values = getComplexInfo().getValues(this);
+ }
+ return _values;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<Version> getVersions() throws IOException {
+ if(getComplexType() != ComplexDataType.VERSION_HISTORY) {
+ throw new UnsupportedOperationException();
+ }
+ return (List<Version>)getValues();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<Attachment> getAttachments() throws IOException {
+ if(getComplexType() != ComplexDataType.ATTACHMENT) {
+ throw new UnsupportedOperationException();
+ }
+ return (List<Attachment>)getValues();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<SingleValue> getMultiValues() throws IOException {
+ if(getComplexType() != ComplexDataType.MULTI_VALUE) {
+ throw new UnsupportedOperationException();
+ }
+ return (List<SingleValue>)getValues();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<UnsupportedValue> getUnsupportedValues() throws IOException {
+ if(getComplexType() != ComplexDataType.UNSUPPORTED) {
+ throw new UnsupportedOperationException();
+ }
+ return (List<UnsupportedValue>)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<String,?> 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));
+ }
+}
--- /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.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<SingleValue>
+ 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();
+ }
+ }
+}
--- /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.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<UnsupportedValue>
+ implements UnsupportedColumnInfo
+{
+
+ public UnsupportedColumnInfoImpl(Column column, int complexId,
+ Table typeObjTable, Table flatTable)
+ throws IOException
+ {
+ super(column, complexId, typeObjTable, flatTable);
+ }
+
+ public List<Column> getValueColumns() {
+ return getTypeColumns();
+ }
+
+ @Override
+ public ComplexDataType getType()
+ {
+ return ComplexDataType.UNSUPPORTED;
+ }
+
+ @Override
+ protected UnsupportedValueImpl toValue(
+ ComplexValueForeignKey complexValueFk,
+ Row rawValue)
+ {
+ ComplexValue.Id id = getValueId(rawValue);
+
+ Map<String,Object> values = new LinkedHashMap<String,Object>();
+ 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<String,Object> values = value.getValues();
+ for(Column col : getValueColumns()) {
+ col.setRowValue(row, col.getRowValue(values));
+ }
+
+ return row;
+ }
+
+ public static UnsupportedValue newValue(Map<String,?> values) {
+ return newValue(INVALID_FK, values);
+ }
+
+ public static UnsupportedValue newValue(
+ ComplexValueForeignKey complexValueFk, Map<String,?> values) {
+ return new UnsupportedValueImpl(INVALID_ID, complexValueFk,
+ new LinkedHashMap<String,Object>(values));
+ }
+
+ private static class UnsupportedValueImpl extends ComplexValueImpl
+ implements UnsupportedValue
+ {
+ private Map<String,Object> _values;
+
+ private UnsupportedValueImpl(Id id, ComplexValueForeignKey complexValueFk,
+ Map<String,Object> values)
+ {
+ super(id, complexValueFk);
+ _values = values;
+ }
+
+ public Map<String,Object> 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();
+ }
+ }
+}
--- /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.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.
+ * <p>
+ * Note, the strongly typed update/delete methods are <i>not</i> supported for
+ * version history columns (the data is supposed to be immutable). That said,
+ * the "raw" update/delete methods are supported for those that <i>really</i>
+ * want to muck with the version history data.
+ *
+ * @author James Ahlborn
+ */
+public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
+ 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<Version> toValues(ComplexValueForeignKey complexValueFk,
+ List<Row> rawValues)
+ throws IOException
+ {
+ List<Version> 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();
+ }
+ }
+
+}