Package flex2.tools.oem

Source Code of flex2.tools.oem.Library

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.net.URI;

import flash.util.Trace;
import flex2.compiler.CompilationUnit;
import flex2.compiler.CompilerAPI;
import flex2.compiler.CompilerException;
import flex2.compiler.CompilerSwcContext;
import flex2.compiler.FileSpec;
import flex2.compiler.ResourceBundlePath;
import flex2.compiler.ResourceContainer;
import flex2.compiler.Source;
import flex2.compiler.SourceList;
import flex2.compiler.SourcePath;
import flex2.compiler.SubCompiler;
import flex2.compiler.SymbolTable;
import flex2.compiler.Transcoder;
import flex2.compiler.common.CompilerConfiguration;
import flex2.compiler.config.ConfigurationException;
import flex2.compiler.extensions.ExtensionManager;
import flex2.compiler.extensions.ILibraryExtension;
import flex2.compiler.i18n.I18nUtils;
import flex2.compiler.io.FileUtil;
import flex2.compiler.io.LocalFile;
import flex2.compiler.io.VirtualFile;
import flex2.compiler.swc.SwcAPI;
import flex2.compiler.swc.SwcArchive;
import flex2.compiler.swc.SwcCache;
import flex2.compiler.swc.SwcComponent;
import flex2.compiler.swc.SwcDirectoryArchive;
import flex2.compiler.swc.SwcLazyReadArchive;
import flex2.compiler.swc.SwcException;
import flex2.compiler.swc.SwcMovie;
import flex2.compiler.util.Benchmark;
import flex2.compiler.util.CompilerControl;
import flex2.compiler.util.MimeMappings;
import flex2.compiler.util.NameFormatter;
import flex2.compiler.util.NameMappings;
import flex2.compiler.util.PerformanceData;
import flex2.compiler.util.QName;
import flex2.compiler.util.ThreadLocalToolkit;
import flex2.linker.LinkerConfiguration;
import flex2.linker.LinkerException;
import flex2.linker.SimpleMovie;
import flex2.tools.CompcPreLink;
import flex2.tools.Mxmlc;
import flex2.tools.ToolsConfiguration;
import flex2.tools.WebTierAPI;
import flex2.tools.oem.internal.LibraryData;
import flex2.tools.oem.internal.OEMConfiguration;
import flex2.tools.oem.internal.OEMReport;
import flex2.tools.oem.internal.OEMUtil;
import macromedia.asc.util.ContextStatics;

/**
* The <code>Library</code> class represents a SWC archive or a RSL. It implements the <code>Builder</code> interface
* which allows for building the library incrementally. The following example defines a SWC archive or RSL:
*
* <pre>
* Library lib = new Library();
* </pre>
*
* You can add components to the <code>Library</code> object in the following ways:
*
* <pre>
* 1. String              - Specify a fully-qualified name.
* 2. File                - Specify a source file.
* 3. VirtualLocalFile    - Specify an in-memory source object.
* 4. URI                 - Specify a namespace URI.
* </pre>
*
* <p>
* To add resource bundles to the <code>Library</code>, you can use the <code>addResourceBundle()</code> method,
* as the following example shows:
*
* <pre>
* lib.addResourceBundle("mx.controls"));
* </pre>
*
* <p>
* To add archive files to the <code>Library</code>, you can use the <code>addArchiveFile()</code> method, as the following
* example shows:
*
* <pre>
* lib.addArchiveFile("defaults.css", new File("path1/myStyle.css"));
* </pre>
*
* Before you can compile with a <code>Library</code> object, you must configure it. The following
* four methods are the most common methods you use to configure the <code>Library</code> object:
*
* <pre>
* 1. setLogger()        - Use this to set a Logger so that the client can be notified of events that occurred during the compilation.
* 2. setConfiguration() - Optional. Use this to specify compiler options.
* 3. setOutput()        - Optional. Use this to specify an output file name.
* 4. setDirectory()     - Optional. Use this to specify an RSL output directory.
* </pre>
*
* You must implement the <code>flex2.tools.oem.Logger</code> interface and use the implementation as the <code>Logger</code>
* for the compilation. The following is an example <code>Logger</code> implementation:
*
* <pre>
* lib.setLogger(new flex2.tools.oem.Logger()
* {
*     public void log(Message message, int errorCode, String source)
*     {
*         System.out.println(message);
*     }
* });
* </pre>
*
* To specify compiler options for the <code>Library</code> object, you
* must get a <code>Configuration</code> object that is populated with default values. Then, you set
* compiler options programmatically using methods of the <code>Configuration</code> class.
*
* <p>
* The <code>setOutput()</code> method lets you specify where the <code>Library</code> object writes
* the output to. If you call the <code>setOutput()</code> method, the <code>build(boolean)</code> method
* writes directly to the specified location; for example:
*
* <pre>
* lib.setOutput(new File("MyLib.swc"));
* lib.build(true);
* </pre>
*
* If you do not call the <code>setOutput()</code> method, you can use the <code>build(OutputStream, boolean)</code>
* method. This requires that you provide a buffered output stream; for example:
*
* <pre>
* lib.build(new BufferedOutputStream(new FileOutputStream("MyLib.swc")), true);
* </pre>
*
* The <code>setDirectory()</code> method lets you output RSLs to the specified directory; for example:
*
* <pre>
* lib.setDirectory(new File("dir1"));
* lib.build(true);
* </pre>
*
* You can save the <code>Library</code> object compilation
* data for reuse. You do this using the <code>save(OutputStream)</code> method. Subsequent compilations can use
* the <code>load(OutputStream)</code> method to get the old data into the <code>Library</code> object; for example:
*
* <pre>
* lib.save(new BufferedOutputStream(new FileOutputStream("MyLib.incr")));
* </pre>
*
* When a cache file (for example, <code>MyLib.incr</code>) from a previous compilation is available before the
* compilation, you can call the <code>load(OutputStream)</code> method before you call the <code>build()</code> method; for example:
*
* <pre>
* lib.load(new BufferedInputStream(FileInputStream("MyLib.incr")));
* lib.build(true);
* </pre>
*
* The <code>build(false)</code> and <code>build(OutputStream, false)</code> methods always rebuild the library.
* The first time you build the <code>Library</code>
* object, the <code>build(true)/build(OutputStream, true)</code> methods do a complete build, which
* is equivalent to the <code>build(false)/build(OutputStream, false)</code> methods, respectively. After you call the
* <code>clean()</code> method, the <code>Library</code> object always does a full build.
*
* <p>
* The <code>clean()</code> method cleans up compilation data in the <code>Library</code> object the output
* file, if the <code>setOutput()</code> method was called.
*
* <p>
* You can use the <code>Library</code> class to build a library from a combination of source
* files in the file system and in-memory, dynamically-generated source objects. You
* must use the <code>addComponent(VirtualLocalFile)</code>, <code>addResourceBundle(VirtualLocalFile)</code>, and
* <code>addArchiveFile(String, VirtualLocalFile)</code> methods to use in-memory objects.
*
* <p>
* The <code>Library</code> class can be part of a <code>Project</code>.
*
* @see flex2.tools.oem.Configuration
* @see flex2.tools.oem.Project
* @version 2.0.1
* @author Clement Wong
*/
public class Library implements Builder, Cloneable
{
    static
    {
        // This "should" trigger the static initialization of Application which locates
        // flex-compiler-oem.jar and set application.home correctly.
        try
        {
            // in Java 1.4, simply saying Application.class would load the class
            // Java 1.5 is much smarter, and you have to coax the JVM to actually load it
            Class.forName("flex2.tools.oem.Application");
        }
        catch (ClassNotFoundException e)
        {
            // I guess it didn't work *shrug*
            e.printStackTrace();
            assert false;
        }
    }

    /**
     * Constructor.
     */
    public Library()
    {
        sources = new TreeSet<VirtualFile>(new Comparator<VirtualFile>()
        {
            public int compare(VirtualFile f0, VirtualFile f1)
            {
                return f0.getName().compareTo(f1.getName());
            }
        });
        classes = new TreeSet<String>();
        namespaces = new TreeSet<URI>();
        resourceBundles = new TreeSet<String>();
        files = new TreeMap<String, VirtualFile>();
        stylesheets = new TreeMap<String, VirtualFile>();

        oemConfiguration = null;
        logger = null;
        output = null;
        directory = null;
        mimeMappings = new MimeMappings();
        meter = null;
        resolver = null;
        cc = new CompilerControl();

        data = null;
        cacheName = null;
        configurationReport = null;
        messages = new ArrayList<Message>();
    }

    private Set<VirtualFile> sources;
    private Set<String> classes, resourceBundles;
    private Set<URI> namespaces;
    private Map<String, VirtualFile> files, stylesheets;
    private OEMConfiguration oemConfiguration;
    private Logger logger;
    private File output, directory;
    private MimeMappings mimeMappings;
    private ProgressMeter meter;
    protected PathResolver resolver;
    private CompilerControl cc;
    private ApplicationCache applicationCache;
    private LibraryCache libraryCache;

    // clean() would null out the following variables
    LibraryData data;
    private String cacheName, configurationReport;
    private List<Message> messages;
    private HashMap<String, PerformanceData[]> compilerBenchmarks;
    private Benchmark benchmark;

    /**
     * Adds a class, function, variable, or namespace to this <code>Library</code> object.
     *
     * This is the equilvalent of the <code>include-classes</code> option of the compc compiler.
     *
     * @param includeClass A fully-qualified name.
     */
    public void addComponent(String includeClass)
    {
        classes.add(includeClass);
    }

    /**
     * Adds a component to this <code>Library</code> object.
     * This is the equilvalent of the <code>include-sources</code> option of the compc compiler.
     *
     * @param includeSource A source file.
     */
    public void addComponent(File includeSource)
    {
        sources.add(new LocalFile(includeSource));
    }

    /**
     * Adds a component to this <code>Library</code> object.
     *
     * This is equilvalent to the <code>include-sources</code> option of the compc compiler.
     *
     * @param includeSource An in-memory source object.
     */
    public void addComponent(VirtualLocalFile includeSource)
    {
        sources.add(includeSource);
    }

    /**
     * Adds a list of components to this <code>Library</code> object.
     *
     * This is equilvalent to the <code>include-namespaces</code> option of the compc compiler.
     *
     * @param includeNamespace A namespace URI.
     */
    public void addComponent(URI includeNamespace)
    {
        namespaces.add(includeNamespace);
    }

    /**
     * Removes the specified component from this <code>Library</code> object.
     * The name can be a class, a function, a variable, or a namespace.
     *
     * @param includeClass A fully-qualified name.
     */
    public void removeComponent(String includeClass)
    {
        classes.remove(includeClass);
    }

    /**
     * Removes the specified component from this <code>Library</code> object.
     *
     * @param includeSource A source file.
     */
    public void removeComponent(File includeSource)
    {
        sources.remove(new LocalFile(includeSource));
    }

    /**
     * Removes the specified component from this <code>Library</code> object.
     *
     * @param includeSource An in-memory source object.
     */
    public void removeComponent(VirtualLocalFile includeSource)
    {
        sources.remove(includeSource);
    }

    /**
     * Removes the specified list of components from this <code>Library</code> object. The input argument is a namespace URI.
     *
     * @param includeNamespace A namespace URI.
     */
    public void removeComponent(URI includeNamespace)
    {
        namespaces.remove(includeNamespace);
    }

    /**
     * Removes all the components from this <code>Library</code> object.
     */
    public void removeAllComponents()
    {
        sources.clear();
        classes.clear();
        namespaces.clear();
    }

    /**
     * Adds a resource bundle to this <code>Library</code> object.
     *
     * This is equilvalent to the <code>include-resource-bundles</code> option of the compc compiler.
     *
     * @param resourceBundle A resource bundle name.
     */
    public void addResourceBundle(String resourceBundle)
    {
        resourceBundles.add(resourceBundle);
    }

    /**
     * Removes the specified resource bundle name from this <code>Library</code> object.
     *
     * @param resourceBundle A resource bundle name.
     */
    public void removeResourceBundle(String resourceBundle)
    {
        resourceBundles.remove(resourceBundle);
    }

    /**
     * Removes all the resource bundles from this <code>Library</code> object.
     *
     */
    public void removeAllResourceBundles()
    {
        resourceBundles.clear();
    }

    /**
     * Adds a file to this <code>Library</code> object. This is equilvalent to the <code>include-file</code> option of the compc compiler.
     *
     * @param name The name in the archive.
     * @param file The file to be added.
     */
    public void addArchiveFile(String name, File file)
    {
        files.put(name, new LocalFile(file));
    }

    /**
     * Adds an in-memory source object to this <code>Library</code> object. This is equilvalent to the <code>
     * include-file</code> option of the compc compiler.
     *
     * @param name The name in the archive.
     * @param file The in-memory source object to be added.
     */
    public void addArchiveFile(String name, VirtualLocalFile file)
    {
        files.put(name, file);
    }

    /**
     * Removes the specified file from this <code>Library</code> object.
     *
     * @param name The name in the archive.
     */
    public void removeArchiveFile(String name)
    {
        files.remove(name);
    }

    /**
     * Removes all the archive files from this <code>Library</code> object.
     */
    public void removeAllArchiveFiles()
    {
        files.clear();
    }

    /**
     * Adds a CSS stylesheet to this <code>Library</code> object. This is equilvalent to the <code>include-stylesheet</code> option of the compc compiler.
     *
     * @param name The name in the archive.
     * @param file The file to be added.
     * @since 3.0
     */
    public void addStyleSheet(String name, File file)
    {
        stylesheets.put(name, new LocalFile(file));
    }

    /**
     * Adds an in-memory CSS stylesheet object to this <code>Library</code> object. This is equilvalent to the <code>
     * include-stylesheet</code> option of the compc compiler.
     *
     * @param name The name in the archive.
     * @param file The in-memory source object to be added.
     * @since 3.0
     */
    public void addStyleSheet(String name, VirtualLocalFile file)
    {
        stylesheets.put(name, file);
    }

    /**
     * Removes the specified CSS stylesheet from this <code>Library</code> object.
     *
     * @param name The name in the archive.
     * @since 3.0
     */
    public void removeStyleSheet(String name)
    {
        stylesheets.remove(name);
    }

    /**
     * Removes all the CSS stylesheets from this <code>Library</code> object.
     * @since 3.0
     */
    public void removeAllStyleSheets()
    {
        stylesheets.clear();
    }

    /**
     * @inheritDoc
     */
    public void setConfiguration(Configuration configuration)
    {
        oemConfiguration = (OEMConfiguration) configuration;
    }

    /**
     * @inheritDoc
     */
    public Configuration getDefaultConfiguration()
    {
        return getDefaultConfiguration(false);
    }

    /**
     *
     * @param processDefaults
     * @return
     */
    private Configuration getDefaultConfiguration(boolean processDefaults)
    {
        return OEMUtil.getLibraryConfiguration(constructCommandLine(null), false, false,
                                               OEMUtil.getLogger(logger, messages), resolver,
                                               mimeMappings, processDefaults);
    }

    /**
     * @inheritDoc
     */
    public HashMap<String, PerformanceData[]> getCompilerBenchmarks()
    {
        return compilerBenchmarks;
    }

    /**
     * @inheritDoc
     */
    public Benchmark getBenchmark()
    {
        return benchmark;
    }

    /**
     * @inheritDoc
     */
    public Configuration getConfiguration()
    {
        return oemConfiguration;
    }

    /**
     * @inheritDoc
     */
    public void setLogger(Logger logger)
    {
        this.logger = logger;
    }

    /**
     * @inheritDoc
     */
    public Logger getLogger()
    {
        return logger;
    }

    /**
     * @inheritDoc
     */
    public void setSupportedFileExtensions(String mimeType, String[] extensions)
    {
        mimeMappings.set(mimeType, extensions);
    }

    /**
     * Sets the output destination. This method is necessary if you use the <code>build(boolean)</code> method.
     * If you use the <code>build(OutputStream, boolean)</code> method, there is no need to use this method.
     *
     * @param output An instance of the <code>java.io.File</code> class.
     */
    public void setOutput(File output)
    {
        this.output = output;
    }

    /**
     * Gets the output destination. This method returns <code>null</code> if you did not call the
     * <code>setOutput()</code> method.
     *
     * @return An instance of the <code>java.io.File</code> class, or <code>null</code> if you did not
     * call the <code>setOutput()</code> method.
     */
    public File getOutput()
    {
        return output;
    }

    /**
     * Sets the RSL output directory.
     *
     * @param directory An RSL directory.
     */
    public void setDirectory(File directory)
    {
        this.directory = directory;
    }

    /**
     * Gets the RSL output directory.
     *
     * @return A <code>java.io.File</code>, or <code>null</code> if you did not call the <code>setDirectory()</code> method.
     */
    public File getDirectory()
    {
        return directory;
    }

    /**
     * @inheritDoc
     */
    public void setProgressMeter(ProgressMeter meter)
    {
        this.meter = meter;
    }

    /**
     * @inheritDoc
     */
    public void setPathResolver(PathResolver resolver)
    {
        this.resolver = resolver;
    }

    /**
     * @inheritDoc
     */
    // IMPORTANT: If you make changes here, you probably want to mirror them in Application.build()
    public long build(boolean incremental) throws IOException
    {
        // I know that directory is not referenced anywhere in here...
        // if you setDirectory but do not setOutput, then output==null but dirctory!=null
        // so this silly looking IF needs to be like this...
        if (output != null || directory != null)
        {
            long size = 0;

            //TODO PERFORMANCE: A lot of unnecessary recopying and buffering here
            try
            {
                int result = compile(incremental);

                if (result == SKIP || result == LINK || result == OK)
                {
                    size = link(null);
                }

                return size;
            }
            finally
            {
                if ((output != null) && (data != null) && (data.swcCache != null))
                {
                    refreshLastModified();
                }

                if ((benchmark != null) && benchmark.hasStarted(Benchmark.POSTCOMPILE))
                {
                    benchmark.stopTime(Benchmark.POSTCOMPILE, false);
                }
               
                runExtensions();
               
                clean(false /* cleanData */,
                      false /* cleanCache */,
                      false /* cleanOutput */,
                      true /* cleanConfig */,
                      false /* cleanMessages */,
                      true /* cleanThreadLocals */);
            }
        }
        else
        {
            return 0;
        }
    }

    private void runExtensions()
    {
        if (oemConfiguration != null)
        {
            Set<ILibraryExtension> extensions = ExtensionManager.getLibraryExtensions( oemConfiguration.getExtensions() );

            for ( ILibraryExtension extension : extensions )
            {
                if (ThreadLocalToolkit.errorCount() == 0)
                {
                    extension.run( this.clone(), oemConfiguration.clone() );
                }
            }
        }
    }

    /**
     * @inheritDoc
     *
     * Note: If the OutputStream is written to a File,
     * refreshLastModified() should be called to update the timestamp
     * in the SwcCache.  Otherwise, subsequent builds in this Project
     * will think the Library has been externally updated and will
     * force a reload.
     */
    public long build(OutputStream out, boolean incremental) throws IOException
    {
        try
        {
            int result = compile(incremental);

            if (result == SKIP || result == LINK || result == OK)
            {
                return link(out);
            }
            else
            {
                return 0;
            }
        }
        finally
        {
            if ((benchmark != null) && benchmark.hasStarted(Benchmark.POSTCOMPILE))
            {
                benchmark.stopTime(Benchmark.POSTCOMPILE, false);
            }
           
            runExtensions();
           
            clean(false /* cleanData */,
                  false /* cleanCache */,
                  false /* cleanOutput */,
                  true /* cleanConfig */,
                  false /* cleanMessages */,
                  true /* cleanThreadLocals */);
        }
    }

    /**
     * @param fullRecompile if true a full recompile is needed, do not attempted to use cache file.
     *
     * @return  {@link Builder#OK} if this method call resulted in compilation of some/all parts of the application;
     *          {@link Builder#LINK} if this method call did not compile anything in the application but advise the caller to link again;
     *          {@link Builder#SKIP} if this method call did not compile anything in the application;
     *          {@link Builder#FAIL} if this method call encountered errors during compilation.
     */
    private int recompile(boolean fullRecompile, Map licenseMap, OEMConfiguration localOEMConfiguration)
    {
        data = new LibraryData();
        data.configuration = localOEMConfiguration.configuration;
        data.cacheName = cacheName;

        NameMappings mappings = CompilerAPI.getNameMappings(localOEMConfiguration.configuration), copy = mappings.copy();

        CompilerConfiguration compilerConfig = localOEMConfiguration.configuration.getCompilerConfiguration();
        compilerConfig.setMetadataExport(true);

        if (output != null || directory != null)
        {
            OEMUtil.setGeneratedDirectory(compilerConfig, output != null ? output : directory);
        }

        Transcoder[] transcoders = WebTierAPI.getTranscoders( localOEMConfiguration.configuration );
        SubCompiler[] compilers = WebTierAPI.getCompilers(compilerConfig, mappings, transcoders);

        if ((data.fileSet = processSources(compilerConfig)) == null)
        {
            return FAIL;
        }

        data.fileSet.addAll(processStylesheets());

        if (!setupSourceContainers(localOEMConfiguration.configuration, data.fileSet))
        {
            return FAIL;
        }

        // Setup SWC cache
        if (libraryCache != null)
        {
            ContextStatics contextStatics = libraryCache.getContextStatics();

            if (contextStatics != null)
            {
                // Clear out ASC's userDefined, so definitions from a
                // previous compilation don't spill over into this one.
                contextStatics.userDefined.clear();
                data.perCompileData = contextStatics;
                data.swcCache = libraryCache.getSwcCache();
            }
        }

        if (data.swcCache == null)
        {
            data.swcCache = new SwcCache();
        }
       
        // load SWCs
        CompilerSwcContext swcContext = new CompilerSwcContext(true);
        try
        {
            swcContext.load( compilerConfig.getLibraryPath(),
                             compilerConfig.getExternalLibraryPath(),
                             null,
                             compilerConfig.getIncludeLibraries(),
                             mappings,
                             I18nUtils.getTranslationFormat(compilerConfig),
                             data.swcCache );
        }
        catch (SwcException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            return FAIL;
        }

        // save the generated cache if the caller provided a libraryCache.
        if (libraryCache != null)
        {
            libraryCache.setSwcCache(data.swcCache);
        }

        data.includes = new HashSet<String>(swcContext.getIncludes());
        data.excludes = new HashSet<String>(swcContext.getExterns());
        localOEMConfiguration.configuration.addExterns( swcContext.getExterns() );
        localOEMConfiguration.configuration.addIncludes( swcContext.getIncludes() );
        data.swcArchiveFiles = new HashMap<String, VirtualFile>(swcContext.getIncludeFiles());
       
        // The ToolsConfiguration expects class names in QName format.
        Set<String> qNameClasses = new HashSet<String>();
        for (String className : classes)
        {
            qNameClasses.add(NameFormatter.toColon(className));
        }

        // Allow -include-classes to override the -external-library-path.
        localOEMConfiguration.configuration.removeExterns(qNameClasses);

        // If we want only inheritance dependencies of -include-classes then
        // add the classes to the includes list. When
        // -include-inheritance-dependencies-only is turned on the dependency
        // walker will ignore all the classes except for the includes.
        if (localOEMConfiguration.configuration.getIncludeInheritanceDependenciesOnly())
        {
            localOEMConfiguration.configuration.addIncludes(qNameClasses);
        }

        data.cmdChecksum = localOEMConfiguration.cfgbuf.checksum_ts();
        data.linkChecksum = localOEMConfiguration.cfgbuf.link_checksum_ts();
        data.swcChecksum = swcContext.checksum();
        int[] checksums = new int[] { 0, data.cmdChecksum, data.linkChecksum, data.swcChecksum };
        boolean clearCache = false;

        // C: must do loadCompilationUnits() after checksum calculation...
        if (!fullRecompile)
        {
            if (!loadCompilationUnits(localOEMConfiguration.configuration, data.fileSet, swcContext, checksums))
            {
                return FAIL;
            }

            data.checksum = checksums[0];
            if (data.units != null &&
                data.units.size() > 0 &&
                OEMUtil.isRecompilationNeeded(data, swcContext, localOEMConfiguration))
            {
                if (!setupSourceContainers(localOEMConfiguration.configuration, data.fileSet))
                {
                    return FAIL;
                }
                clearCache = true;
            }
        }
        else
        {
            clearCache = true;
        }

        if (clearCache && (libraryCache != null) && (output != null))
        {
            String path = FileUtil.getCanonicalPath(output);
            libraryCache.getSwcCache().remove(path);
        }

        // validate CompilationUnits...
        int count = CompilerAPI.validateCompilationUnits(data.fileSpec, data.sourceList, data.sourcePath, data.bundlePath,
                                                         data.resources, swcContext, data.classes, data.perCompileData,
                                                         localOEMConfiguration.configuration);

        SymbolTable symbolTable;

        if (data.perCompileData != null)
        {
            symbolTable = new SymbolTable(localOEMConfiguration.configuration, data.perCompileData);
        }
        else
        {
            symbolTable = new SymbolTable(localOEMConfiguration.configuration);
            data.perCompileData = symbolTable.perCompileData;

            if (libraryCache != null)
            {
                libraryCache.setContextStatics(data.perCompileData);
            }
        }

        Map<String, Source> classes = new TreeMap<String, Source>();
        if ((data.nsComponents = processInputs(swcContext, copy, classes)) == null)
        {
            return FAIL;
        }

        // Only updated the LibraryData's classes if processInputs()
        // is successful.
        data.classes = classes;
        data.sources = new ArrayList<Source>();
        data.units = compile(compilers, swcContext, symbolTable, mappings, licenseMap, data.classes, data.sources);

        // need to update the checksum here since doing a compile could add some
        // some signature checksums and change it.
        data.checksum = OEMUtil.calculateChecksum(data, swcContext, localOEMConfiguration);
        boolean forcedToStop = CompilerAPI.forcedToStop();

        if (data.units == null || forcedToStop)
        {
            return FAIL;
        }
        else
        {
            return OK;
        }
    }

    /**
     * @inheritDoc
     */
    public void stop()
    {
        cc.stop();
    }

    /**
     * @inheritDoc
     */
    public void clean()
    {
        clean(true /* cleanData */,
              true /* cleanCache */,
              true /* cleanOutput */,
              true /* cleanConfig */,
              true /* cleanMessages */,
              true /* cleanThreadLocals */);
    }

    /**
     * @inheritDoc
     */
    public void load(InputStream in) throws IOException
    {
        cacheName = OEMUtil.load(in, cacheName);
        clean(true /* cleanData */,
              false /* cleanCache */,
              false /* cleanOutput */,
              true /* cleanConfig */,
              false /* cleanMessages */,
              true /* cleanThreadLocals */);
    }

    /**
     * @inheritDoc
     */
    public long save(OutputStream out) throws IOException
    {
        return OEMUtil.save(out, cacheName, data);
    }

    /**
     * @inheritDoc
     */
    public Report getReport()
    {
        OEMUtil.setupLocalizationManager();
        return new OEMReport(data == null ? null : data.sources,
                             data == null ? null : data.movie,
                             data == null ? null : data.configuration,
                             data == null ? null : data.sourceList,
                             configurationReport,
                             messages, files);
    }

    /**
     *
     * @param c
     * @return
     */
    private String[] constructCommandLine(OEMConfiguration localOEMConfiguration)
    {
        String[] commandLine = (localOEMConfiguration != null) ? localOEMConfiguration.getCompilerOptions() :
                                                                 new String[0];
       
        // Translate "classes" into "-include-classes" so the CompcConfiguration can
        // properly validate the configuration.
        if (classes.size() > 0)
        {
            StringBuilder buffer = new StringBuilder("-include-classes=");

            for (Iterator<String> iter = classes.iterator(); iter.hasNext();)
            {
                String className = iter.next();
                buffer.append(className);
                if (iter.hasNext())
                {
                    buffer.append(",");
                }
            }
           
            String[] newCommandLine = new String[commandLine.length + 1];
            System.arraycopy(commandLine, 0, newCommandLine, 0, commandLine.length);
            newCommandLine[commandLine.length] = buffer.toString();
           
            return newCommandLine;
        }

        return commandLine;
    }

    /**
     *
     * @param swcContext
     * @param mappings
     * @param classes
     * @return
     */
    private Set<SwcComponent> processInputs(CompilerSwcContext swcContext, NameMappings mappings, Map<String, Source> classes)
    {
        try
        {
            Set<SwcComponent> nsComponents = processNamespaces(mappings, classes);
            if (nsComponents == null)
            {
                return null;
            }

            if (!processClasses(classes))
            {
                return null;
            }

            for (Map.Entry<String, Source> entry : classes.entrySet())
            {
                Source source = entry.getValue();
                String namespaceURI = source.getRelativePath().replace('/', '.');
                String localPart = source.getShortName();
                Source swcSource = (swcContext != null) ? swcContext.getSource(namespaceURI, localPart) : null;
               
                // No sense recompiling the same source file again.
                if ((swcSource != null) &&
                    ((source.getLastModified() == swcSource.getLastModified()) &&
                     ((source.getCompilationUnit() == null) ||
                      (!source.getCompilationUnit().hasTypeInfo))))
                {
                    classes.put(entry.getKey(), swcSource);
                }
            }

            return nsComponents;
        }
        catch (SwcException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            assert ThreadLocalToolkit.errorCount() > 0;
            return null;
        }
    }

    /**
     *
     * @param classes
     * @return
     */
    private boolean processClasses(Map<String, Source> classes)
    {
        try
        {
            SwcAPI.setupClasses(new ArrayList<String>(this.classes), data.sourcePath, data.sourceList, classes);
            return true;
        }
        catch (CompilerException ex)
        {
            assert ThreadLocalToolkit.errorCount() > 0;
            return false;
        }
    }

    /**
     * This must be called before CompilerSwcContext.load().
     *
     * @param mappings
     * @param classes
     * @return
     */
    private Set<SwcComponent> processNamespaces(NameMappings mappings, Map<String, Source> classes)
    {
        Set<SwcComponent> nsComponents = null;

        try
        {
            List<SwcComponent> list = SwcAPI.setupNamespaceComponents(toStrings(namespaces), mappings,
                                                                      data.sourcePath, data.sourceList,
                                                                      classes);
            nsComponents = new TreeSet<SwcComponent>(new Comparator<SwcComponent>()
            {
                public int compare(SwcComponent c0, SwcComponent c1)
                {
                    return c0.getClassName().compareTo(c1.getClassName());
                }
            });
            nsComponents.addAll(list);
        }
        catch (ConfigurationException ex)
        {
            Mxmlc.processConfigurationException(ex, "oem");
        }
        catch (CompilerException ex)
        {
            assert ThreadLocalToolkit.errorCount() > 0;
        }

        return nsComponents;
    }

    /**
     *
     * @param set
     * @return
     */
    private List<String> toStrings(Set<URI> set)
    {
        List<String> a = new ArrayList<String>(set.size());
        for (Iterator<URI> i = set.iterator(); i.hasNext(); )
        {
            URI uri = i.next();
            a.add(uri.toString());
        }
        return a;
    }

    /**
     * @param configuration
     * @param fileList
     * @return true, unless a CompilerException occurs.
     */
    private boolean setupSourceContainers(flex2.compiler.common.Configuration configuration, Set<VirtualFile> fileSet)
    {
        CompilerConfiguration compilerConfig = configuration.getCompilerConfiguration();
        VirtualFile[] asClasspath = compilerConfig.getSourcePath();
        boolean result = false;

        try
        {
            // create a SourcePath...
            data.sourcePath = new SourcePath(WebTierAPI.getSourcePathMimeTypes(),
                                             compilerConfig.allowSourcePathOverlap());
            data.sourcePath.addPathElements( asClasspath );

            List<VirtualFile>[] array = CompilerAPI.getVirtualFileList(fileSet, data.sourcePath.getPaths());

            // create a FileSpec...
            data.fileSpec = new FileSpec(array[0], WebTierAPI.getFileSpecMimeTypes(), false);

            // create a SourceList...
            data.sourceList = new SourceList(array[1], asClasspath, null, WebTierAPI.getSourceListMimeTypes(), false);
           
            // create a ResourceContainer...
            data.resources = new ResourceContainer();

            // create a ResourceBundlePath...
            data.bundlePath = new ResourceBundlePath(compilerConfig, null);

            // clear these...
            if (data.sources != null) data.sources.clear();
            if (data.units != null) data.units.clear();
            if (data.swcDefSignatureChecksums != null) data.swcDefSignatureChecksums.clear();
            if (data.swcFileChecksums != null) data.swcFileChecksums.clear();

            result = true;
        }
        catch (CompilerException ex)
        {
            assert ThreadLocalToolkit.errorCount() > 0;
        }

        return result;
    }

    /**
     * Convert the set of sources, which might include directories and
     * files with tokens in the name, into a set of resolved
     * VirtualFiles.
     *
     * @return
     */
    private Set<VirtualFile> processSources(CompilerConfiguration compilerConfiguration)
    {
        Set<VirtualFile> fileSet = null;

        try
        {
            // Create a list of LocalFile paths and remove the LocalFiles "sources".
            List<String> localPathList = new ArrayList<String>();

            for (Iterator<VirtualFile> iterator = sources.iterator(); iterator.hasNext();)
            {
                VirtualFile virtualFile = iterator.next();

                if (virtualFile instanceof LocalFile)
                {
                    localPathList.add(virtualFile.getName());
                    iterator.remove();
                }
            }

            // Convert the localPathList to an Array
            String[] localPathArray = new String[localPathList.size()];
            int i = 0;

            for (String localPath : localPathList)
            {
                localPathArray[i++] = localPath;
            }

            VirtualFile[] virtualFileArray =
                compilerConfiguration.expandTokens(localPathArray, compilerConfiguration.getLocales(), null);

            // Add the token expanded VirtualFile's back into "sources".
            for (VirtualFile virtualFile : virtualFileArray)
            {
                sources.add(virtualFile);
            }

            List<VirtualFile> fileList =
                CompilerAPI.getVirtualFileList(sources, new HashSet<String>(Arrays.asList(WebTierAPI.getSourcePathMimeTypes())));
            fileSet = new TreeSet<VirtualFile>(new Comparator<VirtualFile>()
            {
                public int compare(VirtualFile f0, VirtualFile f1)
                {
                    return f0.getName().compareTo(f1.getName());
                }
            });
            fileSet.addAll(fileList);
        }
        catch (ConfigurationException ex)
        {
            Mxmlc.processConfigurationException(ex, "oem");
            assert ThreadLocalToolkit.errorCount() > 0;
        }

        return fileSet;
    }

    /**
     *
     * @return
     */
    private Set<VirtualFile> processStylesheets()
    {
        Set<VirtualFile> fileSet = null;

        try
        {
            List<VirtualFile> fileList = CompilerAPI.getVirtualFileList(new ArrayList<VirtualFile>(stylesheets.values()), new HashSet<String>(Arrays.asList(new String[] { MimeMappings.CSS })));
            fileSet = new TreeSet<VirtualFile>(new Comparator<VirtualFile>()
            {
                public int compare(VirtualFile f0, VirtualFile f1)
                {
                    return f0.getName().compareTo(f1.getName());
                }
            });
            fileSet.addAll(fileList);
        }
        catch (ConfigurationException ex)
        {
            assert ThreadLocalToolkit.errorCount() > 0;
        }

        return fileSet;
    }

    /**
     *
     * @param configuration
     * @param fileList
     * @return
     */
    private boolean loadCompilationUnits(ToolsConfiguration configuration, Set fileSet, CompilerSwcContext swcContext, int[] checksums)
    {
        if (data.cacheName == null) // note: NOT (cacheName == null)
        {
            return true;
        }

        RandomAccessFile cacheFile = null;

        try
        {
            cacheFile = new RandomAccessFile(data.cacheName, "r");
            CompilerAPI.loadCompilationUnits(configuration, data.fileSpec, data.sourceList,
                                             data.sourcePath, data.resources, data.bundlePath,
                                             data.sources = new ArrayList<Source>(),
                                             data.units = new ArrayList<CompilationUnit>(),
                                             checksums,
                                             data.swcDefSignatureChecksums = new HashMap<QName, Long>(),
                                             data.swcFileChecksums = new HashMap<String, Long>(),
                                             cacheFile, data.cacheName);

            /*
            for (int i = 0, size = data.sources.size(); i < size; i++)
            {
                Object obj = data.sources.get(i);
                if (obj instanceof String)
                {
                    String name = (String) obj;
                    Source s = swcContext.getSource(name);
                    data.sources.set(i, s);
                    data.units.set(i, s != null ? s.getCompilationUnit() : null);
                }
            }
            */
        }
        catch (FileNotFoundException ex)
        {
            ThreadLocalToolkit.logInfo(ex.getMessage());
            // if the cache file is not found, no big deal... return true so that we recompile.
            return true;
        }
        catch (IOException ex)
        {
            ThreadLocalToolkit.logInfo(ex.getMessage());

            if (!setupSourceContainers(configuration, fileSet))
            {
                return false;
            }
        }
        finally
        {
            if (cacheFile != null) try { cacheFile.close(); } catch (IOException ex) {}
        }

        return true;
    }

    /**
     * Compiles the <code>Library</code>. This method does not link the <code>Library</code>.
     *
     * @param incremental If <code>true</code>, build incrementally; if <code>false</code>, rebuild.
     * @return  {@link Builder#OK} if this method call resulted in compilation of some/all parts of the application;
     *          {@link Builder#LINK} if this method call did not compile anything in the application but advise the caller to link again;
     *          {@link Builder#SKIP} if this method call did not compile anything in the application;
     *          {@link Builder#FAIL} if this method call encountered errors during compilation.
     */
    protected int compile(boolean incremental)
    {
        try
        {
        messages.clear();

        // if there is no configuration, use the default... but don't populate this.configuration.
        OEMConfiguration tempOEMConfiguration;

        if (oemConfiguration == null)
        {
            tempOEMConfiguration = (OEMConfiguration) getDefaultConfiguration(true);
        }
        else
        {
            tempOEMConfiguration = OEMUtil.getLibraryConfiguration(constructCommandLine(oemConfiguration),
                                                                   oemConfiguration.keepLinkReport(),
                                                                   oemConfiguration.keepSizeReport(),
                                                                   OEMUtil.getLogger(logger, messages),
                                                                   resolver, mimeMappings);
        }

        // if c is null, which indicates problems, this method will return.
        if (tempOEMConfiguration == null)
        {
            clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            return FAIL;
        }
        else if (oemConfiguration != null && oemConfiguration.keepConfigurationReport())
        {
            configurationReport = OEMUtil.formatConfigurationBuffer(tempOEMConfiguration.cfgbuf);
        }

        if (oemConfiguration != null)
        {
            oemConfiguration.cfgbuf = tempOEMConfiguration.cfgbuf;
        }

        if (tempOEMConfiguration.configuration.benchmark())
        {
            benchmark = CompilerAPI.runBenchmark();
            benchmark.setTimeFilter(tempOEMConfiguration.configuration.getBenchmarkTimeFilter());
            benchmark.startTime(Benchmark.PRECOMPILE);
        }
        else
        {
            CompilerAPI.disableBenchmark();
        }

        // add archive files to the link checksum
        for (Map.Entry<String, VirtualFile>entry : files.entrySet())
        {
            tempOEMConfiguration.cfgbuf.calculateLinkChecksum(entry.getKey(), entry.getValue().getLastModified());
        }           

        // initialize some ThreadLocal variables...
        cc.run();
        OEMUtil.init(OEMUtil.getLogger(logger, messages), mimeMappings, meter, resolver, cc);

        // if there is any problem getting the licenses, this method will return.
        Map licenseMap = OEMUtil.getLicenseMap(tempOEMConfiguration.configuration);

        // if there are no SWC inputs, output an error and return -1
        VirtualFile[] includeLibs = (tempOEMConfiguration.configuration == null) ? null : tempOEMConfiguration.configuration.getCompilerConfiguration().getIncludeLibraries();
        if (sources.size() == 0 && classes.size() == 0 && namespaces.size() == 0 &&
            resourceBundles.size() == 0 && files.size() == 0 && stylesheets.size() == 0 &&
            (includeLibs == null || includeLibs.length == 0))
        {
            ThreadLocalToolkit.log(new ConfigurationException.NoSwcInputs( null, null, -1 ));
            clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            return FAIL;
        }

        // if nothing has been built yet, let's rebuild.
        if (data == null || !incremental)
        {
            String compilationType = (cacheName != null) ? "inactive" : "full";
            if (benchmark != null)
            {
                benchmark.benchmark2("Starting " + compilationType + " compile for " + getOutput(), true);
            }

            int returnValue = recompile(false, licenseMap, tempOEMConfiguration);

            if (benchmark != null)
            {
                benchmark.benchmark2("Ending " + compilationType + " compile for " + getOutput(), true);
            }

            clean(returnValue != OK, false, false);
            return returnValue;
        }

        CompilerAPI.setupHeadless(tempOEMConfiguration.configuration);
        NameMappings mappings = CompilerAPI.getNameMappings(tempOEMConfiguration.configuration), copy = mappings.copy();
       
        // Clear out ASC's userDefined, so definitions from a
        // previous compilation don't spill over into this one.
        data.perCompileData.userDefined.clear();

        data.sourcePath.clearCache();
        data.bundlePath.clearCache();
        data.resources.refresh();

        CompilerConfiguration compilerConfig = tempOEMConfiguration.configuration.getCompilerConfiguration();
        compilerConfig.setMetadataExport(true);

        if (output != null || directory != null)
        {
            OEMUtil.setGeneratedDirectory(compilerConfig, output != null ? output : directory);
        }

        Transcoder[] transcoders = WebTierAPI.getTranscoders(tempOEMConfiguration.configuration);
        SubCompiler[] compilers = WebTierAPI.getCompilers(compilerConfig, mappings, transcoders);
       
        CompilerSwcContext swcContext = new CompilerSwcContext(true);
        try
        {
            swcContext.load( compilerConfig.getLibraryPath(),
                             compilerConfig.getExternalLibraryPath(),
                             null,
                             compilerConfig.getIncludeLibraries(),
                             mappings,
                             I18nUtils.getTranslationFormat(compilerConfig),
                             data.swcCache );
        }
        catch (SwcException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            return FAIL;
        }

        // save the generated swcCache if the class has a libraryCache.
        if (libraryCache != null)
        {
            libraryCache.setSwcCache(data.swcCache);
        }

        // If checksum is different, rebuild.
        if (OEMUtil.isRecompilationNeeded(data, swcContext, tempOEMConfiguration))
        {
            if (benchmark != null)
            {
                benchmark.benchmark2("Starting full compile for " + getOutput(), true);
            }

            clean(true /* cleanData */,
                  false /* cleanCache */,
                  false /* cleanOutput */,
                  true /* cleanConfig */,
                  false /* cleanMessages */,
                  false /* cleanThreadLocals */);
            int returnValue = recompile(true, licenseMap, tempOEMConfiguration);

            if (benchmark != null)
            {
                benchmark.benchmark2("Ending full compile for " + getOutput(), true);
            }

            clean(returnValue != OK, false, false);
            return returnValue;
        }

        // If --include-sources is different, rebuild.
        Set<VirtualFile> fileSet = null;
        if ((fileSet = processSources(compilerConfig)) == null)
        {
            clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            return FAIL;
        }

        // If --include-stylesheets is different, rebuild.
        fileSet.addAll(processStylesheets());

        boolean isFileSpecDifferent = isDifferent(data.fileSet, fileSet);
        if (isFileSpecDifferent)
        {
            if (benchmark != null)
            {
                benchmark.benchmark2("Starting full compile for " + getOutput(), true);
            }

            clean(true /* cleanData */,
                  false /* cleanCache */,
                  false /* cleanOutput */,
                  true /* cleanConfig */,
                  false /* cleanMessages */,
                  false /* cleanThreadLocals */);
            int returnValue = recompile(true, licenseMap, tempOEMConfiguration);
           
            if (benchmark != null)
            {
                benchmark.benchmark2("Ending full compile for " + getOutput(), true);
            }

            clean(returnValue != OK, false, false);
            return returnValue;
        }

        if (benchmark != null)
        {
            // We aren't really starting the compile here, but it's
            // the earliest that we know that it's going to be an
            // active compilation.
            benchmark.benchmark2("Starting active compile for " + getOutput(), true);
        }

        data.includes = new HashSet<String>(swcContext.getIncludes());
        data.excludes = new HashSet<String>(swcContext.getExterns());
        tempOEMConfiguration.configuration.addExterns( swcContext.getExterns() );
        tempOEMConfiguration.configuration.addIncludes( swcContext.getIncludes() );
        data.swcArchiveFiles = new HashMap<String, VirtualFile>(swcContext.getIncludeFiles());

        // The ToolsConfiguration expects class names in QName format.
        Set<String> qNameClasses = new HashSet<String>();
        for (String className : classes)
        {
            qNameClasses.add(NameFormatter.toColon(className));
        }

        // Allow -include-classes to override the -external-library-path.
        tempOEMConfiguration.configuration.removeExterns(qNameClasses);

        // If we want only inheritance dependencies of -include-classes then
        // add the classes to the includes list. When
        // -include-inheritance-dependencies-only is turned on the dependency
        // walker will ignore all the classes except for the includes.
        if (tempOEMConfiguration.configuration.getIncludeInheritanceDependenciesOnly())
        {
            tempOEMConfiguration.configuration.addIncludes(qNameClasses);
        }
       
        int count = CompilerAPI.validateCompilationUnits(data.fileSpec, data.sourceList, data.sourcePath,
                                                         data.bundlePath, data.resources, swcContext,
                                                         data.classes, data.perCompileData,
                                                         tempOEMConfiguration.configuration);

        Map<String, Source> classes = new TreeMap<String, Source>();
        Set<SwcComponent> nsComponents = null;

        if ((nsComponents = processInputs(swcContext, copy, classes)) == null)
        {
            clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            return FAIL;
        }

        // If the other --include-* are different, build incrementally.
        boolean isDifferent = isDifferent(data.classes.keySet(), classes.keySet());
        if (count > 0 || isDifferent || isResourceBundleListDifferent() ||
            data.swcChecksum != swcContext.checksum())
        {
            // create a symbol table
            SymbolTable symbolTable = new SymbolTable(tempOEMConfiguration.configuration, data.perCompileData);
            data.configuration = tempOEMConfiguration.configuration;
            data.nsComponents = nsComponents;
            data.classes = classes;
            data.fileSet = fileSet;
            data.linkChecksum = tempOEMConfiguration.cfgbuf.link_checksum_ts();
            data.swcChecksum = swcContext.checksum();

            // compile
            data.sources = new ArrayList<Source>();
            data.units = compile(compilers, swcContext, symbolTable, mappings, licenseMap, classes, data.sources);

            boolean forcedToStop = CompilerAPI.forcedToStop();
            if (data.units == null || forcedToStop)
            {
                clean(true, false, false);
                return FAIL;
            }
            else
            {
                if (benchmark != null)
                {
                    benchmark.benchmark2("Ending active compile for " + getOutput(), true);
                }
                clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
                return OK;
            }
        }
        else
        {
            if (benchmark != null)
            {
                benchmark.stopTime(Benchmark.PRECOMPILE, false);
                benchmark.startTime(Benchmark.POSTCOMPILE);
            }

            int retVal = SKIP;
            if (data != null)
            {
                CompilerAPI.displayWarnings(data.units);
                if (data.linkChecksum != tempOEMConfiguration.cfgbuf.link_checksum_ts())
                {
                    retVal = LINK;
                }
            }
            else
            {
                retVal = LINK;
            }
            data.linkChecksum = tempOEMConfiguration.cfgbuf.link_checksum_ts();
            data.swcChecksum = swcContext.checksum();

            if (CompilerAPI.forcedToStop()) retVal = FAIL;

            if (benchmark != null)
            {
                benchmark.benchmark2("Ending active compile for " + getOutput(), true);
            }

            if (retVal == LINK)
            {
                clean(false /* cleanData */,
                      false /* cleanCache */,
                      false /* cleanOutput */,
                      false /* cleanConfig */,
                      false /* cleanMessages */,
                      false /* cleanThreadLocals */);
            }
            else
            {
                clean(false /* cleanData */, false /* cleanCache */, false /* cleanOutput */);
            }

            return retVal;
        }
    }
        finally
        {
            // clean thread locals
            OEMUtil.clean();
        }
    }

    /**
     *
     * @param compilers
     * @param swcContext
     * @param symbolTable
     * @param licenseMap
     * @param classes
     */
    private List<CompilationUnit> compile(SubCompiler[] compilers, CompilerSwcContext swcContext,
                                          SymbolTable symbolTable, NameMappings nameMappings, Map licenseMap,
                                          Map<String, Source> classes, List<Source> sources)
    {
        List<CompilationUnit> units = null;
        Map<String, VirtualFile> rbFiles = new HashMap<String, VirtualFile>();

        try
        {
            if (benchmark != null)
            {
                for (int i = 0; i < compilers.length; i++)
                {
                    compilers[i].initBenchmarks();
                }

                benchmark.stopTime(Benchmark.PRECOMPILE, false);
            }

            units = CompilerAPI.compile(data.fileSpec, data.sourceList, classes.values(), data.sourcePath, data.resources,
                                        data.bundlePath, swcContext, symbolTable, nameMappings, data.configuration,
                                        compilers, new CompcPreLink(rbFiles, new ArrayList<String>(resourceBundles), false),
                                        licenseMap, sources);

            if (benchmark != null)
            {
                benchmark.startTime(Benchmark.POSTCOMPILE);
            }

            if ((benchmark != null) && (ThreadLocalToolkit.getLogger() != null))
            {
                if (compilerBenchmarks == null)
                    compilerBenchmarks = new HashMap<String, PerformanceData[]>();

                compilerBenchmarks.clear();

                flex2.compiler.Logger logger = ThreadLocalToolkit.getLogger();
                for (int i = 0; i < compilers.length; i++)
                {
                    SubCompiler compiler = compilers[i];
                    PerformanceData[] times = compiler.getBenchmarks();

                    if (times == null)
                        continue;

                    compiler.logBenchmarks(logger);
                    String compilerName = compiler.getName();
                    compilerBenchmarks.put(compilerName, times);
                }
            }
        }
        catch (CompilerException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            assert ThreadLocalToolkit.errorCount() > 0;
        }
        catch (SwcException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            assert ThreadLocalToolkit.errorCount() > 0;
        }
        catch (Throwable t)
        {
            if (Trace.error)
            {
                t.printStackTrace();
            }
            ThreadLocalToolkit.logError(t.getLocalizedMessage());
        }
        finally
        {
            data.sourcePath.clearCache();
            data.bundlePath.clearCache();
            data.resources.refresh();
            data.classes = classes;
            data.rbFiles = rbFiles;

            Map<String, VirtualFile> m = data.configuration.getCSSArchiveFiles();
            if (m != null)
            {
                data.cssArchiveFiles = new HashMap<String, VirtualFile>(m);
            }

            m = data.configuration.getL10NArchiveFiles();
            if (m != null)
            {
                data.l10nArchiveFiles = new HashMap<String, VirtualFile>(m);
            }

            OEMUtil.saveSignatureChecksums(units, data, data.configuration);
            OEMUtil.saveSwcFileChecksums(swcContext, data, data.configuration);
            // Make sure the swcContext is closed so we don't leave any dangling file handles
            swcContext.close();
        }

        return units;
    }

    /**
     * Links the <code>Library</code>. This method writes the output
     * to the output stream specified by the client. You should use a
     * buffered output stream for best performance.
     *
     * <p> This method is protected. In most circumstances, the client
     * only needs to call the <code>build()</code> method. Subclasses
     * can call this method so that it links and outputs the
     * application without recompiling.  If the OutputStream is
     * subsequently written to a file, subclasses should call
     * refreshLastModified().
     *
     * @param out The <code>OutputStream</code>.
     * @return The size of the application, in bytes.
     * @throws IOException Thrown when an I/O error occurs during linking.
     */
    protected long link(OutputStream out) throws IOException
    {
        if (data == null || data.units == null)
        {
            return 0;
        }

        boolean hasChanged = (oemConfiguration == null) ? false : oemConfiguration.hasChanged();
        flex2.compiler.common.Configuration config = null;

        if (hasChanged)
        {
            oemConfiguration = OEMUtil.getLinkerConfiguration(oemConfiguration.getLinkerOptions(),
                                                              oemConfiguration.keepLinkReport(),
                                                              oemConfiguration.keepSizeReport(),
                                                              OEMUtil.getLogger(logger, messages),
                                                              mimeMappings, resolver,
                                                              data.configuration,
                                                              oemConfiguration.newLinkerOptionsAfterCompile,
                                                              data.includes, data.excludes);
            if (oemConfiguration == null)
            {
                clean(false /* cleanData */,
                      false /* cleanCache */,
                      false /* cleanOutput */,
                      false /* cleanConfig */,
                      false /* cleanMessages */,
                      true /* cleanThreadLocals */);
                return 0;
            }

            config = oemConfiguration.configuration;
        }
        else
        {
            config = data.configuration;
        }

        if (config.benchmark())
        {
            benchmark = CompilerAPI.runBenchmark();
            benchmark.setTimeFilter(config.getBenchmarkTimeFilter());
        }
        else
        {
            CompilerAPI.disableBenchmark();
        }

        try
        {
            OEMUtil.init(OEMUtil.getLogger(logger, messages), mimeMappings, meter, resolver, cc);

            SimpleMovie temp = data.movie;
            data.movie = SwcAPI.link(config, data.units);

            // link
            SwcArchive archive = null;
            Map<String, VirtualFile> archiveFiles = new TreeMap<String, VirtualFile>();
            if (data.swcArchiveFiles != null) archiveFiles.putAll(data.swcArchiveFiles);
            if (data.cssArchiveFiles != null) archiveFiles.putAll(data.cssArchiveFiles);
            if (data.l10nArchiveFiles != null) archiveFiles.putAll(data.l10nArchiveFiles);
            archiveFiles.putAll(files);

            if (directory != null)
            {
                archive = new SwcDirectoryArchive(FileUtil.getCanonicalPath(directory));
                SwcAPI.exportSwc(archive,
                                                 archiveFiles,
                                                 this.stylesheets,
                                                 (LinkerConfiguration) config,
                                                 (SwcMovie) data.movie,
                                                 new ArrayList<SwcComponent>(data.nsComponents),
                                                 data.swcCache,
                                                 data.rbFiles);
            }

            long size = 0;

            // TODO PERFORMANCE: A lot of unnecessary recopying and buffering here
            ByteArrayOutputStream baos = null;
            String path = null;

            if (output != null)
            {
                path = FileUtil.getCanonicalPath(output);
            }

            // Flex Builder supplies an "out" and an "output", but
            // they really only use the "out", so check for that
            // first.
            if (out != null)
            {
                baos = new ByteArrayOutputStream();
                archive = new SwcLazyReadArchive(baos, path);
            }
            else if (output != null)
            {
                archive = new SwcLazyReadArchive(path);
            }

            SwcAPI.exportSwc(archive,
                             archiveFiles,
                             this.stylesheets,
                             (LinkerConfiguration) config,
                             (SwcMovie) data.movie,
                             new ArrayList<SwcComponent>(data.nsComponents),
                             data.swcCache,
                             data.rbFiles);
           
            if (out != null)
            {
                ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());
                FileUtil.streamOutput(in, out);
                size = baos.size();
            }
            else if (output != null)
            {
                size = output.length();
            }

            if (hasChanged && temp != null)
            {
                data.movie = temp;
            }

            return size;
        }
        catch (LinkerException ex)
        {
            assert ThreadLocalToolkit.errorCount() > 0;
            return 0;
        }
        catch (SwcException ex)
        {
            if (Trace.error)
            {
                ex.printStackTrace();
            }
            assert ThreadLocalToolkit.errorCount() > 0;
            return 0;
        }
        catch (Throwable t)
        {
            if (Trace.error)
            {
                t.printStackTrace();
            }
            ThreadLocalToolkit.logError(t.getLocalizedMessage());
            return 0;
        }
        finally
        {
            // clean thread locals
            OEMUtil.clean();           
    }
    }

    /**
     *
     * @param cleanData
     * @param cleanCache
     * @param cleanOutput
     */
    private void clean(boolean cleanData, boolean cleanCache, boolean cleanOutput)
    {
        clean(cleanData,
              cleanCache,
              cleanOutput,
              true /* cleanConfig */,
              false /* cleanMessages */,
              false /* cleanThreadLocals */);
    }

    /**
     *
     * @param cleanData
     * @param cleanCache
     * @param cleanOutput
     * @param cleanConfig
     * @param cleanMessages
     * @param cleanThreadLocals
     */
    private void clean(boolean cleanData, boolean cleanCache, boolean cleanOutput,
                       boolean cleanConfig, boolean cleanMessages, boolean cleanThreadLocals)
    {
        if (cleanThreadLocals)
        {
            OEMUtil.clean();
        }
       
        if (oemConfiguration != null && cleanConfig)
        {
            oemConfiguration.reset();
        }

        if (cleanData)
        {
            data = null;
            configurationReport = null;
        }

        if (cleanCache)
        {
            if (cacheName != null)
            {
                File dead = FileUtil.openFile(cacheName);
                if (dead != null && dead.exists())
                {
                    dead.delete();
                }
                cacheName = null;
            }
        }

        if (cleanOutput)
        {
            if (output != null && output.exists())
            {
                output.delete();
            }
        }

        if (cleanMessages)
        {
            messages.clear();
        }
    }

    /**
     *
     * @param s1
     * @param s2
     * @return
     */
    private <T> boolean isDifferent(Collection<T> s1, Collection<T> s2)
    {
        for (Iterator<T> i = s2.iterator(); i.hasNext(); )
        {
            if (!s1.contains(i.next()))
            {
                return true;
            }
        }

        return s1.size() > s2.size();
    }

    /**
     *
     * @return
     */
    private boolean isResourceBundleListDifferent()
    {
        int size1 = (data == null || data.rbFiles == null) ? 0 : data.rbFiles.size();
        int size2 = resourceBundles == null ? 0 : resourceBundles.size();
        return size1 != size2;
    }

    /**
     * Returns the cache of sources in the source list and source
     * path.  After building this Library object, the cache may be
     * saved and used to compile another Application or Library object
     * with common sources.
     *
     * @return The active cache. May be null.
     *
     * @since 4.5
     */
    public ApplicationCache getApplicationCache()
    {
        return applicationCache;
    }

    /**
     * Sets the cache for sources in the source list and source path.
     * After compiling this Library object, the cache may be reused to
     * build another Application or Library object with common
     * sources.
     *
     * @param applicationCache A reference to the application cache.
     *
     * @since 4.5
     */
    public void setApplicationCache(ApplicationCache applicationCache)
    {
        this.applicationCache = applicationCache;
    }

    // TODO: deprecate getSwcCache() and setSwcCache(), then add
    // getLibraryCache() and setLibraryCache().
    /**
     * Get the cache of swcs in the library path. After building this
     * Library object, the cache may be saved and used to compile
     * another Library or Application object that uses the same
     * library path.
     *
     * @return The active cache. May be null.
     *
     * @since 3.0
     */
    public LibraryCache getSwcCache()
    {
        return libraryCache;
    }

    /**
     * Set the cache for swcs in the library path. After compiling an
     * Library object, the cache may be reused to build another
     * Library or Application object that uses the same library path.
     *
     * @param swcCache A reference to an allocated swc cache.
     *
     * @since 3.0
     */
    public void setSwcCache(LibraryCache libraryCache)
    {
        this.libraryCache = libraryCache;
    }

    public void refreshLastModified()
    {
        String fileName = FileUtil.getCanonicalPath(output);
        File file = new File(fileName);
        long lastModified = file.lastModified();
        data.swcCache.setLastModified(fileName, lastModified);
    }
   
    @Override
    public Library clone()
    {
        Library clone;
        try
        {
            clone = (Library) super.clone();
        }
        catch ( CloneNotSupportedException e )
        {
            throw new RuntimeException( e ); //wont happen
        }
        clone.oemConfiguration = oemConfiguration.clone();
        return clone;
    }
}
TOP

Related Classes of flex2.tools.oem.Library

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.