/*
*
* 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.
*
*/
package org.apache.flex.swc.io;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamException;
import org.apache.flex.swc.ISWC;
import org.apache.flex.swc.ISWCDigest;
import org.apache.flex.swc.ISWCFileEntry;
import org.apache.flex.swc.ISWCLibrary;
import org.apache.flex.swc.SWCDigest;
import org.apache.flex.swc.catalog.StAXCatalogWriter;
import org.apache.flex.swf.Header;
import org.apache.flex.swf.Header.Compression;
import org.apache.flex.swf.io.ISWFWriterFactory;
/**
* Base class for serializing a SWC model.
*/
abstract class SWCWriterBase implements ISWCWriter
{
/**
* File name of "catalog.xml".
*/
protected static final String CATALOG_XML = "catalog.xml";
/**
* Constructor
*
* @param compressLibrarySWF - true if the library will be built compressed,
* false otherwise.
* @param enableDebug - true if the library should be build with debug
* enabled, false otherwise.
* @param enableTelemetry - true if the library should be build with telemetry
* enabled, false otherwise.
* @param swfWriterFactory - factory for creating swf writers.
*/
protected SWCWriterBase(boolean compressLibrarySWF,
boolean enableDebug, boolean enableTelemetry,
ISWFWriterFactory swfWriterFactory)
{
assert swfWriterFactory != null;
this.compressLibrarySWF = compressLibrarySWF;
this.enableDebug = enableDebug;
this.enableTelemetry = enableTelemetry;
this.swfWriterFactory = swfWriterFactory;
}
private final boolean compressLibrarySWF; // true if the library is built compressed
protected final boolean enableDebug;
protected final boolean enableTelemetry;
protected final ISWFWriterFactory swfWriterFactory;
/**
* @return true if the library is built compressed, false otherwise.
*/
public boolean compressLibrarySWF()
{
return compressLibrarySWF;
}
public Header.Compression getLibrarySWFCompression()
{
// NOTE: this does not support LZMA in SWCS (obviously).
return compressLibrarySWF() ? Compression.ZLIB : Compression.NONE;
}
/**
* This method defines the serialization order.
*/
@Override
public void write(final ISWC swc) throws IOException
{
if (swc == null)
throw new NullPointerException("SWC model can't be null.");
prepare(swc);
for (final ISWCLibrary library : swc.getLibraries())
{
writeLibrary(library);
}
// Write the catalog after the library has been written
// in case the library added a digest.
writeCatalog(swc);
for (final ISWCFileEntry fileEntry : swc.getFiles().values())
{
writeFile(fileEntry);
}
finish(swc);
}
/**
* Before writing SWC contents.
*
* @param swc SWC model
*/
abstract void prepare(ISWC swc) throws IOException;
/**
* Write "catalog.xml" to the target SWC.
*
* @param swc SWC model.
*/
abstract void writeCatalog(ISWC swc) throws IOException;
/**
* Add a library to the target SWC.
*
* @param swc SWC library.
*/
abstract void writeLibrary(ISWCLibrary swc) throws IOException;
/**
* Add a file entry to the target SWC.
*
* @param swc SWC library.
*/
abstract void writeFile(ISWCFileEntry swc) throws IOException;
/**
* Clean up resources after writing out the SWC.
*
* @param swc SWC model.
* @throws IOException
*/
abstract void finish(ISWC swc) throws IOException;
/**
* Serialize the SWC model's catalog.xml to a writer.
*
* @param swc model
*/
protected final void writeCatalogXML(ISWC swc, Writer writer)
{
try
{
final StAXCatalogWriter xmlWriter = new StAXCatalogWriter(swc, writer);
xmlWriter.write();
}
catch (XMLStreamException e)
{
throw new RuntimeException(e);
}
catch (FactoryConfigurationError e)
{
throw new RuntimeException(e);
}
}
/**
* Get a digest stream output if the library needs to create a digest
* for the library.swf.
*
* @param library
*
* @return a digest stream if a digest is needed, null otherwise.
*/
protected DigestOutputStream getDigestOutputStream(ISWCLibrary library, OutputStream outputStream)
{
if (!hasUnsignedDigest(library))
{
try
{
MessageDigest messageDigest = MessageDigest.getInstance(SWCDigest.SHA_256);
return new DigestOutputStream(outputStream, messageDigest);
}
catch (NoSuchAlgorithmException e)
{
// Eat the exception, this should never happen in a
// production environment.
}
}
return null;
}
/**
* Add the digest from the digestStream to the ISWCLibrary.
*
* @param digestStream may be null. If null no digest is created.
* @param library The library to update. May not be null.
* @throws NullPointerException if library is null.
*/
protected void addDigestToLibrary(DigestOutputStream digestStream,
ISWCLibrary library)
{
if (library == null) {
throw new NullPointerException("library may not be null");
}
if (digestStream != null)
{
SWCDigest swcDigest = new SWCDigest();
swcDigest.setType(SWCDigest.SHA_256);
swcDigest.setValue(digestStream.getMessageDigest().digest());
library.addDigest(swcDigest);
}
}
/**
* Determine if the library has an unsigned digest.
*
* @param library
* @return true if the library has an unsigned digest, false
* otherwise
*/
private boolean hasUnsignedDigest(ISWCLibrary library)
{
List<ISWCDigest> digests = library.getDigests();
for (ISWCDigest digest : digests)
{
if (!digest.isSigned()) {
return true;
}
}
return false;
}
}