blob: dd81caad6697a3877ba3cf5fd08edf8d8ce84849 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
import java.util.List;
import java.util.ListIterator;
import java.util.LinkedList;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.CRC32;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import org.openoffice.xmerge.util.Debug;
/**
* Class used by OfficeDocument to handle zip reading and writing,
* as well as storing zip entries.
*
* @author Herbie Ong
*/
class OfficeZip {
/** file name of the xml file in a zipped document. */
private final static String XMLFILE = "content.xml";
private final static int BUFFERSIZE = 1024;
private List entryList = null;
private int contentIndex = -1;
private String filename = null;
private class Entry {
ZipEntry zipEntry = null;
byte bytes[] = null;
}
/**
* Constructor
*
* @param filename Full Path to Zip file to process
*
*/
public OfficeZip(String filename) {
this.filename = filename;
}
/**
* Read each zip entry in the given InputStream object
* and store in entryList both the ZipEntry object as well
* as the bits of each entry. Return the bytes for the
* entry of XMLFILE.
*
* @param is InputStream object to read from
* @return byte[] byte array of XML file
* @throws IOException if any I/O error occurs
*/
byte[] read(InputStream is) throws IOException {
ZipInputStream zis = new ZipInputStream(is);
ZipEntry ze = null;
int i = -1;
entryList = new LinkedList();
while ((ze = zis.getNextEntry()) != null) {
String name = ze.getName();
Entry entry = new Entry();
entry.zipEntry = ze;
Debug.log(Debug.TRACE, "reading entry: " + name);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte bytes[] = new byte[BUFFERSIZE];
while ((len = zis.read(bytes)) > 0) {
baos.write(bytes, 0, len);
}
entry.bytes = baos.toByteArray();
entryList.add(entry);
i++;
if (isContentXML(name)) {
contentIndex = i;
}
}
if (contentIndex == -1) {
throw new IOException(XMLFILE + " not found.");
}
Entry contentEntry = (Entry) entryList.get(contentIndex);
return contentEntry.bytes;
}
/**
* Write out the XMLFILE as a zip into the OutputStream object.
*
* If a zip inputstream was previously read, then use
* those zip contents to recreate the zip, except for XMLFILE,
* update it using the new content from xmlBytes.
*
* If there was no zip inputstream previously read, write
* XMLFILE out into the zip outputstream.
*
* @param os OutputStream object to write zip
* @param xmlBytes bytes of XMLFILE
* @throws IOException if any I/O errors occur.
*/
void write(OutputStream os, byte xmlBytes[]) throws IOException {
ZipOutputStream zos = new ZipOutputStream(os);
// if read was not invoked previously, store the bytes directly.
if (contentIndex == -1) {
Debug.log(Debug.TRACE, "Writing out " + XMLFILE + " into zip.");
ZipEntry ze = new ZipEntry(XMLFILE);
ze.setSize(xmlBytes.length);
CRC32 crc = new CRC32();
crc.reset();
crc.update(xmlBytes);
ze.setCrc(crc.getValue());
ze.setTime(System.currentTimeMillis());
ze.setMethod(ZipEntry.DEFLATED);
zos.putNextEntry(ze);
zos.write(xmlBytes);
} else {
saveEntries(zos, xmlBytes);
}
zos.close();
}
/**
* Used by write method if there was a zip inputstream
* previously read. It would write out each ZipEntry of
* the previously read zip, except for XMLFILE, it would
* update it with new values and with the content from
* xmlBytes.
*
* @param os OutputStream object to write zip
* @param xmlBytes bytes of XMLFILE
* @throws ZipException if any zip I/O errors occur.
*/
private void saveEntries(ZipOutputStream zos, byte xmlBytes[])
throws IOException {
Debug.log(Debug.TRACE, "Writing out the following entries into zip.");
ListIterator iterator = entryList.listIterator();
while (iterator.hasNext()) {
Entry entry = (Entry) iterator.next();
ZipEntry ze = entry.zipEntry;
String name = ze.getName();
Debug.log(Debug.TRACE, "... " + name);
if (isContentXML(name)) {
// set new values for this ZipEntry
ZipEntry zipEntry = new ZipEntry(name);
zipEntry.setMethod(ze.getMethod());
zipEntry.setSize(xmlBytes.length);
CRC32 crc = new CRC32();
crc.reset();
crc.update(xmlBytes);
zipEntry.setCrc(crc.getValue());
zipEntry.setTime(System.currentTimeMillis());
zos.putNextEntry(zipEntry);
zos.write(xmlBytes);
} else {
zos.putNextEntry(ze);
zos.write(entry.bytes);
}
}
}
private boolean isContentXML(String name) {
String lname = name.toLowerCase();
return lname.equals(XMLFILE);
}
}