]> git.saurik.com Git - wxWidgets.git/blame - docs/latex/wx/arc.tex
allow customizing the string values returned by wxGridCellBoolEditor::GetValue()...
[wxWidgets.git] / docs / latex / wx / arc.tex
CommitLineData
00375592
VZ
1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2%% Name: arc.tex
3%% Purpose: Overview of the archive classes
4%% Author: M.J.Wetherell
5%% RCS-ID: $Id$
6%% Copyright: 2004 M.J.Wetherell
8795498c 7%% License: wxWindows license
00375592
VZ
8%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9
10\section{Archive formats such as zip}\label{wxarc}
11
12The archive classes handle archive formats such as zip, tar, rar and cab.
df467a9d
MW
13Currently only the wxZip classes are included. wxTar classes are under
14development at \urlref{wxCode}{http://wxcode.sf.net}.
00375592
VZ
15
16For each archive type, there are the following classes (using zip here
17as an example):
18
19\begin{twocollist}\twocolwidtha{4cm}
20\twocolitem{\helpref{wxZipInputStream}{wxzipinputstream}}{Input stream}
21\twocolitem{\helpref{wxZipOutputStream}{wxzipoutputstream}}{Output stream}
22\twocolitem{\helpref{wxZipEntry}{wxzipentry}}{Holds the meta-data for an
23entry (e.g. filename, timestamp, etc.)}
24\end{twocollist}
25
26There are also abstract wxArchive classes that can be used to write code
27that can handle any of the archive types,
28see '\helpref{Generic archive programming}{wxarcgeneric}'.
29Also see \helpref{wxFileSystem}{fs} for a higher level interface that
30can handle archive files in a generic way.
31
32The classes are designed to handle archives on both seekable streams such
33as disk files, or non-seekable streams such as pipes and sockets
34(see '\helpref{Archives on non-seekable streams}{wxarcnoseek}').
35
36\wxheading{See also}
37
38\helpref{wxFileSystem}{fs}
39
40
41\subsection{Creating an archive}\label{wxarccreate}
42
43\helpref{Archive formats such as zip}{wxarc}
44
45Call \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} to
46create each new entry in the archive, then write the entry's data.
47Another call to PutNextEntry() closes the current entry and begins the next.
48
49For example:
50
51\begin{verbatim}
52 wxFFileOutputStream out(_T("test.zip"));
53 wxZipOutputStream zip(out);
54 wxTextOutputStream txt(zip);
76ad7b36 55 wxString sep(wxFileName::GetPathSeparator());
00375592
VZ
56
57 zip.PutNextEntry(_T("entry1.txt"));
76ad7b36 58 txt << _T("Some text for entry1.txt\n");
00375592 59
76ad7b36
MW
60 zip.PutNextEntry(_T("subdir") + sep + _T("entry2.txt"));
61 txt << _T("Some text for subdir/entry2.txt\n");
00375592
VZ
62
63\end{verbatim}
64
76ad7b36
MW
65The name of each entry can be a full path, which makes it possible to
66store entries in subdirectories.
67
00375592
VZ
68
69\subsection{Extracting an archive}\label{wxarcextract}
70
71\helpref{Archive formats such as zip}{wxarc}
72
df467a9d
MW
73\helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns a pointer
74to entry object containing the meta-data for the next entry in the archive
75(and gives away ownership). Reading from the input stream then returns the
76entry's data. Eof() becomes true after an attempt has been made to read past
77the end of the entry's data.
00375592
VZ
78
79When there are no more entries, GetNextEntry() returns NULL and sets Eof().
80
81\begin{verbatim}
df467a9d 82 // 'smart pointer' type created with wxDEFINE_SCOPED_PTR_TYPE
00375592
VZ
83 wxZipEntryPtr entry;
84
85 wxFFileInputStream in(_T("test.zip"));
86 wxZipInputStream zip(in);
00375592
VZ
87
88 while (entry.reset(zip.GetNextEntry()), entry.get() != NULL)
89 {
df467a9d
MW
90 // access meta-data
91 wxString name = entry->GetName();
92 // read 'zip' to access the entry's data
00375592
VZ
93 }
94
95\end{verbatim}
96
df467a9d
MW
97The \helpref{smart pointer}{wxscopedptr} type {\em wxZipEntryPtr}
98can be created like this:
99
100\begin{verbatim}
101 #include <wx/ptr_scpd.h>
102 wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
103
104\end{verbatim}
105
00375592
VZ
106
107\subsection{Modifying an archive}\label{wxarcmodify}
108
109\helpref{Archive formats such as zip}{wxarc}
110
111To modify an existing archive, write a new copy of the archive to a new file,
112making any necessary changes along the way and transferring any unchanged
113entries using \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}.
114For archive types which compress entry data, CopyEntry() is likely to be
115much more efficient than transferring the data using Read() and Write()
116since it will copy them without decompressing and recompressing them.
117
118In general modifications are not possible without rewriting the archive,
df467a9d
MW
119though it may be possible in some limited cases. Even then, rewriting the
120archive is usually a better choice since a failure can be handled without
121losing the whole
122archive. \helpref{wxTempFileOutputStream}{wxtempfileoutputstream} can
123be helpful to do this.
00375592
VZ
124
125For example to delete all entries matching the pattern "*.txt":
126
127\begin{verbatim}
df467a9d
MW
128 wxFFileInputStreamPtr in(new wxFFileInputStream(_T("test.zip")));
129 wxTempFileOutputStream out(_T("test.zip"));
00375592 130
df467a9d 131 wxZipInputStream inzip(*in);
00375592 132 wxZipOutputStream outzip(out);
df467a9d
MW
133
134 // 'smart pointer' type created with wxDEFINE_SCOPED_PTR_TYPE
00375592
VZ
135 wxZipEntryPtr entry;
136
137 // transfer any meta-data for the archive as a whole (the zip comment
138 // in the case of zip)
139 outzip.CopyArchiveMetaData(inzip);
140
141 // call CopyEntry for each entry except those matching the pattern
142 while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL)
143 if (!entry->GetName().Matches(_T("*.txt")))
144 if (!outzip.CopyEntry(entry.release(), inzip))
145 break;
146
df467a9d
MW
147 // close the input stream by releasing the pointer to it, do this
148 // before closing the output stream so that the file can be replaced
149 in.reset();
150
151 // you can check for success as follows
152 bool success = inzip.Eof() && outzip.Close() && out.Commit();
153
154\end{verbatim}
155
156The \helpref{smart pointer}{wxscopedptr} types {\em wxZipEntryPtr}
157and {\em wxFFileInputStreamPtr} can be created like this:
158
159\begin{verbatim}
160 #include <wx/ptr_scpd.h>
161 wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
162 wxDEFINE_SCOPED_PTR_TYPE(wxFFileInputStream);
00375592
VZ
163
164\end{verbatim}
165
166
167\subsection{Looking up an archive entry by name}\label{wxarcbyname}
168
169\helpref{Archive formats such as zip}{wxarc}
170
171Also see \helpref{wxFileSystem}{fs} for a higher level interface that is
172more convenient for accessing archive entries by name.
173
174To open just one entry in an archive, the most efficient way is
175to simply search for it linearly by calling
176 \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} until the
177required entry is found. This works both for archives on seekable and
178non-seekable streams.
179
180The format of filenames in the archive is likely to be different
181from the local filename format. For example zips and tars use
182unix style names, with forward slashes as the path separator,
183and absolute paths are not allowed. So if on Windows the file
184"C:$\backslash$MYDIR$\backslash$MYFILE.TXT" is stored, then when reading
185the entry back \helpref{GetName()}{wxarchiveentryname} will return
186"MYDIR$\backslash$MYFILE.TXT". The conversion into the internal format
187and back has lost some information.
188
189So to avoid ambiguity when searching for an entry matching a local name,
190it is better to convert the local name to the archive's internal format
191and search for that:
192
193\begin{verbatim}
df467a9d 194 // 'smart pointer' type created with wxDEFINE_SCOPED_PTR_TYPE
00375592
VZ
195 wxZipEntryPtr entry;
196
197 // convert the local name we are looking for into the internal format
198 wxString name = wxZipEntry::GetInternalName(localname);
199
200 // open the zip
201 wxFFileInputStream in(_T("test.zip"));
202 wxZipInputStream zip(in);
203
204 // call GetNextEntry() until the required internal name is found
205 do {
206 entry.reset(zip.GetNextEntry());
207 }
208 while (entry.get() != NULL && entry->GetInternalName() != name);
209
210 if (entry.get() != NULL) {
211 // read the entry's data...
212 }
213
214\end{verbatim}
215
216To access several entries randomly, it is most efficient to transfer the
217entire catalogue of entries to a container such as a std::map or a
218 \helpref{wxHashMap}{wxhashmap} then entries looked up by name can be
219opened using the \helpref{OpenEntry()}{wxarchiveinputstreamopenentry} method.
220
221\begin{verbatim}
222 WX_DECLARE_STRING_HASH_MAP(wxZipEntry*, ZipCatalog);
223 ZipCatalog::iterator it;
224 wxZipEntry *entry;
225 ZipCatalog cat;
226
227 // open the zip
228 wxFFileInputStream in(_T("test.zip"));
229 wxZipInputStream zip(in);
230
231 // load the zip catalog
232 while ((entry = zip.GetNextEntry()) != NULL) {
233 wxZipEntry*& current = cat[entry->GetInternalName()];
234 // some archive formats can have multiple entries with the same name
235 // (e.g. tar) though it is an error in the case of zip
236 delete current;
237 current = entry;
238 }
239
240 // open an entry by name
241 if ((it = cat.find(wxZipEntry::GetInternalName(localname))) != cat.end()) {
242 zip.OpenEntry(*it->second);
243 // ... now read entry's data
244 }
245
246\end{verbatim}
247
248To open more than one entry simultaneously you need more than one
249underlying stream on the same archive:
250
251\begin{verbatim}
252 // opening another entry without closing the first requires another
253 // input stream for the same file
254 wxFFileInputStream in2(_T("test.zip"));
255 wxZipInputStream zip2(in2);
256 if ((it = cat.find(wxZipEntry::GetInternalName(local2))) != cat.end())
257 zip2.OpenEntry(*it->second);
258
259\end{verbatim}
260
261
262\subsection{Generic archive programming}\label{wxarcgeneric}
263
264\helpref{Archive formats such as zip}{wxarc}
265
266Also see \helpref{wxFileSystem}{fs} for a higher level interface that
267can handle archive files in a generic way.
268
269The specific archive classes, such as the wxZip classes, inherit from
270the following abstract classes which can be used to write code that can
271handle any of the archive types:
272
273\begin{twocollist}\twocolwidtha{5cm}
274\twocolitem{\helpref{wxArchiveInputStream}{wxarchiveinputstream}}{Input stream}
275\twocolitem{\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}}{Output stream}
276\twocolitem{\helpref{wxArchiveEntry}{wxarchiveentry}}{Holds the meta-data for an
277entry (e.g. filename)}
278\end{twocollist}
279
280In order to able to write generic code it's necessary to be able to create
281instances of the classes without knowing which archive type is being used.
282So there is a class factory for each archive type, derived from
283 \helpref{wxArchiveClassFactory}{wxarchiveclassfactory}, which can create
284the other classes.
285
df467a9d
MW
286For example, given {\it wxArchiveClassFactory* factory}, streams and
287entries can be created like this:
00375592
VZ
288
289\begin{verbatim}
290 // create streams without knowing their type
291 wxArchiveInputStreamPtr inarc(factory->NewStream(in));
292 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
293
294 // create an empty entry object
295 wxArchiveEntryPtr entry(factory->NewEntry());
296
297\end{verbatim}
298
df467a9d
MW
299The \helpref{smart pointer}{wxscopedptr} types {\em wxArchiveInputStreamPtr},
300{\em wxArchiveOutputStreamPtr} and {\em wxArchiveEntryPtr} would need to
301have already have been defined, which could be done like this:
302
303\begin{verbatim}
304 #include <wx/ptr_scpd.h>
305 wxDEFINE_SCOPED_PTR_TYPE(wxArchiveInputStream);
306 wxDEFINE_SCOPED_PTR_TYPE(wxArchiveOutputStream);
307 wxDEFINE_SCOPED_PTR_TYPE(wxArchiveEntry);
308
309\end{verbatim}
310
00375592
VZ
311The class factory itself can either be created explicitly:
312
313\begin{verbatim}
314 wxArchiveClassFactory *factory = new wxZipClassFactory;
315
316\end{verbatim}
317
318or using wxWidgets' \helpref{RTTI}{runtimeclassoverview}:
319
320\begin{verbatim}
321wxArchiveClassFactory *MakeFactory(const wxString& type)
322{
323 wxString name = _T("wx") + type.Left(1).Upper() +
324 type.Mid(1).Lower() + _T("ClassFactory");
325
326 wxObject *pObj = wxCreateDynamicObject(name);
327 wxArchiveClassFactory *pcf = wxDynamicCast(pObj, wxArchiveClassFactory);
328
329 if (!pcf) {
330 wxLogError(_T("can't handle '%s' archives"), type.c_str());
331 delete pObj;
332 }
333
334 return pcf;
335}
336
337\end{verbatim}
338
339
340\subsection{Archives on non-seekable streams}\label{wxarcnoseek}
341
342\helpref{Archive formats such as zip}{wxarc}
343
344In general, handling archives on non-seekable streams is done in the same
345way as for seekable streams, with a few caveats.
346
347The main limitation is that accessing entries randomly using
348 \helpref{OpenEntry()}{wxarchiveinputstreamopenentry}
349is not possible, the entries can only be accessed sequentially in the order
350they are stored within the archive.
351
352For each archive type, there will also be other limitations which will
353depend on the order the entries' meta-data is stored within the archive.
354These are not too difficult to deal with, and are outlined below.
355
356\wxheading{PutNextEntry and the entry size}
357
358When writing archives, some archive formats store the entry size before
359the entry's data (tar has this limitation, zip doesn't). In this case
360the entry's size must be passed to
361 \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} or an error
362occurs.
363
364This is only an issue on non-seekable streams, since otherwise the archive
365output stream can seek back and fix up the header once the size of the
366entry is known.
367
368For generic programming, one way to handle this is to supply the size
369whenever it is known, and rely on the error message from the output
370stream when the operation is not supported.
371
372\wxheading{GetNextEntry and the weak reference mechanism}
373
374Some archive formats do not store all an entry's meta-data before the
375entry's data (zip is an example). In this case, when reading from a
376non-seekable stream, \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry}
377can only return a partially populated \helpref{wxArchiveEntry}{wxarchiveentry}
378object - not all the fields are set.
379
380The input stream then keeps a weak reference to the entry object and
381updates it when more meta-data becomes available. A weak reference being
382one that does not prevent you from deleting the wxArchiveEntry object - the
383input stream only attempts to update it if it is still around.
384
385The documentation for each archive entry type gives the details
386of what meta-data becomes available and when. For generic programming,
387when the worst case must be assumed, you can rely on all the fields
388of wxArchiveEntry being fully populated when GetNextEntry() returns,
389with the the following exceptions:
390
391\begin{twocollist}\twocolwidtha{3cm}
392\twocolitem{\helpref{GetSize()}{wxarchiveentrysize}}{Guaranteed to be
393available after the entry has been read to \helpref{Eof()}{wxinputstreameof},
394or \helpref{CloseEntry()}{wxarchiveinputstreamcloseentry} has been called}
395\twocolitem{\helpref{IsReadOnly()}{wxarchiveentryisreadonly}}{Guaranteed to
396be available after the end of the archive has been reached, i.e. after
397GetNextEntry() returns NULL and Eof() is true}
398\end{twocollist}
399
400This mechanism allows \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}
401to always fully preserve entries' meta-data. No matter what order order
402the meta-data occurs within the archive, the input stream will always
403have read it before the output stream must write it.
404
405\wxheading{wxArchiveNotifier}
406
407Notifier objects can be used to get a notification whenever an input
408stream updates a \helpref{wxArchiveEntry}{wxarchiveentry} object's data
409via the weak reference mechanism.
410
411Consider the following code which renames an entry in an archive.
412This is the usual way to modify an entry's meta-data, simply set the
413required field before writing it with
414 \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}:
415
416\begin{verbatim}
417 wxArchiveInputStreamPtr arc(factory->NewStream(in));
418 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
419 wxArchiveEntryPtr entry;
420
421 outarc->CopyArchiveMetaData(*arc);
422
423 while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
424 if (entry->GetName() == from)
425 entry->SetName(to);
426 if (!outarc->CopyEntry(entry.release(), *arc))
427 break;
428 }
429
430 bool success = arc->Eof() && outarc->Close();
431
432\end{verbatim}
433
434However, for non-seekable streams, this technique cannot be used for
435fields such as \helpref{IsReadOnly()}{wxarchiveentryisreadonly},
436which are not necessarily set when
437 \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns. In
438this case a \helpref{wxArchiveNotifier}{wxarchivenotifier} can be used:
439
440\begin{verbatim}
441class MyNotifier : public wxArchiveNotifier
442{
443public:
444 void OnEntryUpdated(wxArchiveEntry& entry) { entry.SetIsReadOnly(false); }
445};
446
447\end{verbatim}
448
449The meta-data changes are done in your notifier's
450 \helpref{OnEntryUpdated()}{wxarchivenotifieronentryupdated} method,
451then \helpref{SetNotifier()}{wxarchiveentrynotifier} is called before
452CopyEntry():
453
454\begin{verbatim}
455 wxArchiveInputStreamPtr arc(factory->NewStream(in));
456 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
457 wxArchiveEntryPtr entry;
458 MyNotifier notifier;
459
460 outarc->CopyArchiveMetaData(*arc);
461
462 while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
463 entry->SetNotifier(notifier);
464 if (!outarc->CopyEntry(entry.release(), *arc))
465 break;
466 }
467
468 bool success = arc->Eof() && outarc->Close();
469
470\end{verbatim}
471
472SetNotifier() calls OnEntryUpdated() immediately, then the input
473stream calls it again whenever it sets more fields in the entry. Since
474OnEntryUpdated() will be called at least once, this technique always
475works even when it is not strictly necessary to use it. For example,
476changing the entry name can be done this way too and it works on seekable
477streams as well as non-seekable.
478