/* * * 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; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.swf.tags.DoABCTag; import org.apache.flex.swf.tags.ExportAssetsTag; import org.apache.flex.swf.tags.FrameLabelTag; import org.apache.flex.swf.tags.ICharacterTag; import org.apache.flex.swf.tags.IManagedTag; import org.apache.flex.swf.tags.ITag; import org.apache.flex.swf.tags.ShowFrameTag; import org.apache.flex.swf.tags.SymbolClassTag; /** * SWF frame model manages a list of SWF tags. For the "managed" tags, * {@code SWFFrame} provides API to access their data. Other tags are grouped by * types and are stored in lists. * <p> * {@code SWFFrame} is a {@link ITagContainer}. The enclosed tags are not stored * in a one-dimensional array, however, they can be iterated in a deterministic * order. */ public class SWFFrame implements ITagContainer { /** * Constructor. */ public SWFFrame() { unfiledTags = new ArrayList<ITag>(); doABCTags = new ArrayList<DoABCTag>(); characterTags = new LinkedHashSet<ICharacterTag>(); } // TODO: remove this generic tag container private final List<ITag> unfiledTags; private final List<DoABCTag> doABCTags; private final LinkedHashSet<ICharacterTag> characterTags; protected SymbolClassTag symbolClass; protected ExportAssetsTag exportAssetsTag; protected FrameLabelTag frameLabel; /** * Add a SWF tag to the frame. * * @param tag tag */ public void addTag(ITag tag) { if (tag == null) throw new NullPointerException("can't not add null to SWF frame"); if (tag.getTagType() == TagType.Undefined) return; // ignore undefined tags if (tag instanceof IManagedTag) throw new IllegalArgumentException("Can't add a managed tag to frame."); if (tag.getTagType() == TagType.DoABC) { doABCTags.add((DoABCTag)tag); } else if (tag instanceof ICharacterTag) { characterTags.add((ICharacterTag)tag); } else { unfiledTags.add(tag); } } /** * Export an {@code ICharacterTag} with {@code name}. The character tag has * to be added to the frame before it is defined as a symbol. * * @param tag character tag * @param name export name */ public void defineSymbol(ICharacterTag tag, String name) { defineSymbol(tag, name, null); } /** * Export an {@code ICharacterTag} with {@code name}. The character tag has * to be added to the frame before it is defined as a symbol. * * @param tag character tag * @param name export name * @param dictionary Used when reading an existing SWF. If null, only the * character tags in the current frame will be checked. */ public void defineSymbol(ICharacterTag tag, String name, Map<Integer, ICharacterTag> dictionary) { assert tag != null; assert name != null; if (!(dictionary != null && dictionary.get(tag.getCharacterID()) != null) && !characterTags.contains(tag)) { throw new IllegalStateException("Tag not added to the frame yet: " + tag); } getSymbolClass().addSymbol(tag, name); } /** * Get the SymbolClass tag of the current frame. This method helps * lazy-initialize the tag. * * @return SymbolClass tag object. */ private SymbolClassTag getSymbolClass() { if (symbolClass == null) symbolClass = new SymbolClassTag(); return symbolClass; } /** * Get the symbol name of a class. The symbol class needs to be read * before this can be queried. * * @param tag A character tag. * @return The name of the tag in the dictionary. Null if the symbol * table has not been read or the tag is not defined in this frame. */ public String getSymbolName(ICharacterTag tag) { if (symbolClass == null) return null; return symbolClass.getSymbolName(tag); } /** * Export an {@code ICharacterTag} with {@code name}. The character tag has * to be added to the frame before it is defined as an export asset. * * @param tag character tag * @param name export name */ public void defineExport(ICharacterTag tag, String name) { assert tag != null; assert name != null; if (!characterTags.contains(tag)) throw new IllegalStateException("Tag not added to the frame yet: " + tag); if (exportAssetsTag == null) exportAssetsTag = new ExportAssetsTag(); exportAssetsTag.addExport(tag, name); } /** * Get an iterator of all the tags in the frame. This method combines all * the managed and not managed tags into one ordered collection. */ @Override public Iterator<ITag> iterator() { final List<ITag> orderedTags = new ArrayList<ITag>(); // frame label if (frameLabel != null) orderedTags.add(frameLabel); // TODO: imports // ABC tags orderedTags.addAll(doABCTags); // character tags orderedTags.addAll(characterTags); // symbol class if (symbolClass != null) orderedTags.add(symbolClass); // export assets if (exportAssetsTag != null) orderedTags.add(exportAssetsTag); // TODO: control tags // TODO: frame actions orderedTags.addAll(unfiledTags); // show frame orderedTags.add(new ShowFrameTag()); return orderedTags.iterator(); } /** * Assign a name to this frame. * * @param name frame name * @param isAnchor true if the frame name is a named anchor tag */ public void setName(String name, boolean isAnchor) { assert name != null; assert !"".equals(name); if (frameLabel == null) frameLabel = new FrameLabelTag(name); frameLabel.setNamedAnchorTag(isAnchor); } /** * Get frame name. * * @return frame name */ public String getName() { return frameLabel == null ? null : frameLabel.getName(); } /** * Check if this frame has a named anchor tag. * <p> * In SWF files of version 6 or later, an extension to the FrameLabel tag * called named anchors is available. A named anchor is a special kind of * frame label that, in addition to labeling a frame for seeking using * {@code ActionGoToLabel}, labels the frame for seeking using HTML anchor * syntax. * * @return true if this frame uses a named anchor tag. */ public boolean hasNamedAnchor() { return frameLabel == null ? false : frameLabel.isNamedAnchorTag(); } /** * Force creating a SymbolClass tag on the frame. * * @param frame SWF frame */ public static final void forceSymbolClassTag(SWFFrame frame) { frame.getSymbolClass(); } @Override public Collection<ICompilerProblem> getProblems() { return Collections.emptyList(); } }