/* * * 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.catalog; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.swc.ISWC; import org.apache.flex.swc.ISWCComponent; import org.apache.flex.swc.ISWCDigest; import org.apache.flex.swc.ISWCFileEntry; import org.apache.flex.swc.ISWCLibrary; import org.apache.flex.swc.ISWCScript; import org.apache.flex.swc.ISWCVersion; import com.google.common.collect.Iterables; /** * Use {@link XMLStreamWriter} API to serialize an {@link ISWC} model to XML. */ public class StAXCatalogWriter implements ICatalogXMLConstants { /** * Create an catalog.xml writer. * * @param swc SWC model * @param writer writer * @throws XMLStreamException XML error * @throws FactoryConfigurationError factory error */ public StAXCatalogWriter(final ISWC swc, final Writer writer) throws XMLStreamException, FactoryConfigurationError { assert swc != null : "expect SWC model"; assert writer != null : "expect Writer"; this.swc = swc; final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); assert xmlOutputFactory != null : "Expect XMLOutputFactory implementation."; this.xmlWriter = new XMLFormatter(xmlOutputFactory.createXMLStreamWriter(writer)); } private final XMLStreamWriter xmlWriter; private final ISWC swc; /** * Write serialized XML to output. * * @throws XMLStreamException error */ public void write() throws XMLStreamException { xmlWriter.writeStartDocument(); // start XML { xmlWriter.writeStartElement(TAG_SWC); xmlWriter.writeAttribute(ATTR_XMLNS, XMLNS_SWC_CATALOG_9); writeVersions(); writeFeatures(); writeComponents(); writeLibraries(); writeFiles(); xmlWriter.writeEndElement(); } xmlWriter.writeEndDocument(); // end XML xmlWriter.flush(); xmlWriter.close(); } private static final Comparator<ISWCComponent> COMPONENT_COMPARATOR = new Comparator<ISWCComponent>() { @Override public int compare(ISWCComponent o1, ISWCComponent o2) { return o1.getQName().compareTo(o2.getQName()); } }; private void writeComponents() throws XMLStreamException { Collection<ISWCComponent> componentsList = swc.getComponents(); if (componentsList.isEmpty()) return; xmlWriter.writeStartElement(TAG_COMPONENTS); ISWCComponent[] components = componentsList.toArray(new ISWCComponent[componentsList.size()]); Arrays.sort(components, COMPONENT_COMPARATOR); for (final ISWCComponent component : components) { xmlWriter.writeEmptyElement(TAG_COMPONENT); if (component.getQName() != null) xmlWriter.writeAttribute(ATTR_CLASS_NAME, dottedQNameToColonQName(component.getQName())); if (component.getName() != null) xmlWriter.writeAttribute(ATTR_NAME, component.getName()); if (component.getURI() != null) xmlWriter.writeAttribute(ATTR_URI, component.getURI()); if (component.getIcon() != null) xmlWriter.writeAttribute(ATTR_ICON, component.getIcon()); if (component.getPreview() != null) xmlWriter.writeAttribute(ATTR_PREVIEW, component.getPreview()); } xmlWriter.writeEndElement(); } private static final Comparator<ISWCFileEntry> FILE_COMPARATOR = new Comparator<ISWCFileEntry>() { @Override public int compare(ISWCFileEntry o1, ISWCFileEntry o2) { return o1.getPath().compareTo(o2.getPath()); } }; private void writeFiles() throws XMLStreamException { Collection<ISWCFileEntry> filesCollection = swc.getFiles().values(); if (filesCollection.isEmpty()) return; xmlWriter.writeStartElement(TAG_FILES); ISWCFileEntry[] files = filesCollection.toArray(new ISWCFileEntry[filesCollection.size()]); Arrays.sort(files, FILE_COMPARATOR); for (final ISWCFileEntry file : files) { xmlWriter.writeEmptyElement(TAG_FILE); xmlWriter.writeAttribute(ATTR_PATH, file.getPath()); xmlWriter.writeAttribute(ATTR_MOD, String.valueOf(file.getLastModified())); } xmlWriter.writeEndElement(); } private static final Comparator<ISWCLibrary> LIBRARY_COMPARATOR = new Comparator<ISWCLibrary>() { @Override public int compare(ISWCLibrary o1, ISWCLibrary o2) { return o1.getPath().compareTo(o2.getPath()); } }; private static final Comparator<ISWCScript> SCRIPT_COMPARATOR = new Comparator<ISWCScript>() { @Override public int compare(ISWCScript o1, ISWCScript o2) { return o1.getName().compareTo(o2.getName()); } }; private static final Comparator<ISWCDigest> DIGEST_COMPARATOR = new Comparator<ISWCDigest>() { @Override public int compare(ISWCDigest o1, ISWCDigest o2) { return o1.getValue().compareTo(o2.getValue()); } }; private void writeLibraries() throws XMLStreamException { xmlWriter.writeStartElement(TAG_LIBRARIES); final Collection<ISWCLibrary> librariesCollection = swc.getLibraries(); final ISWCLibrary[] libraries = librariesCollection.toArray(new ISWCLibrary[librariesCollection.size()]); Arrays.sort(libraries, LIBRARY_COMPARATOR); for (final ISWCLibrary library : libraries) { xmlWriter.writeStartElement(TAG_LIBRARY); xmlWriter.writeAttribute(ATTR_PATH, library.getPath()); { final Collection<ISWCScript> scriptsList = library.getScripts(); final ISWCScript[] scripts = scriptsList.toArray(new ISWCScript[scriptsList.size()]); Arrays.sort(scripts, SCRIPT_COMPARATOR); for (final ISWCScript script : scripts) { writeLibraryScript(script); } final Collection<String> keepAS3MetadataList = library.getKeepAS3MetadataSet(); if (!keepAS3MetadataList.isEmpty()) { xmlWriter.writeStartElement(TAG_KEEP_AS3_METADATA); final String[] keepAS3Metadata = keepAS3MetadataList.toArray(new String[keepAS3MetadataList.size()]); Arrays.sort(keepAS3Metadata); for (final String metadata : keepAS3Metadata) { xmlWriter.writeEmptyElement(TAG_METADATA); xmlWriter.writeAttribute(ATTR_NAME, metadata); } xmlWriter.writeEndElement(); // end of <keep-as3-metadata> } xmlWriter.writeStartElement(TAG_DIGESTS); final Collection<ISWCDigest> digestsList = library.getDigests(); final ISWCDigest[] digests = digestsList.toArray(new ISWCDigest[digestsList.size()]); Arrays.sort(digests, DIGEST_COMPARATOR); for (final ISWCDigest digest : digests) { xmlWriter.writeEmptyElement(TAG_DIGEST); xmlWriter.writeAttribute(ATTR_TYPE, digest.getType()); xmlWriter.writeAttribute(ATTR_SIGNED, digest.isSigned() ? "true" : "false"); xmlWriter.writeAttribute(TAG_VALUE, digest.getValue()); } xmlWriter.writeEndElement(); // end of <digests> } xmlWriter.writeEndElement(); // end of <library> } xmlWriter.writeEndElement(); // end of <libraries> } private static String dottedQNameToColonQName(String qname) { int lastDotIndex = qname.lastIndexOf('.'); if (lastDotIndex == -1) return qname; final String packageName = qname.substring(0, lastDotIndex); final String baseName = qname.substring(lastDotIndex + 1); return packageName + ":" + baseName; } private static final Comparator<Map.Entry<String, DependencyType>> DEPENDENCY_MAP_ENTRY_COMPARATOR = new Comparator<Map.Entry<String, DependencyType>>() { @Override public int compare(Entry<String, DependencyType> arg0, Entry<String, DependencyType> arg1) { int result = arg0.getKey().compareTo(arg1.getKey()); if (result != 0) return result; return arg0.getValue().ordinal() - arg1.getValue().ordinal(); } }; private void writeLibraryScript(final ISWCScript script) throws XMLStreamException { xmlWriter.writeStartElement(TAG_SCRIPT); xmlWriter.writeAttribute(ATTR_NAME, script.getName()); xmlWriter.writeAttribute(ATTR_MOD, String.valueOf(script.getLastModified())); if (script.getSignatureChecksum() != null) xmlWriter.writeAttribute(ATTR_SIGNATURE_CHECKSUM, script.getSignatureChecksum()); final Set<String> definitionsSet = script.getDefinitions(); final String[] definitions = definitionsSet.toArray(new String[definitionsSet.size()]); Arrays.sort(definitions); for (final String def : definitions) { xmlWriter.writeEmptyElement(TAG_DEF); xmlWriter.writeAttribute(ATTR_ID, dottedQNameToColonQName(def)); } final Set<Map.Entry<String, DependencyType>> dependenciesSet = script.getDependencies().entries(); final ArrayList<Map.Entry<String, DependencyType>> dependencies = new ArrayList<Map.Entry<String, DependencyType>>(dependenciesSet.size()); Iterables.addAll(dependencies, dependenciesSet); Collections.sort(dependencies, DEPENDENCY_MAP_ENTRY_COMPARATOR); for (final Map.Entry<String, DependencyType> dep : dependencies) { xmlWriter.writeEmptyElement(TAG_DEP); xmlWriter.writeAttribute(ATTR_ID, dottedQNameToColonQName(dep.getKey())); xmlWriter.writeAttribute(ATTR_TYPE, String.valueOf(dep.getValue().getSymbol())); } xmlWriter.writeEndElement(); } private void writeFeatures() throws XMLStreamException { xmlWriter.writeStartElement(TAG_FEATURES); { // Like the pre-Falcon compiler, // Falcon unconditionally writes out script dependencies. xmlWriter.writeEmptyElement(TAG_FEATURE_SCRIPT_DEPS); if (!swc.getComponents().isEmpty()) xmlWriter.writeEmptyElement(TAG_FEATURE_COMPONENTS); if (!swc.getFiles().isEmpty()) xmlWriter.writeEmptyElement(TAG_FEATURE_FILES); } xmlWriter.writeEndElement(); } private void writeVersions() throws XMLStreamException { final ISWCVersion version = swc.getVersion(); if (version == null) return; xmlWriter.writeStartElement(TAG_VERSIONS); // SWC version final String swcVersion = version.getSWCVersion(); if (swcVersion != null) { xmlWriter.writeEmptyElement(TAG_SWC); xmlWriter.writeAttribute(ATTR_VERSION, swcVersion); } // Flex version final String flexVersion = version.getFlexVersion(); if (flexVersion != null) { xmlWriter.writeEmptyElement(TAG_FLEX); xmlWriter.writeAttribute(ATTR_VERSION, flexVersion); final String flexBuild = version.getFlexBuild(); if (flexBuild != null) xmlWriter.writeAttribute(ATTR_BUILD, flexBuild); final String flexMinSupportedVersion = version.getFlexMinSupportedVersion(); if (flexMinSupportedVersion != null) xmlWriter.writeAttribute(ATTR_MINIMUM_SUPPORTED_VERSION, flexMinSupportedVersion); } // Compiler version final String compilerVersion = version.getCompilerVersion(); if (compilerVersion != null) { xmlWriter.writeEmptyElement(TAG_COMPILER); final String compilerName = version.getCompilerName(); if (compilerName != null) xmlWriter.writeAttribute(ATTR_NAME, compilerName); xmlWriter.writeAttribute(ATTR_VERSION, compilerVersion); final String compilerBuild = version.getCompilerBuild(); if (compilerBuild != null) xmlWriter.writeAttribute(ATTR_BUILD, compilerBuild); } xmlWriter.writeEndElement(); } }