1 16 package org.apache.cocoon.serialization; 17 18 import java.io.FilterOutputStream ; 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.util.Enumeration ; 22 import java.util.zip.ZipEntry ; 23 import java.util.zip.ZipOutputStream ; 24 25 import org.apache.avalon.framework.activity.Disposable; 26 import org.apache.avalon.framework.service.ServiceException; 27 import org.apache.avalon.framework.service.ServiceManager; 28 import org.apache.avalon.framework.service.ServiceSelector; 29 import org.apache.avalon.framework.service.Serviceable; 30 import org.apache.excalibur.source.Source; 31 import org.apache.excalibur.source.SourceResolver; 32 import org.xml.sax.Attributes ; 33 import org.xml.sax.SAXException ; 34 import org.xml.sax.helpers.NamespaceSupport ; 35 36 71 72 75 78 public class ZipArchiveSerializer extends AbstractSerializer 79 implements Disposable, Serviceable { 80 81 85 public static final String ZIP_NAMESPACE = "http://apache.org/cocoon/zip-archive/1.0"; 86 87 private static final int START_STATE = 0; 88 private static final int IN_ZIP_STATE = 1; 89 private static final int IN_CONTENT_STATE = 2; 90 91 92 protected ServiceManager manager; 93 94 95 protected ServiceSelector selector; 96 97 98 protected ZipOutputStream zipOutput; 99 100 101 protected int state = START_STATE; 102 103 104 protected SourceResolver resolver; 105 106 107 protected byte[] buffer; 108 109 110 protected Serializer serializer; 111 112 113 protected int contentDepth; 114 115 116 private NamespaceSupport nsSupport = new NamespaceSupport (); 117 118 121 private SAXException exception; 122 123 124 127 public void service(ServiceManager manager) throws ServiceException { 128 this.manager = manager; 129 this.resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE); 130 } 131 132 137 public String getMimeType() { 138 return "application/zip"; 139 } 140 141 144 public void startDocument() throws SAXException { 145 this.state = START_STATE; 146 this.zipOutput = new ZipOutputStream (this.output); 147 } 148 149 155 public void startPrefixMapping(String prefix, String uri) throws SAXException { 156 if (state == IN_CONTENT_STATE && this.contentDepth > 0) { 157 super.startPrefixMapping(prefix, uri); 159 160 } else { 161 if (!uri.equals(ZIP_NAMESPACE)) { 163 this.nsSupport.declarePrefix(prefix, uri); 164 } 165 } 166 } 167 168 public void endPrefixMapping(String prefix) throws SAXException { 169 if (state == IN_CONTENT_STATE && this.contentDepth > 0) { 170 super.endPrefixMapping(prefix); 172 } 173 } 174 175 178 181 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) 182 throws SAXException { 183 184 if (this.exception != null) { 186 throw this.exception; 187 } 188 189 switch (state) { 190 case START_STATE: 191 if (namespaceURI.equals(ZIP_NAMESPACE) && localName.equals("archive")) { 193 this.nsSupport.pushContext(); 194 this.state = IN_ZIP_STATE; 195 } else { 196 throw this.exception = 197 new SAXException ("Expecting 'archive' root element (got '" + localName + "')"); 198 } 199 break; 200 201 case IN_ZIP_STATE: 202 if (namespaceURI.equals(ZIP_NAMESPACE) && localName.equals("entry")) { 204 this.nsSupport.pushContext(); 205 addEntry(atts); 207 } else { 208 throw this.exception = 209 new SAXException ("Expecting 'entry' element (got '" + localName + "')"); 210 } 211 break; 212 213 case IN_CONTENT_STATE: 214 if (this.contentDepth == 0) { 215 Enumeration prefixes = this.nsSupport.getPrefixes(); 217 while (prefixes.hasMoreElements()) { 218 String prefix = (String ) prefixes.nextElement(); 219 super.startPrefixMapping(prefix, this.nsSupport.getURI(prefix)); 220 } 221 } 222 223 this.contentDepth++; 224 super.startElement(namespaceURI, localName, qName, atts); 225 break; 226 } 227 } 228 229 232 public void characters(char[] buffer, int offset, int length) throws SAXException { 233 if (this.state == IN_CONTENT_STATE && this.contentDepth > 0) { 237 super.characters(buffer, offset, length); 238 } 239 } 240 241 245 protected void addEntry(Attributes atts) throws SAXException { 246 String name = atts.getValue("name"); 247 if (name == null) { 248 throw this.exception = 249 new SAXException ("No name given to the Zip entry"); 250 } 251 252 String src = atts.getValue("src"); 253 String serializerType = atts.getValue("serializer"); 254 255 if (src == null && serializerType == null) { 256 throw this.exception = 257 new SAXException ("No source nor serializer given for the Zip entry '" + name + "'"); 258 } 259 260 if (src != null && serializerType != null) { 261 throw this.exception = 262 new SAXException ("Cannot specify both 'src' and 'serializer' on a Zip entry '" + name + "'"); 263 } 264 265 Source source = null; 266 try { 267 ZipEntry entry = new ZipEntry (name); 269 this.zipOutput.putNextEntry(entry); 270 271 if (src != null) { 272 source = resolver.resolveURI(src); 274 InputStream sourceInput = source.getInputStream(); 275 276 if (this.buffer == null) 278 this.buffer = new byte[1024]; 279 280 int len; 282 while ((len = sourceInput.read(this.buffer)) > 0) { 283 this.zipOutput.write(this.buffer, 0, len); 284 } 285 286 this.zipOutput.closeEntry(); 288 289 } else { 290 if (this.selector == null) { 292 this.selector = 293 (ServiceSelector) this.manager.lookup(Serializer.ROLE + "Selector"); 294 } 295 296 this.serializer = (Serializer) this.selector.select(serializerType); 298 299 this.serializer.setOutputStream(new FilterOutputStream (this.zipOutput) { 302 public void close() { } 303 }); 304 305 setConsumer(serializer); 307 308 this.serializer.startDocument(); 310 311 this.state = IN_CONTENT_STATE; 312 this.contentDepth = 0; 313 } 314 315 } catch (RuntimeException re) { 316 throw re; 317 } catch (SAXException se) { 318 throw this.exception = se; 319 } catch (Exception e) { 320 throw this.exception = new SAXException (e); 321 } finally { 322 this.resolver.release( source ); 323 } 324 } 325 326 329 public void endElement(String namespaceURI, String localName, String qName) 330 throws SAXException { 331 332 if (this.exception != null) { 334 throw this.exception; 335 } 336 337 if (state == IN_CONTENT_STATE) { 338 super.endElement(namespaceURI, localName, qName); 339 this.contentDepth--; 340 341 if (this.contentDepth == 0) { 342 344 Enumeration prefixes = this.nsSupport.getPrefixes(); 346 while (prefixes.hasMoreElements()) { 347 String prefix = (String ) prefixes.nextElement(); 348 super.endPrefixMapping(prefix); 349 } 350 351 super.endDocument(); 352 353 try { 354 this.zipOutput.closeEntry(); 355 } catch (IOException ioe) { 356 throw this.exception = new SAXException (ioe); 357 } 358 359 super.setConsumer(null); 360 this.selector.release(this.serializer); 361 this.serializer = null; 362 363 this.state = IN_ZIP_STATE; 365 } 366 } else { 367 this.nsSupport.popContext(); 368 } 369 } 370 371 374 public void endDocument() throws SAXException { 375 try { 376 this.zipOutput.finish(); 378 379 } catch (IOException ioe) { 380 throw new SAXException (ioe); 381 } 382 } 383 384 387 public void recycle() { 388 this.exception = null; 389 if (this.serializer != null) { 390 this.selector.release(this.serializer); 391 } 392 if (this.selector != null) { 393 this.manager.release(this.selector); 394 } 395 396 this.nsSupport.reset(); 397 super.recycle(); 398 } 399 400 403 public void dispose() { 404 if (this.manager != null) { 405 this.manager.release(this.resolver); 406 this.resolver = null; 407 this.manager = null; 408 } 409 } 410 } 411 | Popular Tags |