/* * * 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.swf.io; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Helper class generally used by SWFWriterAndSizeReporter to collect size * related metrics for a given SWF and to generate an optional XML report of the * summarized data. */ public class SizeReport { private int size; private int currentFrame = 1; private int currentFrameSize; private int compressedSize; private Map<Integer, String> symbols; private Map<Integer, DefinitionData> definitionTypes; private String prevFrameLabel = null; // DefinitionEntry Types public static final int BINARY = 0; public static final int BITMAP = 1; public static final int FONT = 2; public static final int FRAME = 3; public static final int FRAME_DATA = 4; public static final int HEADER_DATA = 5; public static final int SCRIPT = 6; public static final int SHAPE = 7; public static final int SOUND = 8; public static final int SPRITE = 9; public static final int VIDEO = 10; public SizeReport() { symbols = new HashMap<Integer, String>(); definitionTypes = new HashMap<Integer, DefinitionData>(); registerDefinitionType(BINARY, "binaryData", "data", "SWF, Pixel Bender, or other miscellaneous embed data."); registerDefinitionType(BITMAP, "bitmaps", "bitmap", "defineBits, definebitsJPEG2/3/4, or defineBitsLossless/2"); registerDefinitionType(FONT, "fonts", "font", "defineFont/2/3/4."); registerDefinitionType(FRAME, "frames", "frame", "Cumulative frame size summary."); registerDefinitionType(FRAME_DATA, "frameData", "tag", "Additional frame tags (symbolClass, exportAssets, showFrame, etc)."); registerDefinitionType(HEADER_DATA, "headerData", "data", "Header data (SWF attributes, product info, markers, etc.)"); registerDefinitionType(SCRIPT, "actionScript", "abc", "Actionscript code and constant data."); registerDefinitionType(SHAPE, "shapes", "shape", "defineShape/2/3/4."); registerDefinitionType(SOUND, "sounds", "sound", "defineSound."); registerDefinitionType(SPRITE, "sprites", "sprite", "defineSprite."); registerDefinitionType(VIDEO, "videos", "video", "defineVideoStream."); } public String generate() { StringBuilder buffer = new StringBuilder(2048); buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); buffer.append("<report>\n"); { // Cumulative Size buffer.append(" <swf size=\""); buffer.append(size); buffer.append("\" compressedSize=\""); buffer.append(compressedSize); buffer.append("\">\n"); dumpDefinitionType(buffer, HEADER_DATA, " "); dumpDefinitionType(buffer, FRAME, " "); dumpDefinitionType(buffer, SCRIPT, " "); dumpDefinitionType(buffer, FONT, " "); dumpDefinitionType(buffer, SPRITE, " "); dumpDefinitionType(buffer, SHAPE, " "); dumpDefinitionType(buffer, BITMAP, " "); dumpDefinitionType(buffer, SOUND, " "); dumpDefinitionType(buffer, VIDEO, " "); dumpDefinitionType(buffer, BINARY, " "); dumpDefinitionType(buffer, FRAME_DATA, " "); buffer.append(" </swf>\n"); } buffer.append("</report>"); return buffer.toString(); } public void setSize(int value) { size = value; } public void setCompressedSize(int value) { compressedSize = value; } private void dumpDefinitionType(StringBuilder buffer, int type, String indent) { DefinitionData data = definitionTypes.get(type); List<DefinitionEntry> definitions = data.definitions; // Sort definitions largest to smallest. Sort frames in frame order. Comparator<DefinitionEntry> comparator = type == FRAME ? new FrameComparator() : new DefinitionComparator(); Collections.sort(definitions, comparator); if (definitions.size() > 0) { buffer.append("\n" + indent + "<!-- " + data.description + " -->\n"); buffer.append(indent + "<" + data.pluralMoniker + " "); buffer.append("totalSize=\""); buffer.append(data.totalSize); buffer.append("\">\n"); for (DefinitionEntry n : definitions) { buffer.append(indent + " <" + data.singularMoniker + " "); String name = (type == FRAME || type == SCRIPT) ? n.stringData : symbols.get(n.id); if (name != null) { buffer.append("name=\""); buffer.append(name); buffer.append("\" "); } if (type == FONT && n.stringData != null) { buffer.append("fontName=\""); buffer.append(n.stringData); buffer.append("\" "); } else if ((type == HEADER_DATA || type == FRAME_DATA) && n.stringData != null) { buffer.append("type=\""); buffer.append(n.stringData); buffer.append("\" "); } buffer.append("size=\""); buffer.append(n.size); if (n.frame != -1 && type != HEADER_DATA) { buffer.append("\" "); buffer.append("frame=\""); buffer.append(n.frame); } buffer.append("\"/>\n"); } buffer.append(indent + "</" + data.pluralMoniker + ">\n"); } } public void startEntry(int type, int startOffset, int id, String stringData) { if (type == FRAME) { prevFrameLabel = stringData; } else { DefinitionData data = definitionTypes.get(type); DefinitionEntry entry = new DefinitionEntry(); entry.stringData = stringData; entry.size = startOffset; entry.id = id; entry.frame = currentFrame; data.definitions.add(entry); } } public void endEntry(int type, int endOffset, String stringData) { if (type == FRAME) { DefinitionData data = definitionTypes.get(type); DefinitionEntry entry = new DefinitionEntry(); entry.size = currentFrameSize; entry.stringData = prevFrameLabel; entry.frame = currentFrame; data.definitions.add(entry); data.totalSize += entry.size; currentFrame++; currentFrameSize = 0; // reset the frame size } else { DefinitionData data = definitionTypes.get(type); DefinitionEntry entry = data.definitions.get(data.definitions.size() - 1); entry.size = endOffset - entry.size; data.totalSize += entry.size; currentFrameSize += entry.size; } } public void addEntry(int type, int id, int size, String stringData) { DefinitionData data = definitionTypes.get(type); DefinitionEntry entry = new DefinitionEntry(); data.totalSize += size; entry.stringData = stringData; entry.size = size; entry.id = id; entry.frame = currentFrame; data.definitions.add(entry); currentFrameSize += entry.size; } public void startEntry(int type, int startOffset, int id) { startEntry(type, startOffset, id, null); } public void endEntry(int type, int endOffset) { endEntry(type, endOffset, null); } public void addEntry(int type, int id, int size) { addEntry(type, id, size, null); } public void addSymbol(String name, int id) { symbols.put(id, name); } private void registerDefinitionType(int type, String pluralMoniker, String singularMoniker, String description) { definitionTypes.put(type, new DefinitionData(pluralMoniker, singularMoniker, description)); } // Private helper classes used to keep track of each definition by type as // well as cumulative size for each type. private class DefinitionEntry { public Integer size; public Integer id; public String stringData; public Integer frame = -1; } private class DefinitionData { public DefinitionData(String pluralMoniker, String singularMoniker, String description) { this.singularMoniker = singularMoniker; this.pluralMoniker = pluralMoniker; this.description = description; definitions = new ArrayList<DefinitionEntry>(); } public List<DefinitionEntry> definitions; public int totalSize; public String singularMoniker; public String pluralMoniker; public String description; } private class DefinitionComparator implements Comparator<DefinitionEntry> { @Override public final int compare(DefinitionEntry a, DefinitionEntry b) { // Largest to smallest. return -(a.size.compareTo(b.size)); } } private class FrameComparator implements Comparator<DefinitionEntry> { @Override public final int compare(DefinitionEntry a, DefinitionEntry b) { // frame 1 is first. return a.frame.compareTo(b.frame); } } }