Package flex2.compiler.as3

Source Code of flex2.compiler.as3.EmbedEvaluator$EmbedOnlyOnClassesAndVars

/*
*
*  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 flex2.compiler.as3;

import flex2.compiler.*;
import flex2.compiler.util.MimeMappings;
import flex2.compiler.util.QName;
import flex2.compiler.util.CompilerMessage;
import flex2.compiler.as3.reflect.MetaData;
import flex2.compiler.as3.reflect.NodeMagic;
import flex2.compiler.as3.MetaDataEvaluator.MetaDataRequiresDefinition;
import flex2.compiler.io.TextFile;
import flex2.compiler.util.MultiName;
import flex2.compiler.util.NameFormatter;
import macromedia.asc.parser.*;
import macromedia.asc.semantics.Value;
import macromedia.asc.util.Context;
import flash.swf.tools.as3.EvaluatorAdapter;
import flash.util.FileUtils;
import flash.util.Trace;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
* Evaluator that transcodes Embed resources, adds assets to the
* CompilationUnit, and turns variable level Embeds into class level
* Embeds.
*
* @author Paul Reilly
* @author Brian Deitte
*/
class EmbedEvaluator extends EvaluatorAdapter
{
    private CompilationUnit unit;
    private String generatedOutputDir;
    private boolean checkDeprecation;
    private Transcoder[] transcoders;
    private Stack<EmbedData> embedDataStack;
  private Set<ClassDefinitionNode> evaluatedClasses;
    private SymbolTable symbolTable;

    EmbedEvaluator(CompilationUnit unit, SymbolTable symbolTable, Transcoder[] transcoders,
                   String generatedOutputDir, boolean checkDeprecation)
    {
        this.unit = unit;
        this.symbolTable = symbolTable;
        this.generatedOutputDir = generatedOutputDir;
        this.transcoders = transcoders;
        this.checkDeprecation = checkDeprecation;
        embedDataStack = new Stack<EmbedData>();
    evaluatedClasses = new HashSet<ClassDefinitionNode>();
  }

    private EmbedData getEmbedData()
    {
        EmbedData embedData = null;
        if (embedDataStack.size() != 0)
        {
            embedData = embedDataStack.peek();
        }
        return embedData;
    }

    public Value evaluate(Context context, ClassDefinitionNode node)
    {
    if (!evaluatedClasses.contains(node))
    {
      evaluatedClasses.add(node);
      String packageName = NodeMagic.getPackageName(node);
      String className = node.name.name;

      EmbedData embedData = getEmbedData();
      if (embedData == null || embedData.inUse)
      {
        embedData = new EmbedData();
        embedDataStack.push(embedData);
      }
      embedData.inUse = true;
      embedData.referenceClassName = className;

      if (node.statements != null)
      {
        node.statements.evaluate(context, this);
      }

      if (embedData.hasData())
      {
        unit.addGeneratedSources( generateSources(packageName, context, node) );
        embedData.clear();
      }

      if (embedDataStack.size() > 0)
      {
        embedDataStack.pop();
      }
      embedData.inUse = false;
    }

    return null;
   }

    public Value evaluate(Context context, MetaDataNode node)
    {
        Node def = node.def;
        if ( "Embed".equals(node.getId()) )
        {
            InputBuffer input = context.input;
            String file;
            int line = -1;
            int col = -1;

            if (input != null)
            {
                file = input.origin;
                int pos = node.pos();
                line = input.getLnNum(pos);
                col = input.getColPos(pos);
            }
            else
            {
                file = context.scriptName();
            }

            if (def instanceof VariableDefinitionNode)
            {
                VariableDefinitionNode variableDefinition = (VariableDefinitionNode) def;

                if ((variableDefinition.list != null) &&
                    (variableDefinition.list.items != null) &&
                    (variableDefinition.list.items.size() > 0))
                {
                    Object item = variableDefinition.list.items.get(0);

                    if (item instanceof VariableBindingNode)
                    {
                        VariableBindingNode variableBinding = (VariableBindingNode) item;

                        if (variableBinding.initializer == null)
                        {
                            EmbedData embedData = getEmbedData();
                            if (embedData == null)
                            {
                                context.localizedError2(node.pos(), new EmbedOnlyOnClassesAndVars());
                                return null;
                            }
                            String name = variableBinding.variable.identifier.name;
                            String className = null;
                           
                            // If this is an embedded css asset then prefix the
                            // name with a generic "_class" instead of the name
                            // of the reference class. We want embedded css
                            // assets to have the same name no matter which
                            // class they are embedded into.
                            if (name != null && name.startsWith("_embed_css_"))
                                className = "_class" + name;
                            else
                                className = embedData.referenceClassName + "_" + name;
                           
                            unit.expressions.add(new MultiName(NameFormatter.toColon(className)));
                            Map<String, Object> values = getMetaDataValues(node, context);

                            // Yeah, I feel dirty doing this, but I don't feel like making a separate map.
                            if (!values.containsKey( Transcoder.FILE ))
                            {
                              if (file.indexOf('\\') != -1)
                              {
                                values.put( Transcoder.FILE, file.replace('\\', '/'));
                                values.put( Transcoder.PATHSEP, "true");
                              }
                              else
                              {
                                values.put( Transcoder.FILE, file );
                              }

                                if (input != null)
                                {
                                    values.put( Transcoder.LINE, new Integer(line).toString() );
                                    values.put( Transcoder.COLUMN, new Integer(col).toString() );
                                }
                            }

                            getEmbedData().class2params.put( className, values );

                            String type = NodeMagic.lookupType( variableBinding );
                            boolean correctType = false;
                            if (type != null)
                            {
                                if (type.equals( "Class" ))
                                {
                                    correctType = true;
                                    variableBinding.initializer = generateClassInitializer( className );
                                }
                                else if (type.equals( "String" ))
                                {
                                    correctType = true;
                                    // FIXME- need way to introduce class dep from string embeds
                                    variableBinding.initializer = generateStringInitializer( className );
                                }
                            }

                            if (!correctType)
                            {
                              context.localizedError2(node.pos(), new UnsupportedTypeForEmbed());
                            }

                        }
                        else
                        {
                          context.localizedError2(node.pos(), new InvalidEmbedVariable());
                        }
                    }
                }
            }
            else if (def instanceof ClassDefinitionNode)
            {
                ClassDefinitionNode cdn = (ClassDefinitionNode)def;
                String pkg = NodeMagic.getPackageName(cdn);
                String cls = ((pkg == null) || (pkg.length() == 0)) ? cdn.name.name : pkg + "." + cdn.name.name;
                Map<String, Object> values = getMetaDataValues(node, context);

                // Yeah, I feel dirty doing this, but I don't feel like making a separate map.
                if (!values.containsKey( Transcoder.FILE ))
                {
                  if (file.indexOf('\\') != -1)
                  {
                    values.put( Transcoder.FILE, file.replace('\\', '/'));
                    values.put( Transcoder.PATHSEP, "true");
                  }
                  else
                  {
                    values.put( Transcoder.FILE, file );
                  }

                    if (input != null)
                    {
                        values.put( Transcoder.LINE, new Integer(line).toString() );
                        values.put( Transcoder.COLUMN, new Integer(col).toString() );
                    }
                }

                Transcoder.TranscodingResults asset = EmbedUtil.transcode(transcoders, unit, symbolTable,
                                                                          cls, values, line, col, false);

                if ((asset == null) && values.containsKey(Transcoder.SOURCE))
                {
                    context.localizedError2(node.pos(), new UnableToTranscode(values.get(Transcoder.SOURCE)));
                }

                // code below to should given a warning once we figure out non-AS2 way to call stop.  Since
                // this will most likely be a solution that's generated in a class, won't be supported on
                // classes.  Should also put this warning in MxmlDocument if it isn't moved to var level embeds
                //if (asset.defineTag instanceof DefineSprite && ((DefineSprite)asset.defineTag).needsStop)
                // {}

                // TODO: compare DefineTag/associatedClass against given class
            }
            else if (def == null)
            {
                context.localizedError2(node.pos(), new MetaDataRequiresDefinition());
            }
            else
            {
              context.localizedError2(node.pos(), new EmbedOnlyOnClassesAndVars());
            }
        }

        return null;
    }

    private Map<String, Object> getMetaDataValues(MetaDataNode node, Context context)
    {
        MetaData metaData = new MetaData(node);
        int len = metaData.count();
        Map<String, Object> values = new HashMap<String, Object>();
        for (int i = 0; i < len; i++)
        {
            String key = metaData.getKey(i);
            String value = metaData.getValue(i);
            // FIXME: look for place where source is being added to generated Embeds remove the key.equals check
            if (key == null || key.equals(Transcoder.SOURCE))
            {
                int octothorpe = value.indexOf( "#" );
                if (octothorpe != -1)
                {
                    Source src = unit.getSource();
                    if (src.resolve(value) != null)
                    {
                        values.put(Transcoder.SOURCE, value);
                    }
                    else
                    {
                        values.put(Transcoder.SOURCE, value.substring( 0, octothorpe ));
                        values.put(Transcoder.SYMBOL, value.substring( octothorpe + 1));
                    }
                }
                else
                {
                    values.put(Transcoder.SOURCE, value);
                }
            }
            else
            {
                values.put(key, value);
            }
        }
       
        if (checkDeprecation && (values.containsKey("flashType") || values.containsKey("flash-type")))
        {
          String deprecated = (values.containsKey("flashType")) ? "flashType" : "flash-type";
          String replacement = (values.containsKey("flashType")) ? "advancedAntiAliasing" : "advanced-anti-aliasing";
          context.localizedError2(node.pos(), new DeprecatedAttribute(deprecated, replacement, "3.0"));
        }
       
        return values;
    }

    public Value evaluate(Context context, ProgramNode node)
    {
        embedDataStack = new Stack<EmbedData>();

        super.evaluate(context, node);

        embedDataStack = null;

        return null;
    }

    private Map<QName, Source> generateSources(String packageName, Context cx, Node node)
    {
        Map<QName, Source> sources = new HashMap<QName, Source>();
        EmbedData embedData = getEmbedData();

        for (Iterator<Map.Entry<String, Map<String, Object>>> iterator = embedData.class2params.entrySet().iterator(); iterator.hasNext();)
        {
            Map.Entry<String, Map<String, Object>> e = iterator.next();
            String className = e.getKey();
            Map<String, Object> params = e.getValue();

            generateSources(sources, packageName, className, params, cx, node);
        }
        return sources;
    }

    private void generateSources(Map<QName, Source> sources, String packageName, String className, Map<String, Object> embedMap, Context cx, Node node )
    {
        int line = embedMap.containsKey( Transcoder.LINE ) ? (Integer.parseInt( embedMap.get( Transcoder.LINE ).toString() )) : -1;
        int col = embedMap.containsKey( Transcoder.COLUMN ) ? (Integer.parseInt( embedMap.get( Transcoder.COLUMN ).toString() )) : -1;
        String path = embedMap.containsKey( Transcoder.FILE ) ? (String) embedMap.get( Transcoder.FILE ) : "";
        String pathSep = embedMap.containsKey( Transcoder.PATHSEP ) ? (String) embedMap.get( Transcoder.PATHSEP ) : null;
        if ("true".equals(pathSep))
        {
          path = path.replace('/', '\\');
        }

        // TODO: This kind of logic should be encapsulated inside Source or even QName
        String packagePrefix = packageName == null || packageName.equals( "" ) ? "" : packageName + ".";
        String nameForReporting = path != null ? path : unit.getSource().getNameForReporting();

        try
        {
            Transcoder.TranscodingResults asset = EmbedUtil.transcode(transcoders, unit, symbolTable,
                                                                      packagePrefix + className,
                                                                      embedMap, line, col, true);
            if (asset != null)
            {
                try
                {
                    generateSource(sources, asset, packageName, className);
                }
                catch(IOException ioe)
                {
                    if (Trace.error)
                    {
                        ioe.printStackTrace();
                    }
                    cx.localizedError(nameForReporting, line, col, ioe.getLocalizedMessage(), "");
                }
            }
          else if (embedMap.containsKey(Transcoder.SOURCE))
          {
            Object what = embedMap.get(Transcoder.SOURCE);
            cx.localizedError2(nameForReporting, line, col, new UnableToTranscode(what), "");
          }
        }
        catch (Exception e)
        {
            if (Trace.error)
            {
                e.printStackTrace();
            }
            cx.localizedError2(nameForReporting, line, col, new UnableToCreateSource(packagePrefix + className, e), null);
        }
    }

    private void generateSource(Map<QName, Source> sources,
            Transcoder.TranscodingResults asset, String packageName, String className) throws IOException
    {
        // TODO: This kind of logic should be encapsulated inside Source or even QName
        String packagePrefix = packageName == null || packageName.equals("") ? "" : packageName + ".";
        String generatedName = (packagePrefix + className).replace('.', File.separatorChar) + ".as";

        if (generatedOutputDir != null)
        {
            FileUtils.writeClassToFile(generatedOutputDir, packagePrefix, className + ".as", asset.generatedCode);
        }

        // timestamp of this compiler-generated Source should match the asset file timestamp
        String relativePath = "";
        if (packageName != null)
        {
            relativePath = packageName.replace( '.', '/' );
        }

        long modified = asset.assetSource != null ? asset.assetSource.getLastModified() : -1;
        TextFile file = new TextFile(asset.generatedCode, generatedName, null, MimeMappings.AS, modified);
        Source source = new Source(file, relativePath, className, null, false, false, false);
        source.setAssetInfo(unit.getAssets().get(className));
        source.setPathResolver(unit.getSource().getPathResolver());

        if (source != null)
            sources.put(new QName(packageName, className), source);

        // Also recursively generate sources for any additional assets
        List<Transcoder.TranscodingResults> childAssets = asset.additionalAssets;
        if (childAssets != null)
        {
            for (int i = 0; i < childAssets.size(); i++)
            {
                Transcoder.TranscodingResults childAsset = childAssets.get(i);
                String qualifiedClassName = childAsset.className;
                if (qualifiedClassName != null)
                {
                    int dot = qualifiedClassName.lastIndexOf('.');
                    if (dot == -1)
                    {
                        className = qualifiedClassName;
                        packageName = "";
                    }
                    else
                    {
                        className = qualifiedClassName.substring(dot + 1);
                        packageName = qualifiedClassName.substring(0, dot);
                    }

                    generateSource(sources, childAsset, packageName, className);
                }
            }
        }
    }

    private MemberExpressionNode generateClassInitializer(String name)
    {
        IdentifierNode identifier = new IdentifierNode(name, 0);

        GetExpressionNode getExpression = new GetExpressionNode(identifier);

        getExpression.pos(0);

        MemberExpressionNode result = new MemberExpressionNode(null, getExpression, 0);

        return result;
    }

    private LiteralStringNode generateStringInitializer(String name)
    {
        LiteralStringNode literalString = new LiteralStringNode(name);

        literalString.pos(0);

        return literalString;
    }

    // EmbedData holds all embeds for a given class
    class EmbedData
    {
        public String referenceClassName;
        public Map<String, Map<String, Object>> class2params = new HashMap<String, Map<String, Object>>();    // unique prefix -> map of embed params
        public boolean inUse;

        public void clear()
        {
            if (hasData())
            {
                class2params = new HashMap<String, Map<String, Object>>();
            }
        }

        public boolean hasData()
        {
            return (class2params.size() != 0);
        }
    }

  // error messages

  public static class UnableToTranscode extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = -3610099352282214079L;

        public UnableToTranscode(Object what)
    {
      super();
      this.what = what;
    }

    public final Object what;
  }

  public static class UnableToCreateSource extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = -8930568038744470639L;

        public UnableToCreateSource(String name)
    {
      super();
      this.name = name;
    }

        public UnableToCreateSource(String name, Throwable rootCause)
        {
            super(rootCause);
            this.name = name;
        }

    public final String name;
  }

  public static class UnsupportedTypeForEmbed extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = 8396431734939591871L;

        public UnsupportedTypeForEmbed()
    {
      super();
    }
  }

  public static class InvalidEmbedVariable extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = 4571106802738159454L;

        public InvalidEmbedVariable()
    {
      super();
    }
  }

  public static class EmbedOnlyOnClassesAndVars extends CompilerMessage.CompilerError
  {
    private static final long serialVersionUID = -7360808013645966487L;

        public EmbedOnlyOnClassesAndVars()
    {
      super();
    }
  }
 
  public static class DeprecatedAttribute extends CompilerMessage.CompilerWarning
  {
    private static final long serialVersionUID = 2749704493074687265L;

        public DeprecatedAttribute(String deprecated, String replacement, String since)
    {
      this.deprecated = deprecated;
      this.replacement = replacement;
      this.since = since;
    }
   
    public final String deprecated, replacement, since;
  }
}
TOP

Related Classes of flex2.compiler.as3.EmbedEvaluator$EmbedOnlyOnClassesAndVars

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.