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