| /************************************************************** |
| * |
| * 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); |
| } |
| } |