Kaynağa Gözat

rework compound content api; add more ole blob unit tests

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@810 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-2.0.1
James Ahlborn 10 yıl önce
ebeveyn
işleme
1281baf89c

+ 12
- 0
src/main/java/com/healthmarketscience/jackcess/impl/ByteUtil.java Dosyayı Görüntüle

import java.io.Closeable; import java.io.Closeable;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
return newArr; return newArr;
} }


/**
* Copies the given InputStream to the given OutputStream.
*/
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[8 * 1024];
int read = 0;
while((read = in.read(buf)) > -1) {
out.write(buf, 0, read);
}
}

/** /**
* Closes the given Closeable if non-null, swallows any IOExceptions. * Closes the given Closeable if non-null, swallows any IOExceptions.
*/ */

+ 1
- 6
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java Dosyayı Görüntüle

ByteArrayOutputStream bout = new ByteArrayOutputStream(); ByteArrayOutputStream bout = new ByteArrayOutputStream();


if(value instanceof InputStream) { if(value instanceof InputStream) {
byte[] buf = new byte[8 * 1024];
InputStream in = (InputStream)value;
int read = 0;
while((read = in.read(buf)) != -1) {
bout.write(buf, 0, read);
}
ByteUtil.copy((InputStream)value, bout);
} else { } else {
// if all else fails, serialize it // if all else fails, serialize it
ObjectOutputStream oos = new ObjectOutputStream(bout); ObjectOutputStream oos = new ObjectOutputStream(bout);

+ 123
- 52
src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java Dosyayı Görüntüle

import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;


import com.healthmarketscience.jackcess.RuntimeIOException;
import static com.healthmarketscience.jackcess.impl.OleUtil.*; import static com.healthmarketscience.jackcess.impl.OleUtil.*;
import com.healthmarketscience.jackcess.util.MemFileChannel; import com.healthmarketscience.jackcess.util.MemFileChannel;
import static com.healthmarketscience.jackcess.util.OleBlob.*; import static com.healthmarketscience.jackcess.util.OleBlob.*;
import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;


/** /**
* support in OleUtil can be utilized without requiring POI. * support in OleUtil can be utilized without requiring POI.
* *
* @author James Ahlborn * @author James Ahlborn
* @usage _advanced_class_
*/ */
public class CompoundOleUtil implements OleUtil.CompoundPackageFactory public class CompoundOleUtil implements OleUtil.CompoundPackageFactory
{ {
{ {
} }


/**
* Creates a nes CompoundContent for the given blob information.
*/
public ContentImpl createCompoundPackageContent( public ContentImpl createCompoundPackageContent(
OleBlobImpl blob, String prettyName, String className, String typeName, OleBlobImpl blob, String prettyName, String className, String typeName,
ByteBuffer blobBb, int dataBlockLen) ByteBuffer blobBb, int dataBlockLen)
blobBb.position(), dataBlockLen); blobBb.position(), dataBlockLen);
} }


/**
* Gets a DocumentEntry from compound storage based on a fully qualified,
* encoded entry name.
*
* @param entryName fully qualified, encoded entry name
* @param dir root directory of the compound storage
*
* @return the relevant DocumentEntry
* @throws FileNotFoundException if the entry does not exist
* @throws IOException if some other io error occurs
*/
public static DocumentEntry getDocumentEntry(String entryName,
DirectoryEntry dir)
throws IOException
{
// split entry name into individual components and decode them
List<String> entryNames = new ArrayList<String>();
for(String str : entryName.split(ENTRY_SEPARATOR)) {
if(str.length() == 0) {
continue;
}
entryNames.add(decodeEntryName(str));
}

DocumentEntry entry = null;
Iterator<String> iter = entryNames.iterator();
while(iter.hasNext()) {
org.apache.poi.poifs.filesystem.Entry tmpEntry = dir.getEntry(iter.next());
if(tmpEntry instanceof DirectoryEntry) {
dir = (DirectoryEntry)tmpEntry;
} else if(!iter.hasNext() && (tmpEntry instanceof DocumentEntry)) {
entry = (DocumentEntry)tmpEntry;
} else {
break;
}
}
if(entry == null) {
throw new FileNotFoundException("Could not find document " + entryName);
}

return entry;
}

private static String encodeEntryName(String name) { private static String encodeEntryName(String name) {
try { try {
return URLEncoder.encode(name, ENTRY_NAME_CHARSET); return URLEncoder.encode(name, ENTRY_NAME_CHARSET);
return _fs; return _fs;
} }


public List<String> getEntries() throws IOException {
return getEntries(new ArrayList<String>(), getFileSystem().getRoot(),
ENTRY_SEPARATOR, false);
public Iterator<Entry> iterator() {
try {
return getEntries(new ArrayList<Entry>(), getFileSystem().getRoot(),
ENTRY_SEPARATOR).iterator();
} catch(IOException e) {
throw new RuntimeIOException(e);
}
} }


public InputStream getEntryStream(String entryName) throws IOException {
return new DocumentInputStream(getDocumentEntry(entryName));
public EntryImpl getEntry(String entryName) throws IOException {
return new EntryImpl(entryName,
getDocumentEntry(entryName, getFileSystem().getRoot()));
} }


public boolean hasContentsEntry() throws IOException { public boolean hasContentsEntry() throws IOException {
return getFileSystem().getRoot().hasEntry(CONTENTS_ENTRY); return getFileSystem().getRoot().hasEntry(CONTENTS_ENTRY);
} }


public InputStream getContentsEntryStream() throws IOException {
return getEntryStream(CONTENTS_ENTRY);
}

private DocumentEntry getDocumentEntry(String entryName) throws IOException {

// split entry name into individual components and decode them
List<String> entryNames = new ArrayList<String>();
for(String str : entryName.split(ENTRY_SEPARATOR)) {
if(str.length() == 0) {
continue;
}
entryNames.add(decodeEntryName(str));
}

DirectoryEntry dir = getFileSystem().getRoot();
DocumentEntry entry = null;
Iterator<String> iter = entryNames.iterator();
while(iter.hasNext()) {
Entry tmpEntry = dir.getEntry(iter.next());
if(tmpEntry instanceof DirectoryEntry) {
dir = (DirectoryEntry)tmpEntry;
} else if(!iter.hasNext() && (tmpEntry instanceof DocumentEntry)) {
entry = (DocumentEntry)tmpEntry;
} else {
break;
}
}
if(entry == null) {
throw new FileNotFoundException("Could not find document " + entryName);
}

return entry;
public EntryImpl getContentsEntry() throws IOException {
return getEntry(CONTENTS_ENTRY);
} }


private List<String> getEntries(List<String> entries, DirectoryEntry dir,
String prefix, boolean includeDetails) {
for(Entry entry : dir) {
private List<Entry> getEntries(List<Entry> entries, DirectoryEntry dir,
String prefix) {
for(org.apache.poi.poifs.filesystem.Entry entry : dir) {
if (entry instanceof DirectoryEntry) { if (entry instanceof DirectoryEntry) {
// .. recurse into this directory // .. recurse into this directory
getEntries(entries, (DirectoryEntry)entry, prefix + ENTRY_SEPARATOR,
includeDetails);
getEntries(entries, (DirectoryEntry)entry, prefix + ENTRY_SEPARATOR);
} else if(entry instanceof DocumentEntry) { } else if(entry instanceof DocumentEntry) {
// grab the entry name/detils // grab the entry name/detils
DocumentEntry de = (DocumentEntry)entry;
String entryName = prefix + encodeEntryName(entry.getName()); String entryName = prefix + encodeEntryName(entry.getName());
if(includeDetails) {
entryName += " (" + ((DocumentEntry)entry).getSize() + ")";
}
entries.add(entryName);
entries.add(new EntryImpl(entryName, de));
} }
} }
return entries; return entries;


try { try {
sb.append("hasContentsEntry", hasContentsEntry()); sb.append("hasContentsEntry", hasContentsEntry());
sb.append("entries",
getEntries(new ArrayList<String>(), getFileSystem().getRoot(),
ENTRY_SEPARATOR, true));
sb.append("entries", getEntries(new ArrayList<Entry>(),
getFileSystem().getRoot(),
ENTRY_SEPARATOR));
} catch(IOException e) { } catch(IOException e) {
sb.append("entries", "<" + e + ">"); sb.append("entries", "<" + e + ">");
} }


return sb.toString(); return sb.toString();
} }

private final class EntryImpl implements CompoundContent.Entry
{
private final String _name;
private final DocumentEntry _docEntry;

private EntryImpl(String name, DocumentEntry docEntry) {
_name = name;
_docEntry = docEntry;
}

public ContentType getType() {
return ContentType.UNKNOWN;
}

public String getName() {
return _name;
}

public CompoundContentImpl getParent() {
return CompoundContentImpl.this;
}

public OleBlobImpl getBlob() {
return getParent().getBlob();
}

public long length() {
return _docEntry.getSize();
}

public InputStream getStream() throws IOException {
return new DocumentInputStream(_docEntry);
}

public void writeTo(OutputStream out) throws IOException {
InputStream in = null;
try {
ByteUtil.copy(in = getStream(), out);
} finally {
ByteUtil.closeQuietly(in);
}
}

@Override
public String toString() {
return CustomToStringStyle.valueBuilder(this)
.append("name", _name)
.append("length", length())
.toString();
}
}
} }


} }

+ 1
- 0
src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java Dosyayı Görüntüle

* Utility code for working with OLE data. * Utility code for working with OLE data.
* *
* @author James Ahlborn * @author James Ahlborn
* @usage _advanced_class_
*/ */
public class OleUtil public class OleUtil
{ {

+ 19
- 8
src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java Dosyayı Görüntüle

import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.sql.Blob; import java.sql.Blob;
import java.util.List;


import com.healthmarketscience.jackcess.impl.OleUtil; import com.healthmarketscience.jackcess.impl.OleUtil;


* ContentType#COMPOUND_STORAGE} type. Compound storage is a complex * ContentType#COMPOUND_STORAGE} type. Compound storage is a complex
* embedding format also known as OLE2. In some situations (mostly * embedding format also known as OLE2. In some situations (mostly
* non-microsoft office file formats) the actual content is available from * non-microsoft office file formats) the actual content is available from
* the {@link #getContentsEntryStream} method (if {@link #hasContentsEntry}
* the {@link #getContentsEntry} method (if {@link #hasContentsEntry}
* returns {@code true}). In other situations (e.g. microsoft office file * returns {@code true}). In other situations (e.g. microsoft office file
* formats), the actual content is most or all of the compound content (but * formats), the actual content is most or all of the compound content (but
* retrieving the final file may be a complex operation, beyond the scope of
* retrieving the final file may be a complex operation beyond the scope of
* jackcess). Note that the CompoundContent type will only be available if * jackcess). Note that the CompoundContent type will only be available if
* the POI library is in the classpath, otherwise compound content will be * the POI library is in the classpath, otherwise compound content will be
* returned as OtherContent. * returned as OtherContent.
*/ */
public interface CompoundContent extends PackageContent, EmbeddedContent
public interface CompoundContent extends PackageContent, EmbeddedContent,
Iterable<CompoundContent.Entry>
{ {
public List<String> getEntries() throws IOException;

public InputStream getEntryStream(String entryName) throws IOException;
public Entry getEntry(String entryName) throws IOException;


public boolean hasContentsEntry() throws IOException; public boolean hasContentsEntry() throws IOException;


public InputStream getContentsEntryStream() throws IOException;
public Entry getContentsEntry() throws IOException;

/**
* A document entry in the compound storage.
*/
public interface Entry extends EmbeddedContent
{
public String getName();

/**
* Returns the CompoundContent which owns this entry.
*/
public CompoundContent getParent();
}
} }


/** /**

BIN
src/test/data/V2007/testOleV2007.accdb Dosyayı Görüntüle


+ 2
- 1
src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java Dosyayı Görüntüle

PROMOTION("testPromotion"), PROMOTION("testPromotion"),
COMPLEX("complexDataTest"), COMPLEX("complexDataTest"),
UNSUPPORTED("unsupportedFieldsTest"), UNSUPPORTED("unsupportedFieldsTest"),
LINKED("linkerTest");
LINKED("linkerTest"),
BLOB("testOle");


private final String _basename; private final String _basename;



+ 154
- 0
src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java Dosyayı Görüntüle

package com.healthmarketscience.jackcess.util; package com.healthmarketscience.jackcess.util;


import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Arrays; import java.util.Arrays;


import com.healthmarketscience.jackcess.ColumnBuilder; import com.healthmarketscience.jackcess.ColumnBuilder;
import com.healthmarketscience.jackcess.Row; import com.healthmarketscience.jackcess.Row;
import com.healthmarketscience.jackcess.Table; import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.TableBuilder; import com.healthmarketscience.jackcess.TableBuilder;
import com.healthmarketscience.jackcess.complex.Attachment;
import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
import com.healthmarketscience.jackcess.impl.ByteUtil; import com.healthmarketscience.jackcess.impl.ByteUtil;
import com.healthmarketscience.jackcess.impl.CompoundOleUtil;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*; import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;


/** /**
* *
db.close(); db.close();
} }
} }

public void testReadBlob() throws Exception
{
for(TestDB testDb : TestDB.getSupportedForBasename(Basename.BLOB, true)) {
Database db = open(testDb);

Table t = db.getTable("Table1");

for(Row row : t) {

OleBlob oleBlob = null;
try {

String name = (String)row.get("name");
oleBlob = OleBlob.Builder.fromInternalData((byte[])row.get("ole_data"));
OleBlob.Content content = oleBlob.getContent();
Attachment attach = null;
if(content.getType() != OleBlob.ContentType.LINK) {
attach = ((ComplexValueForeignKey)row.get("attach_data"))
.getAttachments().get(0);
}

switch(content.getType()) {
case LINK:
OleBlob.LinkContent lc = (OleBlob.LinkContent)content;
if("test_link".equals(name)) {
assertEquals("Z:\\jackcess_test\\ole\\test_data.txt", lc.getLinkPath());
} else {
assertEquals("Z:\\jackcess_test\\ole\\test_datau2.txt", lc.getLinkPath());
}
break;

case SIMPLE_PACKAGE:
OleBlob.SimplePackageContent spc = (OleBlob.SimplePackageContent)content;
byte[] packageBytes = toByteArray(spc.getStream(), spc.length());
assertTrue(Arrays.equals(attach.getFileData(), packageBytes));
break;

case COMPOUND_STORAGE:
OleBlob.CompoundContent cc = (OleBlob.CompoundContent)content;
if(cc.hasContentsEntry()) {
OleBlob.CompoundContent.Entry entry = cc.getContentsEntry();
byte[] entryBytes = toByteArray(entry.getStream(), entry.length());
assertTrue(Arrays.equals(attach.getFileData(), entryBytes));
} else {

if("test_word.doc".equals(name)) {
checkCompoundEntries(cc,
"/%02OlePres000", 466,
"/WordDocument", 4096,
"/%05SummaryInformation", 4096,
"/%05DocumentSummaryInformation", 4096,
"/%03AccessObjSiteData", 56,
"/%02OlePres001", 1620,
"/1Table", 6380,
"/%01CompObj", 114,
"/%01Ole", 20);
checkCompoundStorage(cc, attach);
} else if("test_excel.xls".equals(name)) {
checkCompoundEntries(cc,
"/%02OlePres000", 1326,
"/%03AccessObjSiteData", 56,
"/%05SummaryInformation", 200,
"/%05DocumentSummaryInformation", 264,
"/%02OlePres001", 4208,
"/%01CompObj", 107,
"/Workbook", 13040,
"/%01Ole", 20);
// the excel data seems to be modified when embedded as ole,
// so we can't reallly test it against the attachment data
} else {
throw new RuntimeException("unexpected compound entry " + name);
}
}
break;

case OTHER:
OleBlob.OtherContent oc = (OleBlob.OtherContent)content;
byte[] otherBytes = toByteArray(oc.getStream(), oc.length());
assertTrue(Arrays.equals(attach.getFileData(), otherBytes));
break;

default:
throw new RuntimeException("unexpected type " + content.getType());
}

} finally {
ByteUtil.closeQuietly(oleBlob);
}
}

db.close();
}
}

private static void checkCompoundEntries(OleBlob.CompoundContent cc,
Object... entryInfo)
throws Exception
{
int idx = 0;
for(OleBlob.CompoundContent.Entry e : cc) {
String entryName = (String)entryInfo[idx];
int entryLen = (Integer)entryInfo[idx + 1];

assertEquals(entryName, e.getName());
assertEquals(entryLen, e.length());

idx += 2;
}
}

private static void checkCompoundStorage(OleBlob.CompoundContent cc,
Attachment attach)
throws Exception
{
File tmpData = File.createTempFile("attach_", ".dat");

try {
FileOutputStream fout = new FileOutputStream(tmpData);
fout.write(attach.getFileData());
fout.close();

NPOIFSFileSystem attachFs = new NPOIFSFileSystem(tmpData, true);

for(OleBlob.CompoundContent.Entry e : cc) {
DocumentEntry attachE = null;
try {
attachE = CompoundOleUtil.getDocumentEntry(e.getName(), attachFs.getRoot());
} catch(FileNotFoundException fnfe) {
// ignored, the ole data has extra entries
continue;
}

byte[] attachEBytes = toByteArray(new DocumentInputStream(attachE),
attachE.getSize());
byte[] entryBytes = toByteArray(e.getStream(), e.length());

assertTrue(Arrays.equals(attachEBytes, entryBytes));
}

ByteUtil.closeQuietly(attachFs);
} finally {
tmpData.delete();
}
}
} }

Loading…
İptal
Kaydet