1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl;
18
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.UnsupportedEncodingException;
24 import java.net.URLDecoder;
25 import java.net.URLEncoder;
26 import java.nio.ByteBuffer;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import com.healthmarketscience.jackcess.RuntimeIOException;
32 import static com.healthmarketscience.jackcess.impl.OleUtil.*;
33 import com.healthmarketscience.jackcess.util.MemFileChannel;
34 import static com.healthmarketscience.jackcess.util.OleBlob.*;
35 import org.apache.commons.lang3.builder.ToStringBuilder;
36 import org.apache.poi.poifs.filesystem.DirectoryEntry;
37 import org.apache.poi.poifs.filesystem.DocumentEntry;
38 import org.apache.poi.poifs.filesystem.DocumentInputStream;
39 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
40
41
42
43
44
45
46
47
48
49
50
51 public class CompoundOleUtil implements CompoundPackageFactory
52 {
53 private static final String ENTRY_NAME_CHARSET = "UTF-8";
54 private static final String ENTRY_SEPARATOR = "/";
55 private static final String CONTENTS_ENTRY = "CONTENTS";
56
57 static {
58
59
60 POIFSFileSystem.class.getName();
61 }
62
63 public CompoundOleUtil()
64 {
65 }
66
67
68
69
70 @Override
71 public ContentImpl createCompoundPackageContent(
72 OleBlobImpl blob, String prettyName, String className, String typeName,
73 ByteBuffer blobBb, int dataBlockLen)
74 {
75 return new CompoundContentImpl(blob, prettyName, className, typeName,
76 blobBb.position(), dataBlockLen);
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90 public static DocumentEntry getDocumentEntry(String entryName,
91 DirectoryEntry dir)
92 throws IOException
93 {
94
95 List<String> entryNames = new ArrayList<String>();
96 for(String str : entryName.split(ENTRY_SEPARATOR)) {
97 if(str.length() == 0) {
98 continue;
99 }
100 entryNames.add(decodeEntryName(str));
101 }
102
103 DocumentEntry entry = null;
104 Iterator<String> iter = entryNames.iterator();
105 while(iter.hasNext()) {
106 org.apache.poi.poifs.filesystem.Entry tmpEntry = dir.getEntry(iter.next());
107 if(tmpEntry instanceof DirectoryEntry) {
108 dir = (DirectoryEntry)tmpEntry;
109 } else if(!iter.hasNext() && (tmpEntry instanceof DocumentEntry)) {
110 entry = (DocumentEntry)tmpEntry;
111 } else {
112 break;
113 }
114 }
115
116 if(entry == null) {
117 throw new FileNotFoundException("Could not find document " + entryName);
118 }
119
120 return entry;
121 }
122
123 private static String encodeEntryName(String name) {
124 try {
125 return URLEncoder.encode(name, ENTRY_NAME_CHARSET);
126 } catch(UnsupportedEncodingException e) {
127 throw new RuntimeException(e);
128 }
129 }
130
131 private static String decodeEntryName(String name) {
132 try {
133 return URLDecoder.decode(name, ENTRY_NAME_CHARSET);
134 } catch(UnsupportedEncodingException e) {
135 throw new RuntimeException(e);
136 }
137 }
138
139 private static final class CompoundContentImpl
140 extends EmbeddedPackageContentImpl
141 implements CompoundContent
142 {
143 private POIFSFileSystem _fs;
144
145 private CompoundContentImpl(
146 OleBlobImpl blob, String prettyName, String className,
147 String typeName, int position, int length)
148 {
149 super(blob, prettyName, className, typeName, position, length);
150 }
151
152 @Override
153 public ContentType getType() {
154 return ContentType.COMPOUND_STORAGE;
155 }
156
157 private POIFSFileSystem getFileSystem() throws IOException {
158 if(_fs == null) {
159 _fs = new POIFSFileSystem(MemFileChannel.newChannel(getStream(), "r"));
160 }
161 return _fs;
162 }
163
164 @Override
165 public Iterator<Entry> iterator() {
166 try {
167 return getEntries(new ArrayList<Entry>(), getFileSystem().getRoot(),
168 ENTRY_SEPARATOR).iterator();
169 } catch(IOException e) {
170 throw new RuntimeIOException(e);
171 }
172 }
173
174 @Override
175 public EntryImpl getEntry(String entryName) throws IOException {
176 return new EntryImpl(entryName,
177 getDocumentEntry(entryName, getFileSystem().getRoot()));
178 }
179
180 @Override
181 public boolean hasContentsEntry() throws IOException {
182 return getFileSystem().getRoot().hasEntry(CONTENTS_ENTRY);
183 }
184
185 @Override
186 public EntryImpl getContentsEntry() throws IOException {
187 return getEntry(CONTENTS_ENTRY);
188 }
189
190 private List<Entry> getEntries(List<Entry> entries, DirectoryEntry dir,
191 String prefix) {
192 for(org.apache.poi.poifs.filesystem.Entry entry : dir) {
193 if (entry instanceof DirectoryEntry) {
194
195 getEntries(entries, (DirectoryEntry)entry, prefix + ENTRY_SEPARATOR);
196 } else if(entry instanceof DocumentEntry) {
197
198 DocumentEntry de = (DocumentEntry)entry;
199 String entryName = prefix + encodeEntryName(entry.getName());
200 entries.add(new EntryImpl(entryName, de));
201 }
202 }
203 return entries;
204 }
205
206 @Override
207 public void close() {
208 ByteUtil.closeQuietly(_fs);
209 _fs = null;
210 super.close();
211 }
212
213 @Override
214 public String toString() {
215 ToStringBuilder sb = toString(CustomToStringStyle.builder(this));
216
217 try {
218 sb.append("hasContentsEntry", hasContentsEntry());
219 sb.append("entries", getEntries(new ArrayList<Entry>(),
220 getFileSystem().getRoot(),
221 ENTRY_SEPARATOR));
222 } catch(IOException e) {
223 sb.append("entries", "<" + e + ">");
224 }
225
226 return sb.toString();
227 }
228
229 private final class EntryImpl implements CompoundContent.Entry
230 {
231 private final String _name;
232 private final DocumentEntry _docEntry;
233
234 private EntryImpl(String name, DocumentEntry docEntry) {
235 _name = name;
236 _docEntry = docEntry;
237 }
238
239 @Override
240 public ContentType getType() {
241 return ContentType.UNKNOWN;
242 }
243
244 @Override
245 public String getName() {
246 return _name;
247 }
248
249 @Override
250 public CompoundContentImpl getParent() {
251 return CompoundContentImpl.this;
252 }
253
254 @Override
255 public OleBlobImpl getBlob() {
256 return getParent().getBlob();
257 }
258
259 @Override
260 public long length() {
261 return _docEntry.getSize();
262 }
263
264 @Override
265 public InputStream getStream() throws IOException {
266 return new DocumentInputStream(_docEntry);
267 }
268
269 @Override
270 public void writeTo(OutputStream out) throws IOException {
271 InputStream in = null;
272 try {
273 ByteUtil.copy(in = getStream(), out);
274 } finally {
275 ByteUtil.closeQuietly(in);
276 }
277 }
278
279 @Override
280 public String toString() {
281 return CustomToStringStyle.valueBuilder(this)
282 .append("name", _name)
283 .append("length", length())
284 .toString();
285 }
286 }
287 }
288
289 }