/* * * 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.compiler.internal.embedding.transcoders; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.ABCEmitter; import org.apache.flex.abc.instructionlist.InstructionList; import org.apache.flex.abc.semantics.Label; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Namespace; import org.apache.flex.abc.semantics.Nsset; import org.apache.flex.compiler.common.ISourceLocation; import org.apache.flex.compiler.constants.IASLanguageConstants.BuiltinType; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.references.IResolvedQualifiersReference; import org.apache.flex.compiler.definitions.references.ReferenceFactory; import org.apache.flex.compiler.internal.abc.ClassGeneratorHelper; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase; import org.apache.flex.compiler.internal.embedding.EmbedAttribute; import org.apache.flex.compiler.internal.embedding.EmbedData; import org.apache.flex.compiler.internal.embedding.EmbedData.SkinClassInfo; import org.apache.flex.compiler.internal.tree.as.FileNode; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.problems.EmbedNoSkinClassProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InternalCompilerProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.swf.tags.ICharacterTag; import org.apache.flex.swf.tags.ITag; import com.google.common.collect.ImmutableList; /** * Handle the embedding of Skin assets */ public class SkinTranscoder extends TranscoderBase { /** * @param data The embedding data. * @param workspace The workspace. */ public SkinTranscoder(EmbedData data, Workspace workspace, SkinClassInfo skinClassInfo) { super(data, workspace); iBorderReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IBorder"); iFlexDisplayObjectReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IFlexDisplayObject"); iFlexAssetReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IFlexAsset"); this.skinClass = ""; this.skinClassInfo = skinClassInfo; } private String skinClass; private final SkinClassInfo skinClassInfo; private final IResolvedQualifiersReference iBorderReference; private final IResolvedQualifiersReference iFlexDisplayObjectReference; private final IResolvedQualifiersReference iFlexAssetReference; /** * {@inheritDoc} */ @Override public boolean analyze(ISourceLocation location, Collection<ICompilerProblem> problems) { boolean result = super.analyze(location, problems); if (!result) return false; baseClassQName = skinClass; return result; } /** * {@inheritDoc} */ @Override protected boolean setAttribute(EmbedAttribute attribute) { boolean isSupported = true; switch (attribute) { case SKIN_CLASS: skinClass = (String)data.getAttribute(EmbedAttribute.SKIN_CLASS); break; default: isSupported = super.setAttribute(attribute); } return isSupported; } /** * {@inheritDoc} */ @Override protected boolean checkAttributeValues(ISourceLocation location, Collection<ICompilerProblem> problems) { if (skinClass.length() == 0) { problems.add(new EmbedNoSkinClassProblem(location)); return false; } return true; } /** * {@inheritDoc} */ @Override protected Map<String, ICharacterTag> doTranscode(Collection<ITag> tags, Collection<ICompilerProblem> problems) { // no tags related to skins, as all it's only a generated class return Collections.emptyMap(); } /** * {@inheritDoc} */ @Override public FileNode buildAST(Collection<ICompilerProblem> problems, String filename) { // TODO: remove when all other transcoders updated to generate ABC - should never be called. assert false : "buildAST() called on a SkinTranscoder"; return null; } /** * {@inheritDoc} */ @Override public byte[] buildABC(ICompilerProject project, Collection<ICompilerProblem> problems) { ABCEmitter emitter = new ABCEmitter(); final String classQName = data.getQName(); Name className = new Name(classQName); IDefinition baseClass = project.resolveQNameToDefinition(skinClass); assert (baseClass instanceof ClassDefinition) : "skinClass does not resolve to a class"; ClassDefinition baseClassDef = (ClassDefinition)baseClass; Collection<Name> implementedInterfaces = new ImmutableList.Builder<Name>() .add(iBorderReference.getMName()) .add(iFlexDisplayObjectReference.getMName()) .add(iFlexAssetReference.getMName()) .build(); //FIXME: move this to ClassGeneratorHelper Namespace privateNs = new Namespace(ABCConstants.CONSTANT_PrivateNs, className.getSingleQualifier().getName() + ":" + className.getBaseName()); // generate the constructor: // public function $className() extends $baseClassDef implements IBorder, IFlexDisplayObject, IFlexAsset // { // super(); InstructionList classITraitsInit = new InstructionList(); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); classITraitsInit.addInstruction(ABCConstants.OP_constructsuper, 0); // generate: // _measuredWidth = width; Name _measuredWidth = null; if (skinClassInfo.needsMeasuredWidth) { Name width = new Name("width"); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); classITraitsInit.addInstruction(ABCConstants.OP_getproperty, width); _measuredWidth = new Name(ABCConstants.CONSTANT_Qname, new Nsset(privateNs), "_measuredWidth"); classITraitsInit.addInstruction(ABCConstants.OP_initproperty, _measuredWidth); } // generate: // _measuredHeight = height; Name _measuredHeight = null; if (skinClassInfo.needsMeasuredHeight) { Name height = new Name("height"); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); classITraitsInit.addInstruction(ABCConstants.OP_getproperty, height); _measuredHeight = new Name(ABCConstants.CONSTANT_Qname, new Nsset(privateNs), "_measuredHeight"); classITraitsInit.addInstruction(ABCConstants.OP_initproperty, _measuredHeight); } // generate: TODO: currently we don't wrap this call in a try/catch, as it // seems unnecessary. Change if need be. // try // { // name = NameUtil.createUniqueName(this); // } // catch(e:Error) // { // } if (!skinClassInfo.flexMovieClipOrSprite) { Name name = new Name("name"); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); Name nameUtil = ReferenceFactory.packageQualifiedReference(workspace, UTILS_PACKAGE + ".NameUtil").getMName(); classITraitsInit.addInstruction(ABCConstants.OP_getlex, nameUtil); classITraitsInit.addInstruction(ABCConstants.OP_getlocal0); Object[] createUniqueName = new Object[] { new Name("createUniqueName"), 1 }; classITraitsInit.addInstruction(ABCConstants.OP_callproperty, createUniqueName); classITraitsInit.addInstruction(ABCConstants.OP_initproperty, name); } // finish the constructor classITraitsInit.addInstruction(ABCConstants.OP_returnvoid); TypeDefinitionBase numberDef = (TypeDefinitionBase)project.getBuiltinType(BuiltinType.NUMBER); Name numberName = numberDef.getMName(project); // add all the member variables to the class ClassGeneratorHelper classGen = new ClassGeneratorHelper(project, emitter, className, baseClassDef, implementedInterfaces, classITraitsInit); // generate: // public function get borderMetrics():EdgeMetrics // { // if (scale9Grid == null) // { // return EdgeMetrics.EMPTY; // } // else // { // return new EdgeMetrics(scale9Grid.left, // scale9Grid.top, // Math.ceil(measuredWidth - scale9Grid.right), // Math.ceil(measuredHeight - scale9Grid.bottom)); // } // } if (skinClassInfo.needsIBorder && skinClassInfo.needsBorderMetrics) { InstructionList body = new InstructionList(); Name scale9Grid = new Name("scale9Grid"); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, scale9Grid); Label trueLabel = new Label(); body.addInstruction(ABCConstants.OP_iftrue, trueLabel); Name edgeMetrics = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".EdgeMetrics").getMName(); body.addInstruction(ABCConstants.OP_getlex, edgeMetrics); body.addInstruction(ABCConstants.OP_getproperty, new Name("EMPTY")); body.addInstruction(ABCConstants.OP_returnvalue); body.labelNext(trueLabel); body.addInstruction(ABCConstants.OP_findpropstrict, edgeMetrics); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, scale9Grid); body.addInstruction(ABCConstants.OP_getproperty, new Name("left")); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, scale9Grid); body.addInstruction(ABCConstants.OP_getproperty, new Name("top")); Name math = new Name("Math"); body.addInstruction(ABCConstants.OP_getlex, math); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, new Name("measuredWidth")); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, scale9Grid); body.addInstruction(ABCConstants.OP_getproperty, new Name("right")); body.addInstruction(ABCConstants.OP_subtract); Object[] ceil = new Object[] {new Name("ceil"), 1}; body.addInstruction(ABCConstants.OP_callproperty, ceil); body.addInstruction(ABCConstants.OP_getlex, math); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, new Name("measuredHeight")); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, scale9Grid); body.addInstruction(ABCConstants.OP_getproperty, new Name("bottom")); body.addInstruction(ABCConstants.OP_subtract); body.addInstruction(ABCConstants.OP_callproperty, ceil); body.addInstruction(ABCConstants.OP_constructprop, new Object[] {edgeMetrics, 4}); body.addInstruction(ABCConstants.OP_returnvalue); classGen.addITraitsGetter(new Name("borderMetrics"), edgeMetrics, body); } if (skinClassInfo.needsIFlexDisplayObject) { // generate: // public function get measuredWidth():Number // { // return _measuredWidth; // } if (skinClassInfo.needsMeasuredWidth) { InstructionList body = new InstructionList(); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, _measuredWidth); body.addInstruction(ABCConstants.OP_returnvalue); classGen.addMemberVariable(_measuredWidth, numberName); classGen.addITraitsGetter(new Name("measuredWidth"), numberName, body); } // generate: // public function get measuredHeight():Number // { // return _measuredHeight; // } if (skinClassInfo.needsMeasuredHeight) { InstructionList body = new InstructionList(); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, _measuredHeight); body.addInstruction(ABCConstants.OP_returnvalue); classGen.addMemberVariable(_measuredHeight, numberName); classGen.addITraitsGetter(new Name("measuredHeight"), numberName, body); } // generate: // public function move(x:Number, y:Number):void // { // this.x = x; // this.y = y; // } if (skinClassInfo.needsMove) { InstructionList body = new InstructionList(); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getlocal1); body.addInstruction(ABCConstants.OP_setproperty, new Name("x")); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getlocal2); body.addInstruction(ABCConstants.OP_setproperty, new Name("y")); body.addInstruction(ABCConstants.OP_returnvoid); Collection<Name> paramTypes = new ImmutableList.Builder<Name>() .add(numberName) .add(numberName) .build(); classGen.addITraitsMethod(new Name("move"), paramTypes, new Name("void"), Collections.<Object>emptyList(), false, false, false, body); } // generate: // public function setActualSize(newWidth:Number, newHeight:Number):void // { // if (width != newWidth) // { // width = newWidth; // } // if (height != newHeight) // { // height = newHeight; // } // } if (skinClassInfo.needsSetActualSize) { InstructionList body = new InstructionList(); Name width = new Name("width"); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, width); body.addInstruction(ABCConstants.OP_getlocal1); Label trueLabel = new Label(); body.addInstruction(ABCConstants.OP_ifeq, trueLabel); body.addInstruction(ABCConstants.OP_findproperty, width); body.addInstruction(ABCConstants.OP_getlocal1); body.addInstruction(ABCConstants.OP_setproperty, width); body.labelNext(trueLabel); Name height = new Name("height"); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_getproperty, height); body.addInstruction(ABCConstants.OP_getlocal2); trueLabel = new Label(); body.addInstruction(ABCConstants.OP_ifeq, trueLabel); body.addInstruction(ABCConstants.OP_findproperty, height); body.addInstruction(ABCConstants.OP_getlocal2); body.addInstruction(ABCConstants.OP_setproperty, height); body.labelNext(trueLabel); body.addInstruction(ABCConstants.OP_returnvoid); Collection<Name> paramTypes = new ImmutableList.Builder<Name>() .add(numberName) .add(numberName) .build(); classGen.addITraitsMethod(new Name("setActualSize"), paramTypes, new Name("void"), Collections.<Object>emptyList(), false, false, false, body); } } // generate: // override public function toString():String // { // return NameUtil.displayObjectToString(this); // } if (!skinClassInfo.flexMovieClipOrSprite) { InstructionList body = new InstructionList(); body.addInstruction(ABCConstants.OP_getlocal0); body.addInstruction(ABCConstants.OP_pushscope); Name nameUtil = ReferenceFactory.packageQualifiedReference(workspace, UTILS_PACKAGE + ".NameUtil").getMName(); body.addInstruction(ABCConstants.OP_getlex, nameUtil); body.addInstruction(ABCConstants.OP_getlocal0); Object[] displayObjectToString = new Object[] { new Name("displayObjectToString"), 1 }; body.addInstruction(ABCConstants.OP_callproperty, displayObjectToString); body.addInstruction(ABCConstants.OP_returnvalue); TypeDefinitionBase stringDef = (TypeDefinitionBase)project.getBuiltinType(BuiltinType.STRING); Name stringName = stringDef.getMName(project); classGen.addITraitsMethod(new Name("toString"), Collections.<Name>emptyList(), stringName, Collections.<Object>emptyList(), false, false, true, body); } classGen.finishScript(); try { return emitter.emit(); } catch (Exception e) { problems.add(new InternalCompilerProblem(e)); return null; } } @Override public boolean equals(Object o) { if (!super.equals(o)) return false; if (!(o instanceof SkinTranscoder)) return false; SkinTranscoder t = (SkinTranscoder)o; if (!skinClass.equals(t.skinClass)) return false; return true; } @Override public int hashCode() { int hashCode = super.hashCode(); hashCode += skinClass.hashCode(); return hashCode; } }