com.jswiff.xml.RecordXMLWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.jswiff.xml.RecordXMLWriter.java

Source

/*
 * JSwiff is an open source Java API for Macromedia Flash file generation
 * and manipulation
 *
 * Copyright (C) 2004-2006 Ralf Terdic (contact@jswiff.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.jswiff.xml;

import com.jswiff.swfrecords.AlignmentZone;
import com.jswiff.swfrecords.AlphaBitmapData;
import com.jswiff.swfrecords.AlphaColorMapData;
import com.jswiff.swfrecords.BevelFilter;
import com.jswiff.swfrecords.BitmapData;
import com.jswiff.swfrecords.BitmapPixelData;
import com.jswiff.swfrecords.BlendMode;
import com.jswiff.swfrecords.BlurFilter;
import com.jswiff.swfrecords.ButtonCondAction;
import com.jswiff.swfrecords.ButtonRecord;
import com.jswiff.swfrecords.CXform;
import com.jswiff.swfrecords.CXformWithAlpha;
import com.jswiff.swfrecords.ClipActionRecord;
import com.jswiff.swfrecords.ClipActions;
import com.jswiff.swfrecords.ClipEventFlags;
import com.jswiff.swfrecords.Color;
import com.jswiff.swfrecords.ColorMapData;
import com.jswiff.swfrecords.ColorMatrixFilter;
import com.jswiff.swfrecords.ConvolutionFilter;
import com.jswiff.swfrecords.CurvedEdgeRecord;
import com.jswiff.swfrecords.DropShadowFilter;
import com.jswiff.swfrecords.EnhancedStrokeStyle;
import com.jswiff.swfrecords.FillStyle;
import com.jswiff.swfrecords.FillStyleArray;
import com.jswiff.swfrecords.FocalGradient;
import com.jswiff.swfrecords.FocalMorphGradient;
import com.jswiff.swfrecords.GlowFilter;
import com.jswiff.swfrecords.GlyphEntry;
import com.jswiff.swfrecords.GradRecord;
import com.jswiff.swfrecords.Gradient;
import com.jswiff.swfrecords.GradientBevelFilter;
import com.jswiff.swfrecords.GradientGlowFilter;
import com.jswiff.swfrecords.LineStyle;
import com.jswiff.swfrecords.LineStyle2;
import com.jswiff.swfrecords.LineStyleArray;
import com.jswiff.swfrecords.Matrix;
import com.jswiff.swfrecords.MorphFillStyle;
import com.jswiff.swfrecords.MorphFillStyles;
import com.jswiff.swfrecords.MorphGradRecord;
import com.jswiff.swfrecords.MorphGradient;
import com.jswiff.swfrecords.MorphLineStyle;
import com.jswiff.swfrecords.MorphLineStyle2;
import com.jswiff.swfrecords.MorphLineStyles;
import com.jswiff.swfrecords.Pix15;
import com.jswiff.swfrecords.Pix24;
import com.jswiff.swfrecords.RGB;
import com.jswiff.swfrecords.RGBA;
import com.jswiff.swfrecords.Rect;
import com.jswiff.swfrecords.Shape;
import com.jswiff.swfrecords.ShapeRecord;
import com.jswiff.swfrecords.ShapeWithStyle;
import com.jswiff.swfrecords.SoundEnvelope;
import com.jswiff.swfrecords.SoundInfo;
import com.jswiff.swfrecords.StraightEdgeRecord;
import com.jswiff.swfrecords.StyleChangeRecord;
import com.jswiff.swfrecords.TextRecord;
import com.jswiff.swfrecords.ZlibBitmapData;
import com.jswiff.swfrecords.actions.Action;
import com.jswiff.swfrecords.actions.ActionBlock;
import com.jswiff.util.Base64;
import com.jswiff.util.StringUtilities;

import org.dom4j.Element;

import java.util.Iterator;
import java.util.List;

/*
 * Writes SWF records (everything that's neither a tag nor an action) to XML.
 */
class RecordXMLWriter {
    static void writeActionBlock(Element parentElement, ActionBlock actionBlock) {
        List actions = actionBlock.getActions();
        Element element = parentElement.addElement("actionblock");
        for (Iterator it = actions.iterator(); it.hasNext();) {
            Action action = (Action) it.next();
            ActionXMLWriter.writeAction(element, action);
        }
    }

    static void writeAlignmentZones(Element parentElement, AlignmentZone[] alignmentZones) {
        Element element = parentElement.addElement("zones");
        for (int i = 0; i < alignmentZones.length; i++) {
            AlignmentZone zone = alignmentZones[i];
            Element zoneElement = element.addElement("zone");
            if (zone.hasX()) {
                zoneElement.addAttribute("left", StringUtilities.doubleToString(zone.getLeft()));
                zoneElement.addAttribute("width", StringUtilities.doubleToString(zone.getWidth()));
            }
            if (zone.hasY()) {
                zoneElement.addAttribute("baseline", StringUtilities.doubleToString(zone.getBaseline()));
                zoneElement.addAttribute("height", StringUtilities.doubleToString(zone.getHeight()));
            }
        }
    }

    static void writeButtonCondAction(Element parentElement, ButtonCondAction buttonCondAction) {
        Element element = parentElement.addElement("buttoncondaction");
        if (buttonCondAction.isOutDownToIdle()) {
            element.addAttribute("outdowntoidle", "true");
        }
        if (buttonCondAction.isOutDownToOverDown()) {
            element.addAttribute("outdowntooverdown", "true");
        }
        if (buttonCondAction.isIdleToOverDown()) {
            element.addAttribute("idletooverdown", "true");
        }
        if (buttonCondAction.isIdleToOverUp()) {
            element.addAttribute("idletooverup", "true");
        }
        if (buttonCondAction.isOverDownToIdle()) {
            element.addAttribute("overdowntoidle", "true");
        }
        if (buttonCondAction.isOverDownToOutDown()) {
            element.addAttribute("overdowntooutdown", "true");
        }
        if (buttonCondAction.isOverDownToOverUp()) {
            element.addAttribute("overdowntooverup", "true");
        }
        if (buttonCondAction.isOverUpToIdle()) {
            element.addAttribute("overuptoidle", "true");
        }
        if (buttonCondAction.isOverUpToOverDown()) {
            element.addAttribute("overuptooverdown", "true");
        }
        byte keyPress = buttonCondAction.getKeyPress();
        if (keyPress != 0) {
            element.addAttribute("keypress", Byte.toString(keyPress));
        }
        RecordXMLWriter.writeActionBlock(element, buttonCondAction.getActions());
    }

    static void writeButtonRecord(Element parentElement, ButtonRecord buttonRecord) {
        Element element = parentElement.addElement("buttonrecord");
        element.addAttribute("charid", Integer.toString(buttonRecord.getCharacterId()));
        element.addAttribute("depth", Integer.toString(buttonRecord.getPlaceDepth()));
        if (buttonRecord.hasBlendMode()) {
            element.addAttribute("blendmode", BlendMode.getDescription(buttonRecord.getBlendMode()));
        }
        Element state = element.addElement("state");
        if (buttonRecord.isUpState()) {
            state.addAttribute("up", "true");
        }
        if (buttonRecord.isDownState()) {
            state.addAttribute("down", "true");
        }
        if (buttonRecord.isOverState()) {
            state.addAttribute("over", "true");
        }
        if (buttonRecord.isHitState()) {
            state.addAttribute("hit", "true");
        }
        writeMatrix(element, buttonRecord.getPlaceMatrix());
        CXformWithAlpha colorTransform = buttonRecord.getColorTransform();
        if (colorTransform != null) {
            writeCXFormWithAlpha(element, colorTransform);
        }
        if (buttonRecord.hasFilters()) {
            RecordXMLWriter.writeFilters(element, buttonRecord.getFilters());
        }
    }

    static void writeCXForm(Element parentElement, CXform cXform) {
        Element element = parentElement.addElement("cxform");
        if (cXform.hasAddTerms()) {
            Element add = element.addElement("add");
            add.addAttribute("r", Integer.toString(cXform.getRedAddTerm()));
            add.addAttribute("g", Integer.toString(cXform.getGreenAddTerm()));
            add.addAttribute("b", Integer.toString(cXform.getBlueAddTerm()));
        }
        if (cXform.hasMultTerms()) {
            Element mult = element.addElement("mult");
            mult.addAttribute("r", Integer.toString(cXform.getRedMultTerm()));
            mult.addAttribute("g", Integer.toString(cXform.getGreenMultTerm()));
            mult.addAttribute("b", Integer.toString(cXform.getBlueMultTerm()));
        }
    }

    static void writeCXFormWithAlpha(Element parentElement, CXformWithAlpha cXformWithAlpha) {
        Element element = parentElement.addElement("cxformwithalpha");
        if (cXformWithAlpha.hasAddTerms()) {
            Element add = element.addElement("add");
            add.addAttribute("r", Integer.toString(cXformWithAlpha.getRedAddTerm()));
            add.addAttribute("g", Integer.toString(cXformWithAlpha.getGreenAddTerm()));
            add.addAttribute("b", Integer.toString(cXformWithAlpha.getBlueAddTerm()));
            add.addAttribute("a", Integer.toString(cXformWithAlpha.getAlphaAddTerm()));
        }
        if (cXformWithAlpha.hasMultTerms()) {
            Element mult = element.addElement("mult");
            mult.addAttribute("r", Integer.toString(cXformWithAlpha.getRedMultTerm()));
            mult.addAttribute("g", Integer.toString(cXformWithAlpha.getGreenMultTerm()));
            mult.addAttribute("b", Integer.toString(cXformWithAlpha.getBlueMultTerm()));
            mult.addAttribute("a", Integer.toString(cXformWithAlpha.getAlphaMultTerm()));
        }
    }

    static void writeClipActions(Element parentElement, ClipActions clipActions) {
        Element element = parentElement.addElement("clipactions");
        writeClipEventFlags(element, clipActions.getEventFlags());
        List records = clipActions.getClipActionRecords();
        for (Iterator it = records.iterator(); it.hasNext();) {
            ClipActionRecord record = (ClipActionRecord) it.next();
            Element recordElement = element.addElement("clipactionrecord");
            ClipEventFlags eventFlags = record.getEventFlags();
            writeClipEventFlags(recordElement, eventFlags);
            if (eventFlags.isKeyPress()) {
                recordElement.addAttribute("keycode", Short.toString(record.getKeyCode()));
            }
            writeActionBlock(recordElement, record.getActions());
        }
    }

    static void writeColor(Element parentElement, String elementName, Color color) {
        if (color instanceof RGB) {
            writeRGB(parentElement, elementName, (RGB) color);
        } else {
            writeRGBA(parentElement, elementName, (RGBA) color);
        }
    }

    static void writeFilters(Element parentElement, List filters) {
        Element element = parentElement.addElement("filters");
        for (int j = 0; j < filters.size(); j++) {
            Object filter = filters.get(j);
            if (filter instanceof ColorMatrixFilter) {
                ColorMatrixFilter colorMatrixFilter = (ColorMatrixFilter) filter;
                Element filterElement = element.addElement("colormatrix");
                float[] matrix = colorMatrixFilter.getMatrix();
                for (int i = 0; i < matrix.length; i++) {
                    filterElement.addElement("val").addText(StringUtilities.doubleToString(matrix[i]));
                }
            } else if (filter instanceof ConvolutionFilter) {
                ConvolutionFilter convolutionFilter = (ConvolutionFilter) filter;
                Element filterElement = element.addElement("convolution");
                Element matrixElement = filterElement.addElement("matrix");
                matrixElement.addAttribute("rows", Integer.toString(convolutionFilter.getMatrixRows()));
                float[] matrix = convolutionFilter.getMatrix();
                for (int i = 0; i < matrix.length; i++) {
                    matrixElement.addElement("val").addText(StringUtilities.doubleToString(matrix[i]));
                }
                writeRGBA(filterElement, "color", convolutionFilter.getColor());
                filterElement.addAttribute("divisor",
                        StringUtilities.doubleToString(convolutionFilter.getDivisor()));
                filterElement.addAttribute("bias", StringUtilities.doubleToString(convolutionFilter.getBias()));
                if (convolutionFilter.isClamp()) {
                    filterElement.addAttribute("clamp", "true");
                }
                if (convolutionFilter.isPreserveAlpha()) {
                    filterElement.addAttribute("preservealpha", "true");
                }
            } else if (filter instanceof BlurFilter) {
                BlurFilter blurFilter = (BlurFilter) filter;
                Element filterElement = element.addElement("blur");
                filterElement.addAttribute("x", StringUtilities.doubleToString(blurFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(blurFilter.getY()));
                filterElement.addAttribute("quality", Integer.toString(blurFilter.getQuality()));
            } else if (filter instanceof DropShadowFilter) {
                DropShadowFilter dropShadowFilter = (DropShadowFilter) filter;
                Element filterElement = element.addElement("dropshadow");
                writeRGBA(filterElement, "color", dropShadowFilter.getColor());
                filterElement.addAttribute("x", StringUtilities.doubleToString(dropShadowFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(dropShadowFilter.getY()));
                filterElement.addAttribute("angle", StringUtilities.doubleToString(dropShadowFilter.getAngle()));
                filterElement.addAttribute("distance",
                        StringUtilities.doubleToString(dropShadowFilter.getDistance()));
                filterElement.addAttribute("strength",
                        StringUtilities.doubleToString(dropShadowFilter.getStrength()));
                filterElement.addAttribute("quality", Integer.toString(dropShadowFilter.getQuality()));
                if (dropShadowFilter.isInner()) {
                    filterElement.addAttribute("inner", "true");
                }
                if (dropShadowFilter.isKnockout()) {
                    filterElement.addAttribute("knockout", "true");
                }
                if (dropShadowFilter.isHideObject()) {
                    filterElement.addAttribute("hideobject", "true");
                }
            } else if (filter instanceof GlowFilter) {
                GlowFilter glowFilter = (GlowFilter) filter;
                Element filterElement = element.addElement("glow");
                writeRGBA(filterElement, "color", glowFilter.getColor());
                filterElement.addAttribute("x", StringUtilities.doubleToString(glowFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(glowFilter.getY()));
                filterElement.addAttribute("strength", StringUtilities.doubleToString(glowFilter.getStrength()));
                filterElement.addAttribute("quality", Integer.toString(glowFilter.getQuality()));
                if (glowFilter.isInner()) {
                    filterElement.addAttribute("inner", "true");
                }
                if (glowFilter.isKnockout()) {
                    filterElement.addAttribute("knockout", "true");
                }
            } else if (filter instanceof BevelFilter) {
                BevelFilter bevelFilter = (BevelFilter) filter;
                Element filterElement = element.addElement("bevel");
                writeRGBA(filterElement, "highlightcolor", bevelFilter.getHighlightColor());
                writeRGBA(filterElement, "shadowcolor", bevelFilter.getShadowColor());
                filterElement.addAttribute("x", StringUtilities.doubleToString(bevelFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(bevelFilter.getY()));
                filterElement.addAttribute("angle", StringUtilities.doubleToString(bevelFilter.getAngle()));
                filterElement.addAttribute("distance", StringUtilities.doubleToString(bevelFilter.getDistance()));
                filterElement.addAttribute("strength", StringUtilities.doubleToString(bevelFilter.getStrength()));
                filterElement.addAttribute("quality", Integer.toString(bevelFilter.getQuality()));
                if (bevelFilter.isInner()) {
                    filterElement.addAttribute("inner", "true");
                }
                if (bevelFilter.isKnockout()) {
                    filterElement.addAttribute("knockout", "true");
                }
                if (bevelFilter.isOnTop()) {
                    filterElement.addAttribute("ontop", "true");
                }
            } else if (filter instanceof GradientGlowFilter) {
                GradientGlowFilter gradientGlowFilter = (GradientGlowFilter) filter;
                Element filterElement = element.addElement("gradientglow");
                RGBA[] colors = gradientGlowFilter.getColors();
                short[] ratios = gradientGlowFilter.getRatios();
                int controlPointsCount = colors.length;
                Element pointsElement = filterElement.addElement("controlpoints");
                for (int i = 0; i < controlPointsCount; i++) {
                    Element pointElement = pointsElement.addElement("controlpoint");
                    pointElement.addAttribute("ratio", Short.toString(ratios[i]));
                    writeRGBA(pointElement, "color", colors[i]);
                }
                filterElement.addAttribute("x", StringUtilities.doubleToString(gradientGlowFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(gradientGlowFilter.getY()));
                filterElement.addAttribute("angle", StringUtilities.doubleToString(gradientGlowFilter.getAngle()));
                filterElement.addAttribute("distance",
                        StringUtilities.doubleToString(gradientGlowFilter.getDistance()));
                filterElement.addAttribute("strength",
                        StringUtilities.doubleToString(gradientGlowFilter.getStrength()));
                filterElement.addAttribute("quality", Integer.toString(gradientGlowFilter.getQuality()));
                if (gradientGlowFilter.isInner()) {
                    filterElement.addAttribute("inner", "true");
                }
                if (gradientGlowFilter.isKnockout()) {
                    filterElement.addAttribute("knockout", "true");
                }
                if (gradientGlowFilter.isOnTop()) {
                    filterElement.addAttribute("ontop", "true");
                }
            } else if (filter instanceof GradientBevelFilter) {
                GradientBevelFilter gradientBevelFilter = (GradientBevelFilter) filter;
                Element filterElement = element.addElement("gradientbevel");
                RGBA[] colors = gradientBevelFilter.getColors();
                short[] ratios = gradientBevelFilter.getRatios();
                int controlPointsCount = colors.length;
                Element pointsElement = filterElement.addElement("controlpoints");
                for (int i = 0; i < controlPointsCount; i++) {
                    Element pointElement = pointsElement.addElement("controlpoint");
                    pointElement.addAttribute("ratio", Short.toString(ratios[i]));
                    writeRGBA(pointElement, "color", colors[i]);
                }
                filterElement.addAttribute("x", StringUtilities.doubleToString(gradientBevelFilter.getX()));
                filterElement.addAttribute("y", StringUtilities.doubleToString(gradientBevelFilter.getY()));
                filterElement.addAttribute("angle", StringUtilities.doubleToString(gradientBevelFilter.getAngle()));
                filterElement.addAttribute("distance",
                        StringUtilities.doubleToString(gradientBevelFilter.getDistance()));
                filterElement.addAttribute("strength",
                        StringUtilities.doubleToString(gradientBevelFilter.getStrength()));
                filterElement.addAttribute("quality", Integer.toString(gradientBevelFilter.getQuality()));
                if (gradientBevelFilter.isInner()) {
                    filterElement.addAttribute("inner", "true");
                }
                if (gradientBevelFilter.isKnockout()) {
                    filterElement.addAttribute("knockout", "true");
                }
                if (gradientBevelFilter.isOnTop()) {
                    filterElement.addAttribute("ontop", "true");
                }
            }
        }
    }

    static void writeMatrix(Element parentElement, Matrix matrix) {
        writeMatrix(parentElement, "matrix", matrix);
    }

    static void writeMatrix(Element parentElement, String elementName, Matrix matrix) {
        Element element = parentElement.addElement(elementName);
        Element translate = element.addElement("translate");
        translate.addAttribute("x", Integer.toString(matrix.getTranslateX()));
        translate.addAttribute("y", Integer.toString(matrix.getTranslateY()));
        if (matrix.hasRotateSkew()) {
            Element rotateSkew = element.addElement("rotateskew");
            rotateSkew.addAttribute("rs0", StringUtilities.doubleToString(matrix.getRotateSkew0()));
            rotateSkew.addAttribute("rs1", StringUtilities.doubleToString(matrix.getRotateSkew1()));
        }
        if (matrix.hasScale()) {
            Element scale = element.addElement("scale");
            scale.addAttribute("x", StringUtilities.doubleToString(matrix.getScaleX()));
            scale.addAttribute("y", StringUtilities.doubleToString(matrix.getScaleY()));
        }
    }

    static void writeMorphFillStyles(Element parentElement, MorphFillStyles fillStyles) {
        int size = fillStyles.getSize();
        Element fillStylesElement = parentElement.addElement("morphfillstyles");
        for (int i = 1; i <= size; i++) {
            MorphFillStyle fillStyle = fillStyles.getStyle(i);
            writeMorphFillStyle(fillStylesElement, fillStyle);
        }
    }

    static void writeMorphLineStyles(Element parentElement, MorphLineStyles lineStyles) {
        int size = lineStyles.getSize();
        Element lineStylesElement = parentElement.addElement("morphlinestyles");
        for (int i = 1; i <= size; i++) {
            Object style = lineStyles.getStyle(i);
            if (style instanceof MorphLineStyle) {
                MorphLineStyle lineStyle = (MorphLineStyle) style;
                Element lineStyleElement = lineStylesElement.addElement("morphlinestyle");
                Element startElement = lineStyleElement.addElement("start");
                startElement.addAttribute("width", Integer.toString(lineStyle.getStartWidth()));
                writeRGBA(startElement, "color", lineStyle.getStartColor());
                Element endElement = lineStyleElement.addElement("end");
                endElement.addAttribute("width", Integer.toString(lineStyle.getEndWidth()));
                writeRGBA(endElement, "color", lineStyle.getEndColor());
            } else {
                MorphLineStyle2 lineStyle2 = (MorphLineStyle2) style;
                Element lineStyle2Element = lineStylesElement.addElement("morphlinestyle2");
                Element startElement = lineStyle2Element.addElement("start");
                Element end = lineStyle2Element.addElement("end");
                startElement.addAttribute("width", Integer.toString(lineStyle2.getStartWidth()));
                end.addAttribute("width", Integer.toString(lineStyle2.getEndWidth()));
                writeEnhancedStrokeStyles(lineStyle2Element, lineStyle2.getJointStyle(),
                        lineStyle2.getStartCapStyle(), lineStyle2.getEndCapStyle(), lineStyle2.getMiterLimit(),
                        lineStyle2.getScaleStroke());
                if (lineStyle2.isClose()) {
                    parentElement.addAttribute("close", "true");
                }
                if (lineStyle2.isPixelHinting()) {
                    parentElement.addAttribute("pixelhinting", "true");
                }
                MorphFillStyle fillStyle = lineStyle2.getFillStyle();
                if (fillStyle == null) {
                    writeColor(startElement, "color", lineStyle2.getStartColor());
                    writeColor(end, "color", lineStyle2.getEndColor());
                } else {
                    writeMorphFillStyle(lineStyle2Element, fillStyle);
                }
            }
        }
    }

    static void writeRGB(Element parentElement, String elementName, RGB rgbColor) {
        Element element = parentElement.addElement(elementName);
        element.addAttribute("r", Integer.toString(rgbColor.getRed()));
        element.addAttribute("g", Integer.toString(rgbColor.getGreen()));
        element.addAttribute("b", Integer.toString(rgbColor.getBlue()));
    }

    static void writeRGBA(Element parentElement, String elementName, RGBA rgbaColor) {
        Element element = parentElement.addElement(elementName);
        element.addAttribute("r", Integer.toString(rgbaColor.getRed()));
        element.addAttribute("g", Integer.toString(rgbaColor.getGreen()));
        element.addAttribute("b", Integer.toString(rgbaColor.getBlue()));
        element.addAttribute("a", Integer.toString(rgbaColor.getAlpha()));
    }

    static void writeRect(Element parentElement, String elementName, Rect rect) {
        Element element = parentElement.addElement(elementName);
        element.addAttribute("xmin", Long.toString(rect.getXMin()));
        element.addAttribute("xmax", Long.toString(rect.getXMax()));
        element.addAttribute("ymin", Long.toString(rect.getYMin()));
        element.addAttribute("ymax", Long.toString(rect.getYMax()));
    }

    static void writeShape(Element parentElement, Shape shape) {
        Element element = parentElement.addElement("shape");
        ShapeRecord[] records = shape.getShapeRecords();
        for (int i = 0; i < records.length; i++) {
            writeShapeRecord(element, records[i]);
        }
    }

    static void writeShapeRecord(Element parentElement, ShapeRecord shapeRecord) {
        if (shapeRecord instanceof CurvedEdgeRecord) {
            writeCurvedEdgeRecord(parentElement, (CurvedEdgeRecord) shapeRecord);
        } else if (shapeRecord instanceof StraightEdgeRecord) {
            writeStraightEdgeRecord(parentElement, (StraightEdgeRecord) shapeRecord);
        } else {
            writeStyleChangeRecord(parentElement, (StyleChangeRecord) shapeRecord);
        }
    }

    static void writeShapeWithStyle(Element parentElement, ShapeWithStyle shapeWithStyle) {
        Element element = parentElement.addElement("shapewithstyle");
        Element shapeElement = element.addElement("shape");
        ShapeRecord[] records = shapeWithStyle.getShapeRecords();
        for (int i = 0; i < records.length; i++) {
            writeShapeRecord(shapeElement, records[i]);
        }
        writeLineStyles(element, shapeWithStyle.getLineStyles());
        writeFillStyles(element, shapeWithStyle.getFillStyles());
    }

    static void writeSoundInfo(Element parentElement, SoundInfo soundInfo) {
        Element element = parentElement.addElement("soundinfo");
        if (soundInfo.isSyncStop()) {
            element.addAttribute("syncstop", "true");
        }
        if (soundInfo.isSyncNoMultiple()) {
            element.addAttribute("syncnomultiple", "true");
        }
        if (soundInfo.hasLoops()) {
            element.addAttribute("loopcount", Integer.toString(soundInfo.getLoopCount()));
        }
        if (soundInfo.hasInPoint()) {
            element.addAttribute("inpoint", Long.toString(soundInfo.getInPoint()));
        }
        if (soundInfo.hasOutPoint()) {
            element.addAttribute("outpoint", Long.toString(soundInfo.getOutPoint()));
        }
        if (soundInfo.hasEnvelope()) {
            Element recordsElement = element.addElement("enveloperecords");
            SoundEnvelope[] records = soundInfo.getEnvelopeRecords();
            for (int i = 0; i < records.length; i++) {
                RecordXMLWriter.writeSoundEnvelope(recordsElement, records[i]);
            }
        }
    }

    static void writeTextRecord(Element parentElement, TextRecord record) {
        Element element = parentElement.addElement("textrecord");
        if (record.hasFont()) {
            element.addAttribute("fontid", Integer.toString(record.getFontId()));
            element.addAttribute("height", Integer.toString(record.getTextHeight()));
        }
        if (record.hasXOffset()) {
            element.addAttribute("xoffset", Short.toString(record.getXOffset()));
        }
        if (record.hasYOffset()) {
            element.addAttribute("yoffset", Short.toString(record.getYOffset()));
        }
        if (record.hasColor()) {
            writeColor(element, "color", record.getTextColor());
        }
        GlyphEntry[] glyphEntries = record.getGlyphEntries();
        Element glyphEntriesElement = element.addElement("glyphentries");
        for (int i = 0; i < glyphEntries.length; i++) {
            GlyphEntry glyphEntry = glyphEntries[i];
            Element glyphEntryElement = glyphEntriesElement.addElement("glyphentry");
            glyphEntryElement.addAttribute("index", Integer.toString(glyphEntry.getGlyphIndex()));
            glyphEntryElement.addAttribute("advance", Integer.toString(glyphEntry.getGlyphAdvance()));
        }
    }

    static void writeZlibBitmapData(Element parentElement, ZlibBitmapData data) {
        if (data instanceof BitmapData) {
            writeBitmapData(parentElement, (BitmapData) data);
        } else if (data instanceof ColorMapData) {
            writeColorMapData(parentElement, (ColorMapData) data);
        } else if (data instanceof AlphaBitmapData) {
            writeAlphaBitmapData(parentElement, (AlphaBitmapData) data);
        } else {
            writeAlphaColorMapData(parentElement, (AlphaColorMapData) data);
        }
    }

    private static void writeAlphaBitmapData(Element parentElement, AlphaBitmapData data) {
        Element element = parentElement.addElement("alphabitmapdata");
        RGBA[] pixelData = data.getBitmapPixelData();
        writeRGBAArray(element, pixelData);
    }

    private static void writeAlphaColorMapData(Element parentElement, AlphaColorMapData data) {
        Element element = parentElement.addElement("alphacolormapdata");
        Element colorTableElement = element.addElement("colortable");
        RGBA[] colorTable = data.getColorTableRGBA();
        writeRGBAArray(colorTableElement, colorTable);
        Element pixelDataElement = element.addElement("pixeldata");
        short[] pixelData = data.getColorMapPixelData();
        pixelDataElement.addText(Base64.encodeUnsigned(pixelData));
    }

    private static void writeBitmapData(Element parentElement, BitmapData data) {
        Element element = parentElement.addElement("bitmapdata");
        BitmapPixelData[] pixelData = data.getBitmapPixelData();
        if (pixelData[0] instanceof Pix15) { // array is homogeneous
            writePix15Array(element, (Pix15[]) pixelData);
        } else {
            writePix24Array(element, (Pix24[]) pixelData);
        }
    }

    private static void writeClipEventFlags(Element parentElement, ClipEventFlags flags) {
        Element element = parentElement.addElement("clipeventflags");
        if (flags.isKeyUp()) {
            element.addAttribute("keyup", "true");
        }
        if (flags.isKeyDown()) {
            element.addAttribute("keydown", "true");
        }
        if (flags.isKeyPress()) {
            element.addAttribute("keypress", "true");
        }
        if (flags.isMouseUp()) {
            element.addAttribute("mouseup", "true");
        }
        if (flags.isMouseDown()) {
            element.addAttribute("mousedown", "true");
        }
        if (flags.isMouseMove()) {
            element.addAttribute("mousemove", "true");
        }
        if (flags.isLoad()) {
            element.addAttribute("load", "true");
        }
        if (flags.isUnload()) {
            element.addAttribute("unload", "true");
        }
        if (flags.isEnterFrame()) {
            element.addAttribute("enterframe", "true");
        }
        if (flags.isDragOver()) {
            element.addAttribute("dragover", "true");
        }
        if (flags.isDragOut()) {
            element.addAttribute("dragout", "true");
        }
        if (flags.isRollOver()) {
            element.addAttribute("rollover", "true");
        }
        if (flags.isRollOut()) {
            element.addAttribute("rollout", "true");
        }
        if (flags.isReleaseOutside()) {
            element.addAttribute("releaseoutside", "true");
        }
        if (flags.isRelease()) {
            element.addAttribute("release", "true");
        }
        if (flags.isPress()) {
            element.addAttribute("press", "true");
        }
        if (flags.isInitialize()) {
            element.addAttribute("initialize", "true");
        }
        if (flags.isData()) {
            element.addAttribute("data", "true");
        }
        if (flags.isConstruct()) {
            element.addAttribute("construct", "true");
        }
    }

    private static void writeColorMapData(Element parentElement, ColorMapData data) {
        Element element = parentElement.addElement("colormapdata");
        Element colorTableElement = element.addElement("colortable");
        RGB[] colorTable = data.getColorTableRGB();
        writeRGBArray(colorTableElement, colorTable);
        Element pixelDataElement = element.addElement("pixeldata");
        short[] pixelData = data.getColorMapPixelData();
        pixelDataElement.addText(Base64.encodeUnsigned(pixelData));
    }

    private static void writeCurvedEdgeRecord(Element parentElement, CurvedEdgeRecord curvedEdgeRecord) {
        Element element = parentElement.addElement("curvededgerecord");
        element.addAttribute("anchordx", Integer.toString(curvedEdgeRecord.getAnchorDeltaX()));
        element.addAttribute("anchordy", Integer.toString(curvedEdgeRecord.getAnchorDeltaY()));
        element.addAttribute("controldx", Integer.toString(curvedEdgeRecord.getControlDeltaX()));
        element.addAttribute("controldy", Integer.toString(curvedEdgeRecord.getControlDeltaY()));
    }

    private static void writeEnhancedStrokeStyles(Element parentElement, byte jointStyle, byte startCapStyle,
            byte endCapStyle, double miterLimit, byte scaleStroke) {
        switch (jointStyle) {
        case EnhancedStrokeStyle.JOINT_ROUND:
            parentElement.addAttribute("joint", "round");
            break;
        case EnhancedStrokeStyle.JOINT_MITER:
            parentElement.addAttribute("joint", "miter");
            parentElement.addAttribute("miterlimit", StringUtilities.doubleToString(miterLimit));
            break;
        case EnhancedStrokeStyle.JOINT_BEVEL:
            parentElement.addAttribute("joint", "bevel");
            break;
        }
        Element capStyleElement = parentElement.addElement("capstyle");
        switch (startCapStyle) {
        case EnhancedStrokeStyle.CAPS_NONE:
            capStyleElement.addAttribute("start", "none");
            break;
        case EnhancedStrokeStyle.CAPS_ROUND:
            capStyleElement.addAttribute("start", "round");
            break;
        case EnhancedStrokeStyle.CAPS_SQUARE:
            capStyleElement.addAttribute("start", "square");
            break;
        }
        switch (endCapStyle) {
        case EnhancedStrokeStyle.CAPS_NONE:
            capStyleElement.addAttribute("end", "none");
            break;
        case EnhancedStrokeStyle.CAPS_ROUND:
            capStyleElement.addAttribute("end", "round");
            break;
        case EnhancedStrokeStyle.CAPS_SQUARE:
            capStyleElement.addAttribute("end", "square");
            break;
        }
        switch (scaleStroke) {
        case EnhancedStrokeStyle.SCALE_NONE:
            parentElement.addAttribute("scalestroke", "none");
            break;
        case EnhancedStrokeStyle.SCALE_HORIZONTAL:
            parentElement.addAttribute("scalestroke", "horizontal");
            break;
        case EnhancedStrokeStyle.SCALE_VERTICAL:
            parentElement.addAttribute("scalestroke", "vertical");
            break;
        case EnhancedStrokeStyle.SCALE_BOTH:
            parentElement.addAttribute("scalestroke", "both");
            break;
        }
    }

    private static void writeFillStyle(Element parentElement, FillStyle fillStyle) {
        Element fillStyleElement = parentElement.addElement("fillstyle");
        boolean isGradient = false;
        boolean isFocal = false;
        boolean isBitmap = false;
        switch (fillStyle.getType()) {
        case FillStyle.TYPE_SOLID:
            fillStyleElement.addAttribute("type", "solid");
            writeColor(fillStyleElement, "color", fillStyle.getColor());
            break;
        case FillStyle.TYPE_LINEAR_GRADIENT:
            fillStyleElement.addAttribute("type", "lineargradient");
            isGradient = true;
            break;
        case FillStyle.TYPE_FOCAL_RADIAL_GRADIENT:
            fillStyleElement.addAttribute("type", "focalradialgradient");
            isGradient = true;
            isFocal = true;
            break;
        case FillStyle.TYPE_RADIAL_GRADIENT:
            fillStyleElement.addAttribute("type", "radialgradient");
            isGradient = true;
            break;
        case FillStyle.TYPE_TILED_BITMAP:
            fillStyleElement.addAttribute("type", "tiledbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_CLIPPED_BITMAP:
            fillStyleElement.addAttribute("type", "clippedbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_NONSMOOTHED_TILED_BITMAP:
            fillStyleElement.addAttribute("type", "nonsmoothedtiledbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_NONSMOOTHED_CLIPPED_BITMAP:
            fillStyleElement.addAttribute("type", "nonsmoothedclippedbitmap");
            isBitmap = true;
            break;
        default:
            throw new IllegalArgumentException("Illegal fill type: " + fillStyle.getType());
        }
        if (isGradient) {
            if (isFocal) {
                writeFocalGradient(fillStyleElement.addElement("focalgradient"),
                        (FocalGradient) fillStyle.getGradient());
            } else {
                writeGradient(fillStyleElement.addElement("gradient"), fillStyle.getGradient());
            }
            writeMatrix(fillStyleElement, "gradientmatrix", fillStyle.getGradientMatrix());
        } else if (isBitmap) {
            fillStyleElement.addAttribute("bitmapid", Integer.toString(fillStyle.getBitmapId()));
            writeMatrix(fillStyleElement, "bitmapmatrix", fillStyle.getBitmapMatrix());
        }
    }

    private static void writeFillStyles(Element parentElement, FillStyleArray fillStyles) {
        int size = fillStyles.getSize();
        Element fillStylesElement = parentElement.addElement("fillstyles");
        for (int i = 1; i <= size; i++) {
            FillStyle fillStyle = fillStyles.getStyle(i);
            writeFillStyle(fillStylesElement, fillStyle);
        }
    }

    private static void writeFocalGradient(Element gradientElement, FocalGradient gradient) {
        writeGradient(gradientElement, gradient);
        gradientElement.addAttribute("focalpointratio",
                StringUtilities.doubleToString(gradient.getFocalPointRatio()));
    }

    private static void writeFocalMorphGradient(Element gradientElement, FocalMorphGradient gradient) {
        writeMorphGradient(gradientElement, gradient);
        gradientElement.addAttribute("startfocalpointratio",
                StringUtilities.doubleToString(gradient.getStartFocalPointRatio()));
        gradientElement.addAttribute("endfocalpointratio",
                StringUtilities.doubleToString(gradient.getEndFocalPointRatio()));
    }

    private static void writeGradient(Element gradientElement, Gradient gradient) {
        switch (gradient.getInterpolationMethod()) {
        case Gradient.INTERPOLATION_LINEAR_RGB:
            gradientElement.addAttribute("interpolation", "linear-rgb");
            break;
        case Gradient.INTERPOLATION_RGB:
            gradientElement.addAttribute("interpolation", "rgb");
            break;
        }
        switch (gradient.getSpreadMethod()) {
        case Gradient.SPREAD_PAD:
            gradientElement.addAttribute("spread", "pad");
            break;
        case Gradient.SPREAD_REFLECT:
            gradientElement.addAttribute("spread", "reflect");
            break;
        case Gradient.SPREAD_REPEAT:
            gradientElement.addAttribute("spread", "repeat");
            break;
        }
        GradRecord[] records = gradient.getGradientRecords();
        for (int i = 0; i < records.length; i++) {
            GradRecord record = records[i];
            Element recordElement = gradientElement.addElement("gradrecord");
            recordElement.addAttribute("ratio", Short.toString(record.getRatio()));
            writeColor(recordElement, "color", record.getColor());
        }
    }

    private static void writeLineStyles(Element parentElement, LineStyleArray lineStyles) {
        int size = lineStyles.getSize();
        Element lineStylesElement = parentElement.addElement("linestyles");
        for (int i = 1; i <= size; i++) {
            Object style = lineStyles.getStyle(i);
            if (style instanceof LineStyle) {
                LineStyle lineStyle = (LineStyle) style;
                Element lineStyleElement = lineStylesElement.addElement("linestyle");
                lineStyleElement.addAttribute("width", Integer.toString(lineStyle.getWidth()));
                writeColor(lineStyleElement, "color", lineStyle.getColor());
            } else {
                LineStyle2 lineStyle2 = (LineStyle2) style;
                Element lineStyle2Element = lineStylesElement.addElement("linestyle2");
                lineStyle2Element.addAttribute("width", Integer.toString(lineStyle2.getWidth()));
                writeEnhancedStrokeStyles(lineStyle2Element, lineStyle2.getJointStyle(),
                        lineStyle2.getStartCapStyle(), lineStyle2.getEndCapStyle(), lineStyle2.getMiterLimit(),
                        lineStyle2.getScaleStroke());
                if (lineStyle2.isClose()) {
                    parentElement.addAttribute("close", "true");
                }
                if (lineStyle2.isPixelHinting()) {
                    parentElement.addAttribute("pixelhinting", "true");
                }
                FillStyle fillStyle = lineStyle2.getFillStyle();
                if (fillStyle == null) {
                    writeColor(lineStyle2Element, "color", lineStyle2.getColor());
                } else {
                    writeFillStyle(lineStyle2Element, fillStyle);
                }
            }
        }
    }

    private static void writeMorphFillStyle(Element parentElement, MorphFillStyle fillStyle) {
        Element fillStyleElement = parentElement.addElement("morphfillstyle");
        Element start = fillStyleElement.addElement("start");
        Element end = fillStyleElement.addElement("end");
        boolean isGradient = false;
        boolean isFocal = false;
        boolean isBitmap = false;
        switch (fillStyle.getType()) {
        case FillStyle.TYPE_SOLID:
            fillStyleElement.addAttribute("type", "solid");
            writeRGBA(start, "color", fillStyle.getStartColor());
            writeRGBA(end, "color", fillStyle.getEndColor());
            break;
        case FillStyle.TYPE_LINEAR_GRADIENT:
            fillStyleElement.addAttribute("type", "lineargradient");
            isGradient = true;
            break;
        case FillStyle.TYPE_FOCAL_RADIAL_GRADIENT:
            fillStyleElement.addAttribute("type", "focalradialgradient");
            isGradient = true;
            isFocal = true;
            break;
        case FillStyle.TYPE_RADIAL_GRADIENT:
            fillStyleElement.addAttribute("type", "radialgradient");
            isGradient = true;
            break;
        case FillStyle.TYPE_TILED_BITMAP:
            fillStyleElement.addAttribute("type", "tiledbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_CLIPPED_BITMAP:
            fillStyleElement.addAttribute("type", "clippedbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_NONSMOOTHED_TILED_BITMAP:
            fillStyleElement.addAttribute("type", "nonsmoothedtiledbitmap");
            isBitmap = true;
            break;
        case FillStyle.TYPE_NONSMOOTHED_CLIPPED_BITMAP:
            fillStyleElement.addAttribute("type", "nonsmoothedclippedbitmap");
            isBitmap = true;
            break;
        default:
            throw new IllegalArgumentException("Illegal morph fill type: " + fillStyle.getType());
        }
        if (isGradient) {
            if (isFocal) {
                writeFocalMorphGradient(fillStyleElement.addElement("focalmorphgradient"),
                        (FocalMorphGradient) fillStyle.getGradient());
            } else {
                writeMorphGradient(fillStyleElement.addElement("morphgradient"), fillStyle.getGradient());
            }
            writeMatrix(start, "gradientmatrix", fillStyle.getStartGradientMatrix());
            writeMatrix(end, "gradientmatrix", fillStyle.getEndGradientMatrix());
        } else if (isBitmap) {
            fillStyleElement.addAttribute("bitmapid", Integer.toString(fillStyle.getBitmapId()));
            writeMatrix(start, "bitmapmatrix", fillStyle.getStartBitmapMatrix());
            writeMatrix(end, "bitmapmatrix", fillStyle.getEndBitmapMatrix());
        }
    }

    private static void writeMorphGradient(Element gradientElement, MorphGradient gradient) {
        switch (gradient.getInterpolationMethod()) {
        case Gradient.INTERPOLATION_LINEAR_RGB:
            gradientElement.addAttribute("interpolation", "linear-rgb");
            break;
        case Gradient.INTERPOLATION_RGB:
            gradientElement.addAttribute("interpolation", "rgb");
            break;
        }
        switch (gradient.getSpreadMethod()) {
        case Gradient.SPREAD_PAD:
            gradientElement.addAttribute("spread", "pad");
            break;
        case Gradient.SPREAD_REFLECT:
            gradientElement.addAttribute("spread", "reflect");
            break;
        case Gradient.SPREAD_REPEAT:
            gradientElement.addAttribute("spread", "repeat");
            break;
        }
        MorphGradRecord[] records = gradient.getGradientRecords();
        for (int i = 0; i < records.length; i++) {
            MorphGradRecord record = records[i];
            Element recordElement = gradientElement.addElement("morphgradrecord");
            Element start = recordElement.addElement("start");
            start.addAttribute("ratio", Short.toString(record.getStartRatio()));
            writeRGBA(start, "color", record.getStartColor());
            Element end = recordElement.addElement("end");
            end.addAttribute("ratio", Short.toString(record.getEndRatio()));
            writeRGBA(end, "color", record.getEndColor());
        }
    }

    private static void writePix15Array(Element parentElement, Pix15[] data) {
        Element element = parentElement.addElement("pix15array");
        byte[] buffer = new byte[data.length * 3];
        for (int i = 0; i < data.length; i++) {
            Pix15 pix15 = data[i];
            buffer[i * 3] = pix15.getRed();
            buffer[(i * 3) + 1] = pix15.getGreen();
            buffer[(i * 3) + 2] = pix15.getBlue();
        }
        element.addText(Base64.encode(buffer));
    }

    private static void writePix24Array(Element parentElement, Pix24[] data) {
        Element element = parentElement.addElement("pix24array");
        short[] buffer = new short[data.length * 3];
        for (int i = 0; i < data.length; i++) {
            Pix24 pix24 = data[i];
            buffer[i * 3] = pix24.getRed();
            buffer[(i * 3) + 1] = pix24.getGreen();
            buffer[(i * 3) + 2] = pix24.getBlue();
        }
        element.addText(Base64.encodeUnsigned(buffer));
    }

    private static void writeRGBAArray(Element parentElement, RGBA[] data) {
        Element element = parentElement.addElement("rgbaarray");
        short[] buffer = new short[data.length * 4];
        for (int i = 0; i < data.length; i++) {
            RGBA rgba = data[i];
            buffer[i * 4] = rgba.getRed();
            buffer[(i * 4) + 1] = rgba.getGreen();
            buffer[(i * 4) + 2] = rgba.getBlue();
            buffer[(i * 4) + 3] = rgba.getAlpha();
        }
        element.addText(Base64.encodeUnsigned(buffer));
    }

    private static void writeRGBArray(Element parentElement, RGB[] data) {
        Element element = parentElement.addElement("rgbarray");
        short[] buffer = new short[data.length * 3];
        for (int i = 0; i < data.length; i++) {
            RGB rgb = data[i];
            buffer[i * 3] = rgb.getRed();
            buffer[(i * 3) + 1] = rgb.getGreen();
            buffer[(i * 3) + 2] = rgb.getBlue();
        }
        element.addText(Base64.encodeUnsigned(buffer));
    }

    private static void writeSoundEnvelope(Element parentElement, SoundEnvelope soundEnvelope) {
        Element element = parentElement.addElement("soundenvelope");
        element.addAttribute("pos44", Long.toString(soundEnvelope.getPos44()));
        element.addAttribute("leftlevel", Integer.toString(soundEnvelope.getLeftLevel()));
        element.addAttribute("rightlevel", Integer.toString(soundEnvelope.getRightLevel()));
    }

    private static void writeStraightEdgeRecord(Element parentElement, StraightEdgeRecord straightEdgeRecord) {
        Element element = parentElement.addElement("straightedgerecord");
        element.addAttribute("dx", Integer.toString(straightEdgeRecord.getDeltaX()));
        element.addAttribute("dy", Integer.toString(straightEdgeRecord.getDeltaY()));
    }

    private static void writeStyleChangeRecord(Element parentElement, StyleChangeRecord styleChangeRecord) {
        Element element = parentElement.addElement("stylechangerecord");
        if (styleChangeRecord.hasMoveTo()) {
            Element moveTo = element.addElement("moveto");
            moveTo.addAttribute("x", Integer.toString(styleChangeRecord.getMoveToX()));
            moveTo.addAttribute("y", Integer.toString(styleChangeRecord.getMoveToY()));
        }
        if (styleChangeRecord.hasLineStyle() || styleChangeRecord.hasFillStyle0()
                || styleChangeRecord.hasFillStyle1() || styleChangeRecord.hasNewStyles()) {
            Element styles = element.addElement("styles");
            if (styleChangeRecord.hasLineStyle()) {
                styles.addAttribute("line", Integer.toString(styleChangeRecord.getLineStyle()));
            }
            if (styleChangeRecord.hasFillStyle0()) {
                styles.addAttribute("fill0", Integer.toString(styleChangeRecord.getFillStyle0()));
            }
            if (styleChangeRecord.hasFillStyle1()) {
                styles.addAttribute("fill1", Integer.toString(styleChangeRecord.getFillStyle1()));
            }
            if (styleChangeRecord.hasNewStyles()) {
                writeLineStyles(styles, styleChangeRecord.getNewLineStyles());
                writeFillStyles(styles, styleChangeRecord.getNewFillStyles());
            }
        }
    }
}