org.apache.flex.compiler.internal.targets.FlexLibrarySWFTarget.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.flex.compiler.internal.targets.FlexLibrarySWFTarget.java

Source

/*
 *
 *  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.targets;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

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.Name;
import org.apache.flex.compiler.constants.IASLanguageConstants;
import org.apache.flex.compiler.definitions.IClassDefinition;
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.projects.FlexProject;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.problems.ResourceBundleNotFoundForLocaleProblem;
import org.apache.flex.compiler.problems.ResourceBundleNotFoundProblem;
import org.apache.flex.compiler.targets.ITargetSettings;
import org.apache.flex.compiler.units.ICompilationUnit;
import org.apache.flex.swf.ISWF;
import org.apache.flex.swf.SWFFrame;
import org.apache.flex.swf.tags.DoABCTag;
import com.google.common.collect.ForwardingCollection;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

public final class FlexLibrarySWFTarget extends LibrarySWFTarget {
    public FlexLibrarySWFTarget(FlexProject project, ITargetSettings targetSettings,
            Set<ICompilationUnit> rootedCompilationUnits) {
        super(project, targetSettings, rootedCompilationUnits);
        flexProject = project;
        delegate = new FlexDelegate(targetSettings, project);
    }

    private final FlexProject flexProject;

    private ModuleFactoryInfo moduleFactoryInfo;

    private final FlexDelegate delegate;

    private ModuleFactoryInfo computeModuleFactoryInfo() {
        final IResolvedQualifiersReference moduleFactoryBaseClassReference = ReferenceFactory
                .packageQualifiedReference(flexProject.getWorkspace(), getBaseClassQName());
        final IDefinition moduleFactoryBaseClassDef = moduleFactoryBaseClassReference.resolve(flexProject);
        if (!(moduleFactoryBaseClassDef instanceof ClassDefinition))
            return ModuleFactoryInfo.create(Collections.<ICompilerProblem>emptyList()); // TODO make a compiler problem here!

        final ClassDefinition moduleFactoryBaseClass = (ClassDefinition) moduleFactoryBaseClassDef;

        final ICompilationUnit moduleFactoryBaseClassCompilationUnit = flexProject.getScope()
                .getCompilationUnitForDefinition(moduleFactoryBaseClass);
        assert moduleFactoryBaseClassCompilationUnit != null : "Unable to find compilation unit for definition!";

        return ModuleFactoryInfo.create(getGeneratedModuleFactoryClassName(moduleFactoryBaseClass),
                moduleFactoryBaseClass, moduleFactoryBaseClassCompilationUnit);
    }

    private ModuleFactoryInfo getModuleFactoryInfo() {
        if (moduleFactoryInfo != null)
            return moduleFactoryInfo;
        moduleFactoryInfo = computeModuleFactoryInfo();
        return moduleFactoryInfo;
    }

    @Override
    public String getBaseClassQName() {
        // TODO: check configuration for a user defined class.
        // Defaults to an "empty" module factory to handle the case where fonts
        // are embedded in an RSL.
        return "EmptyModuleFactory";
    }

    @Override
    protected FramesInformation computeFramesInformation() throws InterruptedException {
        final ModuleFactoryInfo moduleFactoryInfo = getModuleFactoryInfo();
        if (!moduleFactoryInfo.generateModuleFactory())
            return super.computeFramesInformation();
        final Set<ICompilationUnit> compilationUnits = Sets.union(
                Collections.singleton(moduleFactoryInfo.moduleFactoryBaseClassCompilationUnit),
                this.rootedCompilationUnits);
        final SWFFrameInfo frameInfo = new SWFFrameInfo(null, SWFFrameInfo.EXTERNS_ALLOWED, compilationUnits,
                moduleFactoryInfo.problems);
        final FlexLibrarySWFFramesInformation framesInfo = new FlexLibrarySWFFramesInformation(frameInfo);
        return framesInfo;
    }

    /**
     * Generated a unique name for the root class name.
     * @param baseClass
     * @return unique class name for the library.swf in this SWC.
     */
    private String getGeneratedModuleFactoryClassName(IClassDefinition moduleFactoryBaseClass) {
        File outputFile = targetSettings.getOutput();
        String outputName = null;
        String absolutePath = null;
        if (outputFile != null) {
            absolutePath = outputFile.getAbsolutePath();
            String name = outputFile.getName();
            if (name != null) {
                int endIndex = name.lastIndexOf('.');
                if (endIndex != -1) {
                    name = name.substring(0, endIndex);
                }
            }

            // help make root class unique by using a hashcode
            // of the absolute path of the swc.
            outputName = name + "_" + absolutePath.hashCode();
        }

        assert outputName != null : "Provide an output name for the SWC by setting -output";

        // Use system time as a fall back for a unique name for the SWC.
        if (outputName == null)
            outputName = Long.toHexString(System.nanoTime());

        String generatedRootName = "_" + outputName + "_" + moduleFactoryBaseClass.getQualifiedName();
        generatedRootName = generatedRootName.replaceAll("[^a-zA-Z0-9]", "_");
        return generatedRootName;
    }

    @Override
    protected DirectDependencies getDirectDependencies(ICompilationUnit cu) throws InterruptedException {
        final DirectDependencies directDependencies = super.getDirectDependencies(cu);
        if (!targetSettings.isAccessible())
            return directDependencies;
        final DirectDependencies acccessibilityDependencies = delegate.getAccessibilityDependencies(cu);
        return DirectDependencies.concat(directDependencies, acccessibilityDependencies);
    }

    @Override
    protected void waitForCompilationUnitToFinish(final ICompilationUnit cu,
            final Collection<ICompilerProblem> problems) throws InterruptedException {
        Collection<ICompilerProblem> problemsWithFilter = problems;

        // We we are externally linking a SWC into another SWC,
        // we need to filter out resource bundle not found problems
        // like the Flex 4.6.X compiler did.
        //
        // In an ideal world we would not need to do this
        // or we'd know we need to do it forever.  If we don't
        // need this filtering in the future, we can rip out this code.
        // If we continue to need this filter we should defer creation
        // of these problems until link time.
        if (getLinkageChecker().isExternal(cu)) {
            // Collection implementation that drops
            // ICompilerProblems on the floor if they are instances of
            // ResourceBundleNotFoundProblem or
            // ResourceBundleNotFoundForLocaleProblem.
            problemsWithFilter = new ForwardingCollection<ICompilerProblem>() {
                @Override
                protected final Collection<ICompilerProblem> delegate() {
                    return problems;
                }

                @Override
                public final boolean add(ICompilerProblem element) {
                    if (element instanceof ResourceBundleNotFoundProblem)
                        return false;
                    if (element instanceof ResourceBundleNotFoundForLocaleProblem)
                        return false;
                    return super.add(element);
                }

                @Override
                public final boolean addAll(Collection<? extends ICompilerProblem> collection) {
                    boolean result = false;
                    for (ICompilerProblem problem : collection) {
                        if (add(problem))
                            result = true;
                    }
                    return result;
                }
            };
        }
        super.waitForCompilationUnitToFinish(cu, problemsWithFilter);
    }

    @Override
    protected ISWF initializeSWF(List<ICompilationUnit> reachableCompilationUnits) throws InterruptedException {
        ISWF swf = super.initializeSWF(reachableCompilationUnits);
        delegate.addProductInfoToSWF(swf);

        return swf;
    }

    /**
     * Sub-class of {@link FramesInformation} that can create {@link SWFFrame}s
     * for all the frames in a library.swf in a flex SWC.
     * <p>
     * This class should only be constructed if we are also generating a module
     * factory.
     */
    private class FlexLibrarySWFFramesInformation extends FramesInformation {
        /**
         * Constructor
         * 
         * @param frameInfo The single {@link SWFFrameInfo} for the one frame in
         * a libary.swf in a flex SWC.
         */
        FlexLibrarySWFFramesInformation(SWFFrameInfo frameInfo) {
            super(Collections.singletonList(frameInfo));
        }

        @Override
        protected void createFrames(SWFTarget swfTarget, ISWF swf,
                ImmutableSet<ICompilationUnit> builtCompilationUnits, Set<ICompilationUnit> emittedCompilationUnits,
                Collection<ICompilerProblem> problems) throws InterruptedException {
            assert Iterables.size(frameInfos) == 1;
            SWFFrameInfo frameInfo = Iterables.getOnlyElement(frameInfos);

            final SWFFrame frame = createFrame(swfTarget, frameInfo, builtCompilationUnits, emittedCompilationUnits,
                    problems);

            ModuleFactoryInfo moduleFactoryInfo = getModuleFactoryInfo();

            delegate.addGeneratedRootClassToSWFFrame(frame, swf, moduleFactoryInfo, builtCompilationUnits,
                    problems);
            swf.addFrame(frame);
            swf.setTopLevelClass(moduleFactoryInfo.generatedModuleFactoryClassName);
        }
    }

    /**
     * Helper class that keeps track of information about generating a module factory.
     * <p>
     * Module factories are generatign for library.swf's that can be used as RSLs.
     */
    private static class ModuleFactoryInfo {
        /**
         * Creates a {@link ModuleFactoryInfo} that will <em>not</em> cause a
         * module factory to be generated. The resulting library.swf will
         * <em>not</em> be suitable for loading as an RSL in a flex
         * application.
         * 
         * @param problems
         * @return A new {@link ModuleFactoryInfo}
         */
        static ModuleFactoryInfo create(Iterable<ICompilerProblem> problems) {
            return new ModuleFactoryInfo(null, null, null, problems);
        }

        /**
         * Creates a {@link ModuleFactoryInfo} that will cause a module factory
         * to be generated. The resulting library.swf will be suitable for
         * loading as an RSL in a flex application.
         * 
         * @param generatedModuleFactoryClassName The name of the module factory
         * to generate
         * @param moduleFactoryBaseClass The {@link ClassDefinition} for the
         * base class of the generated module factory.
         * @param moduleFactoryBaseClassCompilationUnit The
         * {@link ICompilationUnit} that defines the base class of the generated
         * module factory.
         * @return A new {@link ModuleFactoryInfo}
         */
        static ModuleFactoryInfo create(String generatedModuleFactoryClassName,
                IClassDefinition moduleFactoryBaseClass, ICompilationUnit moduleFactoryBaseClassCompilationUnit) {
            assert generatedModuleFactoryClassName != null;
            assert moduleFactoryBaseClass != null;
            assert moduleFactoryBaseClassCompilationUnit != null;
            return new ModuleFactoryInfo(generatedModuleFactoryClassName, moduleFactoryBaseClass,
                    moduleFactoryBaseClassCompilationUnit, Collections.<ICompilerProblem>emptyList());
        }

        private ModuleFactoryInfo(String generatedModuleFactoryClassName, IClassDefinition moduleFactoryBaseClass,
                ICompilationUnit moduleFactoryBaseClassCompilationUnit, Iterable<ICompilerProblem> problems) {
            this.moduleFactoryBaseClass = moduleFactoryBaseClass;
            this.moduleFactoryBaseClassCompilationUnit = moduleFactoryBaseClassCompilationUnit;
            this.problems = problems;
            this.generatedModuleFactoryClassName = generatedModuleFactoryClassName;
        }

        /**
         * Determins if a module factory should be generated.
         * 
         * @return true if a module factory should be generated, false
         * otherwise.
         */
        boolean generateModuleFactory() {
            return moduleFactoryBaseClass != null;
        }

        final IClassDefinition moduleFactoryBaseClass;
        final ICompilationUnit moduleFactoryBaseClassCompilationUnit;
        final Iterable<ICompilerProblem> problems;
        final String generatedModuleFactoryClassName;
    }

    /**
     * Sub-class of {@link FlexTarget} that adds logic specific to building 
     * library.swf's in flex SWCs.
     */
    private class FlexDelegate extends FlexTarget {

        FlexDelegate(ITargetSettings targetSettings, FlexProject project) {
            super(targetSettings, project);
        }

        /**
         * Add the generated root class and its dependencies to the specified frame.
         * 
         * @param frame
         * @param swf
         * @param baseClass
         * @param projectScope
         * @param allowExternals if true, allow symbols to be externalized in this frame.
         * @param emittedCompilationUnits
         * @param problemCollection
         * @return
         * @throws InterruptedException
         */
        private boolean addGeneratedRootClassToSWFFrame(SWFFrame frame, ISWF swf,
                ModuleFactoryInfo moduleFactoryInfo, ImmutableSet<ICompilationUnit> emittedCompilationUnits,
                Collection<ICompilerProblem> problemCollection) throws InterruptedException {
            ABCEmitter emitter = new ABCEmitter();
            emitter.visit(ABCConstants.VERSION_ABC_MAJOR_FP10, ABCConstants.VERSION_ABC_MINOR_FP10);

            final String generatedRootClassNameString = moduleFactoryInfo.generatedModuleFactoryClassName;

            Name generatedRootName = new Name(generatedRootClassNameString);

            // Generate code for the constructor:
            // public function ClassName()
            // {
            //    super();
            // }
            InstructionList classITraitsInit = new InstructionList();
            classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
            classITraitsInit.addInstruction(ABCConstants.OP_constructsuper, 0);
            classITraitsInit.addInstruction(ABCConstants.OP_returnvoid);
            ClassGeneratorHelper classGen = new ClassGeneratorHelper(project, emitter, generatedRootName,
                    (ClassDefinition) moduleFactoryInfo.moduleFactoryBaseClass, Collections.<Name>emptyList(),
                    classITraitsInit);
            IResolvedQualifiersReference objectReference = ReferenceFactory
                    .packageQualifiedReference(project.getWorkspace(), IASLanguageConstants.Object);

            // Codegen various methods
            // TODO: Determine whether a override is needed or not depending on the 
            // methods in the base class. Same deal for the Create and Info Methods.
            codegenCallInContextMethod(classGen, true);

            final FlexLibraryFrame1Info frame1Info = new FlexLibraryFrame1Info(flexProject,
                    emittedCompilationUnits);

            // Override the create() and info() methods if we have embedded fonts.
            if (!frame1Info.embeddedFonts.isEmpty()) {
                codegenCreateMethod(classGen, objectReference.getMName());
                codegenInfoMethod(classGen, IASLanguageConstants.Object, frame1Info, accessibleClassNames,
                        problemCollection);
            }

            classGen.finishScript();

            DoABCTag doABC = new DoABCTag();
            try {
                doABC.setABCData(emitter.emit());
            } catch (Exception e) {
                return false;
            }

            doABC.setName(generatedRootClassNameString);
            frame.addTag(doABC);
            return true;
        }

        /**
         * Wrapping for the codegenInfoMethod.
         * Only exposing what library.swf needs.
         * 
         * @param classGen
         * @param rootClassQName
         * @param embeddedFonts
         * @param accessibleClassNames
         * @param problemCollection
         * @throws InterruptedException
         */
        private void codegenInfoMethod(ClassGeneratorHelper classGen, String rootClassQName,
                FlexFrame1Info frame1Info, Set<String> accessibleClassNames,
                Collection<ICompilerProblem> problemCollection) throws InterruptedException {
            codegenInfoMethod(classGen, null, rootClassQName, null, // preloader
                    null, // runtimeDPI
                    null, // splash screen
                    null, // root node
                    null, // target attributes
                    null, // locales
                    frame1Info, accessibleClassNames, null, // flex init
                    null, // styles class name
                    null, // rsls
                    null, // rslinof
                    problemCollection);

        }

    }

}