git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@735 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-1.2.13
@@ -13,8 +13,7 @@ | |||
Add more methods to Database for retrieving Relationships. | |||
</action> | |||
<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> | |||
</release> | |||
<release version="1.2.12" date="2013-05-09"> |
@@ -33,9 +33,9 @@ public interface Attachment extends ComplexValue | |||
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(); | |||
@@ -166,8 +166,8 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
Date ts = (Date)getFileTimeStampColumn().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 | |||
@@ -180,7 +180,7 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
getFileTypeColumn().setRowValue(row, attachment.getFileType()); | |||
getFileFlagsColumn().setRowValue(row, attachment.getFileFlags()); | |||
getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp()); | |||
getFileDataColumn().setRowValue(row, attachment.getFileData()); | |||
getFileDataColumn().setRowValue(row, attachment.getEncodedFileData()); | |||
return row; | |||
} | |||
@@ -209,30 +209,30 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
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); | |||
} | |||
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) | |||
{ | |||
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, | |||
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, | |||
null, timeStamp, flags, decodedData); | |||
null, timeStamp, flags, encodedData); | |||
} | |||
@@ -288,11 +288,11 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
private byte[] _data; | |||
private Date _timeStamp; | |||
private Integer _flags; | |||
private byte[] _decodedData; | |||
private byte[] _encodedData; | |||
private AttachmentImpl(int id, ComplexValueForeignKey complexValueFk, | |||
String url, String name, String type, byte[] data, | |||
Date timeStamp, Integer flags, byte[] decodedData) | |||
Date timeStamp, Integer flags, byte[] encodedData) | |||
{ | |||
super(id, complexValueFk); | |||
_url = url; | |||
@@ -301,30 +301,30 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
_data = data; | |||
_timeStamp = timeStamp; | |||
_flags = flags; | |||
_decodedData = decodedData; | |||
_encodedData = encodedData; | |||
} | |||
public byte[] getFileData() throws IOException { | |||
if((_data == null) && (_decodedData != null)) { | |||
_data = encodeData(); | |||
if((_data == null) && (_encodedData != null)) { | |||
_data = decodeData(); | |||
} | |||
return _data; | |||
} | |||
public void setFileData(byte[] 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; | |||
} | |||
@@ -397,20 +397,21 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
*/ | |||
private byte[] decodeData() throws IOException { | |||
if(_data.length < WRAPPER_HEADER_SIZE) { | |||
if(_encodedData.length < WRAPPER_HEADER_SIZE) { | |||
// nothing we can do | |||
throw new IOException("Unknown encoded attachment data format"); | |||
} | |||
// read initial header info | |||
ByteBuffer bb = PageChannel.wrap(_data); | |||
ByteBuffer bb = PageChannel.wrap(_encodedData); | |||
int typeFlag = bb.getInt(); | |||
int dataLen = bb.getInt(); | |||
DataInputStream contentStream = null; | |||
try { | |||
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) { | |||
// nothing else to do | |||
@@ -465,7 +466,7 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
type, JetFormat.VERSION_12.CHARSET); | |||
int headerLen = typeBytes.remaining() + CONTENT_HEADER_SIZE; | |||
int dataLen = _decodedData.length; | |||
int dataLen = _data.length; | |||
ByteUtil.ByteStream dataStream = new ByteUtil.ByteStream( | |||
WRAPPER_HEADER_SIZE + headerLen + dataLen); | |||
@@ -494,7 +495,7 @@ public class AttachmentColumnInfo extends ComplexColumnInfo<Attachment> | |||
contentStream.write(typeBytes.array(), 0, typeBytes.remaining()); | |||
// write the _actual_ contents | |||
contentStream.write(_decodedData); | |||
contentStream.write(_data); | |||
contentStream.close(); | |||
contentStream = null; | |||
@@ -215,20 +215,20 @@ public class ComplexValueForeignKey extends Number | |||
return a; | |||
} | |||
public Attachment addDecodedAttachment(byte[] decodedData) | |||
public Attachment addEncodedAttachment(byte[] encodedData) | |||
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) | |||
throws IOException | |||
{ | |||
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); | |||
return a; | |||
} |
@@ -19,6 +19,7 @@ USA | |||
package com.healthmarketscience.jackcess; | |||
import java.nio.ByteBuffer; | |||
import java.util.Arrays; | |||
import java.util.Date; | |||
import java.util.List; | |||
@@ -189,8 +190,8 @@ public class ComplexColumnTest extends TestCase | |||
row8ValFk.addAttachment(null, "test_data.txt", "txt", | |||
getFileBytes("test_data.txt"), null, null); | |||
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); | |||
checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt", | |||
"test_data2.txt"); | |||
@@ -204,17 +205,22 @@ public class ComplexColumnTest extends TestCase | |||
null); | |||
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.update(); | |||
Attachment updated = row4ValFk.getAttachments().get(1); | |||
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())); | |||
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(); | |||
checkAttachments(4, row4ValFk, "test_data2.txt"); | |||
@@ -376,8 +382,8 @@ public class ComplexColumnTest extends TestCase | |||
assertEquals(fname, a.getFileName()); | |||
assertEquals("txt", a.getFileType()); | |||
assertTrue(Arrays.equals(getFileBytes(fname), a.getFileData())); | |||
assertTrue(Arrays.equals(getDecodedFileBytes(fname), | |||
a.getDecodedFileData())); | |||
assertTrue(Arrays.equals(getEncodedFileBytes(fname), | |||
a.getEncodedFileData())); | |||
} | |||
} | |||
} | |||
@@ -436,13 +442,13 @@ public class ComplexColumnTest extends TestCase | |||
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)) { | |||
return TEST_DEC_BYTES; | |||
return TEST_ENC_BYTES; | |||
} | |||
if("test_data2.txt".equals(fname)) { | |||
return TEST2_DEC_BYTES; | |||
return TEST2_ENC_BYTES; | |||
} | |||
throw new RuntimeException("unexpected bytes"); | |||
} | |||
@@ -457,20 +463,20 @@ public class ComplexColumnTest extends TestCase | |||
} | |||
} | |||
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(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) | |||
}; | |||
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(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) | |||
}; | |||
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."); | |||
} |