/* * * 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.units; import java.io.FileNotFoundException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.flex.compiler.internal.as.codegen.CodeGeneratorManager; import org.apache.commons.io.FilenameUtils; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.common.Multiname; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.definitions.references.IReference; import org.apache.flex.compiler.definitions.references.ReferenceFactory; import org.apache.flex.compiler.filespecs.IFileSpecification; import org.apache.flex.compiler.fxg.dom.IFXGNode; import org.apache.flex.compiler.fxg.flex.FXGSymbolClass; import org.apache.flex.compiler.fxg.flex.FlexFXG2SWFTranscoder; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.PackageDefinition; import org.apache.flex.compiler.internal.fxg.resources.FXGFileResolver; import org.apache.flex.compiler.internal.fxg.sax.FXGSAXParser; import org.apache.flex.compiler.internal.parsing.as.ASParser; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.projects.DefinitionPriority.BasePriority; import org.apache.flex.compiler.internal.scopes.ASProjectScope; import org.apache.flex.compiler.internal.scopes.FXGFileScope; import org.apache.flex.compiler.internal.scopes.PackageScope; import org.apache.flex.compiler.internal.scopes.TypeScope; import org.apache.flex.compiler.internal.semantics.PostProcessStep; import org.apache.flex.compiler.internal.targets.TagSorter; import org.apache.flex.compiler.internal.tree.as.FileNode; import org.apache.flex.compiler.internal.units.requests.ABCBytesRequestResult; import org.apache.flex.compiler.internal.units.requests.ASFileScopeRequestResult; import org.apache.flex.compiler.internal.units.requests.SyntaxTreeRequestResult; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InternalCompilerProblem2; import org.apache.flex.compiler.units.requests.IABCBytesRequestResult; import org.apache.flex.compiler.units.requests.IFileScopeRequestResult; import org.apache.flex.compiler.units.requests.IOutgoingDependenciesRequestResult; import org.apache.flex.compiler.units.requests.ISWFTagsRequestResult; import org.apache.flex.compiler.units.requests.ISyntaxTreeRequestResult; import org.apache.flex.swf.SWFFrame; import org.apache.flex.swf.tags.DoABCTag; import org.apache.flex.swf.tags.ICharacterTag; import org.apache.flex.swf.tags.ITag; import org.apache.flex.utils.FilenameNormalization; import com.google.common.collect.Iterables; /** * This is a Compilation Unit which handles FXG compilation */ public class FXGCompilationUnit extends CompilationUnitBase { private static final String fxgBaseClassName = "spark.core.SpriteVisualElement"; private String qname; private static final String SUB_SYSTEM = "FXGCompilationUnit"; // used for error reporting /** * A {@code IFileSpecification} implementation from {@code String}. */ private class GeneratedSourceFileSpecfication implements IFileSpecification { public GeneratedSourceFileSpecfication(String name, String content) { this.reader = new StringReader(content); this.name = FilenameNormalization.normalize(name); } private final StringReader reader; private final String name; @Override public String getPath() { return name; } @Override public Reader createReader() throws FileNotFoundException { return reader; } @Override public long getLastModified() { return 0; } @Override public boolean isOpenDocument() { return false; } } public FXGCompilationUnit(CompilerProject project, String path, BasePriority basePriority, String qname) { super(project, path, basePriority, qname); this.qname = qname; } @Override public UnitType getCompilationUnitType() { return UnitType.FXG_UNIT; } @Override protected ISyntaxTreeRequestResult handleSyntaxTreeRequest() throws InterruptedException { startProfile(Operation.GET_SYNTAX_TREE); getProject().clearScopeCacheForCompilationUnit(this); final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(); IFXGNode fxgroot = null; try { fxgroot = new FXGSAXParser().parse(getRootFileSpecification().createReader(), qname, problems); } catch(Exception ex) { ex.printStackTrace(); problems.add(new InternalCompilerProblem2(getAbsoluteFilename(), ex, SUB_SYSTEM)); } finally { stopProfile(Operation.GET_SYNTAX_TREE); } return new FXGSyntaxTreeRequestResult(fxgroot, getRootFileSpecification().getLastModified(), problems); } /** * Syntax Tree request result for FXG files. FXG files are not represented * by a traditional AST, they use their own tree and this class allows * {@link FXGCompilationUnit} to cache an FXG tree result. */ private static class FXGSyntaxTreeRequestResult extends SyntaxTreeRequestResult { private IFXGNode rootNode; public FXGSyntaxTreeRequestResult(IFXGNode rootNode, long lastModified, Collection<ICompilerProblem> problems) { super(lastModified, problems); this.rootNode = rootNode; } /** * Returns the root node for the FXG file handled * by this compilation unit * * @return root node */ public IFXGNode getRootNode() { return rootNode; } } /** * This function will create a temporary filscope. Please note that this will not be the filescope that is used in the actual AST */ @Override protected IFileScopeRequestResult handleFileScopeRequest() throws InterruptedException { startProfile(Operation.GET_FILESCOPE); try { List<ICompilerProblem> noProblems = Collections.emptyList(); IFileSpecification rootFileSpec = getRootFileSpecification(); FXGFileScope fileScope = createFileScope(); return new ASFileScopeRequestResult(getDefinitionPromises(), getDefinitionPriority(), noProblems, fileScope, rootFileSpec); } finally { stopProfile(Operation.GET_FILESCOPE); } } @Override protected IOutgoingDependenciesRequestResult handleOutgoingDependenciesRequest () throws InterruptedException { startProfile(Operation.GET_SEMANTIC_PROBLEMS); final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(); Map<ITag, ITag> extraTags = new HashMap<ITag, ITag>(); FXGSymbolClass symbolClass = null; FileNode fileNode = null; ASProjectScope projectScope = getProject().getScope(); try { FXGSyntaxTreeRequestResult syntaxTreeResult = (FXGSyntaxTreeRequestResult)getSyntaxTreeRequest().get(); IFXGNode tree = syntaxTreeResult.getRootNode(); FlexFXG2SWFTranscoder transcoder = new FlexFXG2SWFTranscoder(getProject()); transcoder.setResourceResolver(new FXGFileResolver(FilenameUtils.getFullPath(getRootFileSpecification().getPath()))); symbolClass = transcoder.transcode(tree, Multiname.getPackageNameForQName(qname), Multiname.getBaseNameForQName(qname), extraTags, problems); //Add dependencies to the classes required by the FXG processed by this compilation unit for (ITypeDefinition definition : transcoder.getDependencies()) { getProject().addDependency(this, projectScope.getCompilationUnitForDefinition(definition), DependencyType.EXPRESSION, definition.getQualifiedName()); } StringBuilder sb = new StringBuilder(symbolClass.getGeneratedSource()); if (symbolClass.getAdditionalSymbolClasses() != null) { for (FXGSymbolClass symbol : symbolClass.getAdditionalSymbolClasses()) { sb.append(symbol.getGeneratedSource()); } } IFileSpecification virtualSymbolSource = new GeneratedSourceFileSpecfication(qname, sb.toString()); fileNode = ASParser.parseFile(virtualSymbolSource, getProject().getWorkspace()); fileNode.runPostProcess(EnumSet.of(PostProcessStep.POPULATE_SCOPE)); projectScope.addScopeForCompilationUnit(this, fileNode.getFileScope()); updateEmbedCompilationUnitDependencies(fileNode.getEmbedNodes(), problems); } catch (Exception e) { e.printStackTrace(); problems.add(new InternalCompilerProblem2(getAbsoluteFilename(), e, SUB_SYSTEM)); } finally { stopProfile(Operation.GET_SEMANTIC_PROBLEMS); } return new FXGOutgoingDependenciesResult(fileNode, symbolClass, extraTags, problems); } /** * Semantic problems result for FXG files. */ private static class FXGOutgoingDependenciesResult implements IOutgoingDependenciesRequestResult { private Collection<ICompilerProblem> problems; protected FileNode fileNode; protected FXGSymbolClass symbolClass; protected Map<ITag, ITag> extraTags; public FXGOutgoingDependenciesResult (FileNode fileNode, FXGSymbolClass symbolClass, Map<ITag, ITag> extraTags, Collection<ICompilerProblem> problems) { this.problems = problems; this.fileNode = fileNode; this.symbolClass = symbolClass; this.extraTags = extraTags; } @Override public ICompilerProblem[] getProblems() { return problems.toArray(new ICompilerProblem[0]); } } @Override protected IABCBytesRequestResult handleABCBytesRequest() throws InterruptedException { FXGOutgoingDependenciesResult semanticResults = (FXGOutgoingDependenciesResult) getOutgoingDependenciesRequest().get(); startProfile(Operation.GET_ABC_BYTES); try { byte abc[] = CodeGeneratorManager.getCodeGenerator().generate(getFilenameNoPath(), semanticResults.fileNode, getProject()).getABCBytes(); return new ABCBytesRequestResult(abc); } finally { stopProfile(Operation.GET_ABC_BYTES); } } @Override protected ISWFTagsRequestResult handleSWFTagsRequest() throws InterruptedException { FXGOutgoingDependenciesResult semanticResults = (FXGOutgoingDependenciesResult) getOutgoingDependenciesRequest().get(); IABCBytesRequestResult byteResult = getABCBytesRequest().get(); startProfile(Operation.GET_SWF_TAGS); final FXGSymbolClass symbolClass = semanticResults.symbolClass; final Map<ITag, ITag> extraTags = semanticResults.extraTags; final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(); final DoABCTag abcTag = new DoABCTag(); try { abcTag.setName(qname); abcTag.setABCData(byteResult.getABCBytes()); } catch (Exception e) { ICompilerProblem problem = new InternalCompilerProblem2(getRootFileSpecification().getPath(), e, SUB_SYSTEM); problems.add(problem); } finally { stopProfile(Operation.GET_SWF_TAGS); } return new ISWFTagsRequestResult() { @Override public boolean addToFrame(SWFFrame frame) { ICharacterTag symbolTag = symbolClass.getSymbol(); List<ITag> symbolTags = TagSorter.sortFullGraph(Collections.singletonList((ITag)symbolTag)); for (ITag tag : symbolTags) { frame.addTag(tag); if (extraTags.containsKey(tag)) { frame.addTag(extraTags.get(tag)); } } if (symbolClass.getAdditionalSymbolClasses() != null ) { for (FXGSymbolClass symbol : symbolClass.getAdditionalSymbolClasses()) { frame.defineSymbol(symbol.getSymbol(), symbol.getQualifiedClassName()); } } frame.addTag(abcTag); frame.defineSymbol(symbolClass.getSymbol(), qname); return true; } @Override public ICompilerProblem[] getProblems() { return problems.toArray(new ICompilerProblem[0]); } @Override public String getDoABCTagName() { return abcTag.getName(); } @Override public DoABCTag getDoABCTag() { return abcTag; } }; } /** * Creates an empty FileScope for purposes of definition reporting * * @return a {@link FXGFileScope} with a class definition for the FXG source */ private FXGFileScope createFileScope() { FXGFileScope fileScope = new FXGFileScope(this, getAbsoluteFilename()); String packageName = Multiname.getPackageNameForQName(qname); PackageScope packageScope = new PackageScope(fileScope, packageName); packageScope.setContainingScope(fileScope); PackageDefinition packageDefinition = new PackageDefinition(packageName); packageDefinition.setContainedScope(packageScope); fileScope.addDefinition(packageDefinition); Multiname mname = Multiname.crackDottedQName(getProject(), qname); INamespaceDefinition packageNS = Iterables.getOnlyElement(mname.getNamespaceSet()); ClassDefinition classDefinition = new ClassDefinition(mname.getBaseName(), (INamespaceReference)packageNS); IReference baseClass = ReferenceFactory.packageQualifiedReference(getProject().getWorkspace(), fxgBaseClassName); classDefinition.setBaseClassReference(baseClass); TypeScope classScope = new TypeScope(packageScope, classDefinition); classScope.setContainingDefinition(classDefinition); classDefinition.setContainedScope(classScope); classDefinition.setupThisAndSuper(); packageScope.addDefinition(classDefinition); return fileScope; } }