1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 import java.util.List; 29 import java.util.ListIterator; 30 import java.util.LinkedList; 31 import java.util.zip.ZipInputStream; 32 import java.util.zip.ZipOutputStream; 33 import java.util.zip.ZipEntry; 34 import java.util.zip.CRC32; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.io.IOException; 38 import java.io.ByteArrayOutputStream; 39 import org.openoffice.xmerge.util.Debug; 40 41 /** 42 * Class used by OfficeDocument to handle zip reading and writing, 43 * as well as storing zip entries. 44 * 45 * @author Herbie Ong 46 */ 47 48 class OfficeZip { 49 50 /** file name of the xml file in a zipped document. */ 51 private final static String XMLFILE = "content.xml"; 52 53 private final static int BUFFERSIZE = 1024; 54 55 private List entryList = null; 56 57 private int contentIndex = -1; 58 59 private String filename = null; 60 61 private class Entry { 62 63 ZipEntry zipEntry = null; 64 byte bytes[] = null; 65 } 66 67 /** 68 * Constructor 69 * 70 * @param filename Full Path to Zip file to process 71 * 72 */ 73 public OfficeZip(String filename) { 74 this.filename = filename; 75 } 76 77 78 /** 79 * Read each zip entry in the given InputStream object 80 * and store in entryList both the ZipEntry object as well 81 * as the bits of each entry. Return the bytes for the 82 * entry of XMLFILE. 83 * 84 * @param is InputStream object to read from 85 * @return byte[] byte array of XML file 86 * @throws IOException if any I/O error occurs 87 */ 88 89 byte[] read(InputStream is) throws IOException { 90 91 ZipInputStream zis = new ZipInputStream(is); 92 ZipEntry ze = null; 93 int i = -1; 94 95 entryList = new LinkedList(); 96 97 while ((ze = zis.getNextEntry()) != null) { 98 99 String name = ze.getName(); 100 101 Entry entry = new Entry(); 102 entry.zipEntry = ze; 103 104 Debug.log(Debug.TRACE, "reading entry: " + name); 105 106 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 107 108 int len = 0; 109 byte bytes[] = new byte[BUFFERSIZE]; 110 111 while ((len = zis.read(bytes)) > 0) { 112 baos.write(bytes, 0, len); 113 } 114 115 entry.bytes = baos.toByteArray(); 116 117 entryList.add(entry); 118 119 i++; 120 121 if (isContentXML(name)) { 122 contentIndex = i; 123 } 124 } 125 126 if (contentIndex == -1) { 127 throw new IOException(XMLFILE + " not found."); 128 } 129 130 Entry contentEntry = (Entry) entryList.get(contentIndex); 131 132 return contentEntry.bytes; 133 } 134 135 /** 136 * Write out the XMLFILE as a zip into the OutputStream object. 137 * 138 * If a zip inputstream was previously read, then use 139 * those zip contents to recreate the zip, except for XMLFILE, 140 * update it using the new content from xmlBytes. 141 * 142 * If there was no zip inputstream previously read, write 143 * XMLFILE out into the zip outputstream. 144 * 145 * @param os OutputStream object to write zip 146 * @param xmlBytes bytes of XMLFILE 147 * @throws IOException if any I/O errors occur. 148 */ 149 150 void write(OutputStream os, byte xmlBytes[]) throws IOException { 151 152 ZipOutputStream zos = new ZipOutputStream(os); 153 154 // if read was not invoked previously, store the bytes directly. 155 if (contentIndex == -1) { 156 157 Debug.log(Debug.TRACE, "Writing out " + XMLFILE + " into zip."); 158 159 ZipEntry ze = new ZipEntry(XMLFILE); 160 ze.setSize(xmlBytes.length); 161 162 CRC32 crc = new CRC32(); 163 crc.reset(); 164 crc.update(xmlBytes); 165 ze.setCrc(crc.getValue()); 166 167 ze.setTime(System.currentTimeMillis()); 168 ze.setMethod(ZipEntry.DEFLATED); 169 170 zos.putNextEntry(ze); 171 zos.write(xmlBytes); 172 173 } else { 174 175 saveEntries(zos, xmlBytes); 176 } 177 178 zos.close(); 179 } 180 181 /** 182 * Used by write method if there was a zip inputstream 183 * previously read. It would write out each ZipEntry of 184 * the previously read zip, except for XMLFILE, it would 185 * update it with new values and with the content from 186 * xmlBytes. 187 * 188 * @param os OutputStream object to write zip 189 * @param xmlBytes bytes of XMLFILE 190 * @throws ZipException if any zip I/O errors occur. 191 */ 192 193 private void saveEntries(ZipOutputStream zos, byte xmlBytes[]) 194 throws IOException { 195 196 Debug.log(Debug.TRACE, "Writing out the following entries into zip."); 197 198 ListIterator iterator = entryList.listIterator(); 199 200 while (iterator.hasNext()) { 201 202 Entry entry = (Entry) iterator.next(); 203 ZipEntry ze = entry.zipEntry; 204 205 String name = ze.getName(); 206 207 Debug.log(Debug.TRACE, "... " + name); 208 209 if (isContentXML(name)) { 210 211 // set new values for this ZipEntry 212 213 ZipEntry zipEntry = new ZipEntry(name); 214 215 zipEntry.setMethod(ze.getMethod()); 216 zipEntry.setSize(xmlBytes.length); 217 218 CRC32 crc = new CRC32(); 219 crc.reset(); 220 crc.update(xmlBytes); 221 zipEntry.setCrc(crc.getValue()); 222 223 zipEntry.setTime(System.currentTimeMillis()); 224 225 zos.putNextEntry(zipEntry); 226 zos.write(xmlBytes); 227 228 } else { 229 230 zos.putNextEntry(ze); 231 zos.write(entry.bytes); 232 } 233 } 234 } 235 236 private boolean isContentXML(String name) { 237 238 String lname = name.toLowerCase(); 239 return lname.equals(XMLFILE); 240 } 241 } 242