git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@735 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-1.2.13
Add more methods to Database for retrieving Relationships. | Add more methods to Database for retrieving Relationships. | ||||
</action> | </action> | ||||
<action dev="jahlborn" type="update"> | <action dev="jahlborn" type="update"> | ||||
Add methods to get the actual attachment content, thanks to Lorenzo | |||||
Carrara. | |||||
Implement attachment decoding, thanks to Lorenzo Carrara. | |||||
</action> | </action> | ||||
</release> | </release> | ||||
<release version="1.2.12" date="2013-05-09"> | <release version="1.2.12" date="2013-05-09"> |
public void setFileData(byte[] data); | public void setFileData(byte[] data); | ||||
public byte[] getDecodedFileData() throws IOException; | |||||
public byte[] getEncodedFileData() throws IOException; | |||||
public void setDecodedFileData(byte[] data); | |||||
public void setEncodedFileData(byte[] data); | |||||
public String getFileName(); | public String getFileName(); | ||||
Date ts = (Date)getFileTimeStampColumn().getRowValue(rawValue); | Date ts = (Date)getFileTimeStampColumn().getRowValue(rawValue); | ||||
byte[] data = (byte[])getFileDataColumn().getRowValue(rawValue); | byte[] data = (byte[])getFileDataColumn().getRowValue(rawValue); | ||||
return new AttachmentImpl(id, complexValueFk, url, name, type, data, | |||||
ts, flags, null); | |||||
return new AttachmentImpl(id, complexValueFk, url, name, type, null, | |||||
ts, flags, data); | |||||
} | } | ||||
@Override | @Override | ||||
getFileTypeColumn().setRowValue(row, attachment.getFileType()); | getFileTypeColumn().setRowValue(row, attachment.getFileType()); | ||||
getFileFlagsColumn().setRowValue(row, attachment.getFileFlags()); | getFileFlagsColumn().setRowValue(row, attachment.getFileFlags()); | ||||
getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp()); | getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp()); | ||||
getFileDataColumn().setRowValue(row, attachment.getFileData()); | |||||
getFileDataColumn().setRowValue(row, attachment.getEncodedFileData()); | |||||
return row; | return row; | ||||
} | } | ||||
data, timeStamp, flags, null); | data, timeStamp, flags, null); | ||||
} | } | ||||
public static Attachment newDecodedAttachment(byte[] decodedData) { | |||||
return newDecodedAttachment(INVALID_COMPLEX_VALUE_ID, decodedData); | |||||
public static Attachment newEncodedAttachment(byte[] encodedData) { | |||||
return newEncodedAttachment(INVALID_COMPLEX_VALUE_ID, encodedData); | |||||
} | } | ||||
public static Attachment newDecodedAttachment( | |||||
ComplexValueForeignKey complexValueFk, byte[] decodedData) { | |||||
return newDecodedAttachment(complexValueFk, null, null, null, decodedData, | |||||
public static Attachment newEncodedAttachment( | |||||
ComplexValueForeignKey complexValueFk, byte[] encodedData) { | |||||
return newEncodedAttachment(complexValueFk, null, null, null, encodedData, | |||||
null, null); | null, null); | ||||
} | } | ||||
public static Attachment newDecodedAttachment( | |||||
String url, String name, String type, byte[] decodedData, | |||||
public static Attachment newEncodedAttachment( | |||||
String url, String name, String type, byte[] encodedData, | |||||
Date timeStamp, Integer flags) | Date timeStamp, Integer flags) | ||||
{ | { | ||||
return newDecodedAttachment(INVALID_COMPLEX_VALUE_ID, url, name, type, | |||||
decodedData, timeStamp, flags); | |||||
return newEncodedAttachment(INVALID_COMPLEX_VALUE_ID, url, name, type, | |||||
encodedData, timeStamp, flags); | |||||
} | } | ||||
public static Attachment newDecodedAttachment( | |||||
public static Attachment newEncodedAttachment( | |||||
ComplexValueForeignKey complexValueFk, String url, String name, | ComplexValueForeignKey complexValueFk, String url, String name, | ||||
String type, byte[] decodedData, Date timeStamp, Integer flags) | |||||
String type, byte[] encodedData, Date timeStamp, Integer flags) | |||||
{ | { | ||||
return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type, | return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type, | ||||
null, timeStamp, flags, decodedData); | |||||
null, timeStamp, flags, encodedData); | |||||
} | } | ||||
private byte[] _data; | private byte[] _data; | ||||
private Date _timeStamp; | private Date _timeStamp; | ||||
private Integer _flags; | private Integer _flags; | ||||
private byte[] _decodedData; | |||||
private byte[] _encodedData; | |||||
private AttachmentImpl(int id, ComplexValueForeignKey complexValueFk, | private AttachmentImpl(int id, ComplexValueForeignKey complexValueFk, | ||||
String url, String name, String type, byte[] data, | String url, String name, String type, byte[] data, | ||||
Date timeStamp, Integer flags, byte[] decodedData) | |||||
Date timeStamp, Integer flags, byte[] encodedData) | |||||
{ | { | ||||
super(id, complexValueFk); | super(id, complexValueFk); | ||||
_url = url; | _url = url; | ||||
_data = data; | _data = data; | ||||
_timeStamp = timeStamp; | _timeStamp = timeStamp; | ||||
_flags = flags; | _flags = flags; | ||||
_decodedData = decodedData; | |||||
_encodedData = encodedData; | |||||
} | } | ||||
public byte[] getFileData() throws IOException { | public byte[] getFileData() throws IOException { | ||||
if((_data == null) && (_decodedData != null)) { | |||||
_data = encodeData(); | |||||
if((_data == null) && (_encodedData != null)) { | |||||
_data = decodeData(); | |||||
} | } | ||||
return _data; | return _data; | ||||
} | } | ||||
public void setFileData(byte[] data) { | public void setFileData(byte[] data) { | ||||
_data = data; | _data = data; | ||||
_decodedData = null; | |||||
_encodedData = null; | |||||
} | } | ||||
public byte[] getDecodedFileData() throws IOException { | |||||
if((_decodedData == null) && (_data != null)) { | |||||
_decodedData = decodeData(); | |||||
public byte[] getEncodedFileData() throws IOException { | |||||
if((_encodedData == null) && (_data != null)) { | |||||
_encodedData = encodeData(); | |||||
} | } | ||||
return _decodedData; | |||||
return _encodedData; | |||||
} | } | ||||
public void setDecodedFileData(byte[] data) { | |||||
_decodedData = data; | |||||
public void setEncodedFileData(byte[] data) { | |||||
_encodedData = data; | |||||
_data = null; | _data = null; | ||||
} | } | ||||
*/ | */ | ||||
private byte[] decodeData() throws IOException { | private byte[] decodeData() throws IOException { | ||||
if(_data.length < WRAPPER_HEADER_SIZE) { | |||||
if(_encodedData.length < WRAPPER_HEADER_SIZE) { | |||||
// nothing we can do | // nothing we can do | ||||
throw new IOException("Unknown encoded attachment data format"); | throw new IOException("Unknown encoded attachment data format"); | ||||
} | } | ||||
// read initial header info | // read initial header info | ||||
ByteBuffer bb = PageChannel.wrap(_data); | |||||
ByteBuffer bb = PageChannel.wrap(_encodedData); | |||||
int typeFlag = bb.getInt(); | int typeFlag = bb.getInt(); | ||||
int dataLen = bb.getInt(); | int dataLen = bb.getInt(); | ||||
DataInputStream contentStream = null; | DataInputStream contentStream = null; | ||||
try { | try { | ||||
InputStream bin = new ByteArrayInputStream( | InputStream bin = new ByteArrayInputStream( | ||||
_data, WRAPPER_HEADER_SIZE, _data.length - WRAPPER_HEADER_SIZE); | |||||
_encodedData, WRAPPER_HEADER_SIZE, | |||||
_encodedData.length - WRAPPER_HEADER_SIZE); | |||||
if(typeFlag == DATA_TYPE_RAW) { | if(typeFlag == DATA_TYPE_RAW) { | ||||
// nothing else to do | // nothing else to do | ||||
type, JetFormat.VERSION_12.CHARSET); | type, JetFormat.VERSION_12.CHARSET); | ||||
int headerLen = typeBytes.remaining() + CONTENT_HEADER_SIZE; | int headerLen = typeBytes.remaining() + CONTENT_HEADER_SIZE; | ||||
int dataLen = _decodedData.length; | |||||
int dataLen = _data.length; | |||||
ByteUtil.ByteStream dataStream = new ByteUtil.ByteStream( | ByteUtil.ByteStream dataStream = new ByteUtil.ByteStream( | ||||
WRAPPER_HEADER_SIZE + headerLen + dataLen); | WRAPPER_HEADER_SIZE + headerLen + dataLen); | ||||
contentStream.write(typeBytes.array(), 0, typeBytes.remaining()); | contentStream.write(typeBytes.array(), 0, typeBytes.remaining()); | ||||
// write the _actual_ contents | // write the _actual_ contents | ||||
contentStream.write(_decodedData); | |||||
contentStream.write(_data); | |||||
contentStream.close(); | contentStream.close(); | ||||
contentStream = null; | contentStream = null; | ||||
return a; | return a; | ||||
} | } | ||||
public Attachment addDecodedAttachment(byte[] decodedData) | |||||
public Attachment addEncodedAttachment(byte[] encodedData) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
return addDecodedAttachment(null, null, null, decodedData, null, null); | |||||
return addEncodedAttachment(null, null, null, encodedData, null, null); | |||||
} | } | ||||
public Attachment addDecodedAttachment( | |||||
String url, String name, String type, byte[] decodedData, | |||||
public Attachment addEncodedAttachment( | |||||
String url, String name, String type, byte[] encodedData, | |||||
Date timeStamp, Integer flags) | Date timeStamp, Integer flags) | ||||
throws IOException | throws IOException | ||||
{ | { | ||||
reset(); | reset(); | ||||
Attachment a = AttachmentColumnInfo.newDecodedAttachment( | |||||
this, url, name, type, decodedData, timeStamp, flags); | |||||
Attachment a = AttachmentColumnInfo.newEncodedAttachment( | |||||
this, url, name, type, encodedData, timeStamp, flags); | |||||
getAttachmentInfo().addValue(a); | getAttachmentInfo().addValue(a); | ||||
return a; | return a; | ||||
} | } |
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.nio.ByteBuffer; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
row8ValFk.addAttachment(null, "test_data.txt", "txt", | row8ValFk.addAttachment(null, "test_data.txt", "txt", | ||||
getFileBytes("test_data.txt"), null, null); | getFileBytes("test_data.txt"), null, null); | ||||
checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt"); | checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt"); | ||||
row8ValFk.addDecodedAttachment(null, "test_data2.txt", "txt", | |||||
getDecodedFileBytes("test_data2.txt"), null, | |||||
row8ValFk.addEncodedAttachment(null, "test_data2.txt", "txt", | |||||
getEncodedFileBytes("test_data2.txt"), null, | |||||
null); | null); | ||||
checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt", | checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt", | ||||
"test_data2.txt"); | "test_data2.txt"); | ||||
null); | null); | ||||
checkAttachments(4, row4ValFk, "test_data2.txt", "test_data.txt"); | checkAttachments(4, row4ValFk, "test_data2.txt", "test_data.txt"); | ||||
a.setFileType("xml"); | |||||
a.setFileName("some_data.xml"); | |||||
byte[] newBytes = "this is not xml".getBytes("US-ASCII"); | |||||
a.setFileType("zip"); | |||||
a.setFileName("some_data.zip"); | |||||
byte[] newBytes = "this is not a zip file".getBytes("US-ASCII"); | |||||
a.setFileData(newBytes); | a.setFileData(newBytes); | ||||
a.update(); | a.update(); | ||||
Attachment updated = row4ValFk.getAttachments().get(1); | Attachment updated = row4ValFk.getAttachments().get(1); | ||||
assertNotSame(updated, a); | assertNotSame(updated, a); | ||||
assertEquals("xml", updated.getFileType()); | |||||
assertEquals("some_data.xml", updated.getFileName()); | |||||
assertEquals("zip", updated.getFileType()); | |||||
assertEquals("some_data.zip", updated.getFileName()); | |||||
assertTrue(Arrays.equals(newBytes, updated.getFileData())); | assertTrue(Arrays.equals(newBytes, updated.getFileData())); | ||||
byte[] encBytes = updated.getEncodedFileData(); | |||||
assertEquals(newBytes.length + 28, encBytes.length); | |||||
ByteBuffer bb = PageChannel.wrap(encBytes); | |||||
assertEquals(0, bb.getInt()); | |||||
assertTrue(ByteUtil.matchesRange(bb, 28, newBytes)); | |||||
updated.delete(); | updated.delete(); | ||||
checkAttachments(4, row4ValFk, "test_data2.txt"); | checkAttachments(4, row4ValFk, "test_data2.txt"); | ||||
assertEquals(fname, a.getFileName()); | assertEquals(fname, a.getFileName()); | ||||
assertEquals("txt", a.getFileType()); | assertEquals("txt", a.getFileType()); | ||||
assertTrue(Arrays.equals(getFileBytes(fname), a.getFileData())); | assertTrue(Arrays.equals(getFileBytes(fname), a.getFileData())); | ||||
assertTrue(Arrays.equals(getDecodedFileBytes(fname), | |||||
a.getDecodedFileData())); | |||||
assertTrue(Arrays.equals(getEncodedFileBytes(fname), | |||||
a.getEncodedFileData())); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
throw new RuntimeException("unexpected bytes"); | throw new RuntimeException("unexpected bytes"); | ||||
} | } | ||||
private static byte[] getDecodedFileBytes(String fname) throws Exception | |||||
private static byte[] getEncodedFileBytes(String fname) throws Exception | |||||
{ | { | ||||
if("test_data.txt".equals(fname)) { | if("test_data.txt".equals(fname)) { | ||||
return TEST_DEC_BYTES; | |||||
return TEST_ENC_BYTES; | |||||
} | } | ||||
if("test_data2.txt".equals(fname)) { | if("test_data2.txt".equals(fname)) { | ||||
return TEST2_DEC_BYTES; | |||||
return TEST2_ENC_BYTES; | |||||
} | } | ||||
throw new RuntimeException("unexpected bytes"); | throw new RuntimeException("unexpected bytes"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private static final byte[] TEST_BYTES = new byte[] { | |||||
private static final byte[] TEST_ENC_BYTES = new byte[] { | |||||
b(0x01),b(0x00),b(0x00),b(0x00),b(0x3A),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62), | b(0x01),b(0x00),b(0x00),b(0x00),b(0x3A),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62), | ||||
b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89),b(0x25),b(0x89),b(0x0A),b(0x69),b(0xF9), | b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89),b(0x25),b(0x89),b(0x0A),b(0x69),b(0xF9), | ||||
b(0x45),b(0x0A),b(0x89),b(0x25),b(0x25),b(0x89),b(0xC9),b(0x19),b(0xB9),b(0xA9),b(0x79),b(0x25),b(0x7A),b(0x00),b(0x52),b(0xA9),b(0x0F),b(0x7A) | b(0x45),b(0x0A),b(0x89),b(0x25),b(0x25),b(0x89),b(0xC9),b(0x19),b(0xB9),b(0xA9),b(0x79),b(0x25),b(0x7A),b(0x00),b(0x52),b(0xA9),b(0x0F),b(0x7A) | ||||
}; | }; | ||||
private static final byte[] TEST_DEC_BYTES = getAsciiBytes("this is some test data for attachment."); | |||||
private static final byte[] TEST_BYTES = getAsciiBytes("this is some test data for attachment."); | |||||
private static final byte[] TEST2_BYTES = new byte[] { | |||||
private static final byte[] TEST2_ENC_BYTES = new byte[] { | |||||
b(0x01),b(0x00),b(0x00),b(0x00),b(0x3F),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62), | b(0x01),b(0x00),b(0x00),b(0x00),b(0x3F),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62), | ||||
b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0xB9),b(0xF9),b(0x45),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89), | b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0xB9),b(0xF9),b(0x45),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89), | ||||
b(0x25),b(0x89),b(0x0A),b(0x69),b(0xF9),b(0x45),b(0x0A),b(0x89),b(0x25),b(0x25),b(0x89),b(0xC9),b(0x19),b(0xB9),b(0xA9),b(0x79),b(0x25),b(0x7A),b(0x00),b(0xA5),b(0x0B),b(0x11),b(0x4D) | b(0x25),b(0x89),b(0x0A),b(0x69),b(0xF9),b(0x45),b(0x0A),b(0x89),b(0x25),b(0x25),b(0x89),b(0xC9),b(0x19),b(0xB9),b(0xA9),b(0x79),b(0x25),b(0x7A),b(0x00),b(0xA5),b(0x0B),b(0x11),b(0x4D) | ||||
}; | }; | ||||
private static final byte[] TEST2_DEC_BYTES = getAsciiBytes("this is some more test data for attachment."); | |||||
private static final byte[] TEST2_BYTES = getAsciiBytes("this is some more test data for attachment."); | |||||
} | } |