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