flex2.tools.oem.Application Maven / Gradle / Ivy
/*
*
* 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.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.royale.compiler.clients.MXMLJSC;
import org.apache.royale.compiler.clients.problems.ProblemFormatter;
import org.apache.royale.compiler.clients.problems.ProblemQuery;
import org.apache.royale.compiler.problems.CompilerProblemSeverity;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.annotations.DefaultSeverity;
import org.apache.royale.swf.ISWF;
import org.apache.royale.swf.types.RGB;
import flash.swf.tags.SetBackgroundColor;
import flex2.compiler.CompilerException;
import flex2.compiler.Source;
import flex2.compiler.SourceList;
import flex2.compiler.io.FileUtil;
import flex2.compiler.io.LocalFile;
import flex2.compiler.io.VirtualFile;
import flex2.compiler.util.Benchmark;
import flex2.compiler.util.CompilerControl;
import flex2.compiler.util.CompilerMessage;
import flex2.compiler.util.MimeMappings;
import flex2.compiler.util.PerformanceData;
import flex2.compiler.util.ThreadLocalToolkit;
import flex2.linker.SimpleMovie;
import flex2.tools.ToolsConfiguration;
import flex2.tools.oem.internal.ApplicationCompilerConfiguration;
import flex2.tools.oem.internal.OEMConfiguration;
import flex2.tools.oem.internal.OEMReport;
import flex2.tools.oem.internal.OEMUtil;
/**
* The Application
class represents a Flex application. It implements the Builder
interface
* which allows for building the application incrementally. There are many ways to define
* a Flex application. The most common way is specify the location of the target source file
* on disk:
*
*
* Application app = new Application(new File("MyApp.mxml"));
*
*
* Before the Application
object starts the compilation, it must be configured. The most common methods that the client
* calls are setLogger()
, setConfiguration()
, and setOutput()
.
*
* A logger must implement flex2.tools.oem.Logger
and use the implementation as the Logger
* for the compilation. The following is an example Logger
implementation:
*
*
* app.setLogger(new flex2.tools.oem.Logger()
* {
* public void log(Message message, int errorCode, String source)
* {
* System.out.println(message);
* }
* });
*
*
* To specify compiler options for the Application
object, the client
* must get a Configuration
object populated with default values. Then, the client can set
* compiler options programmatically.
*
* The setOutput()
method lets clients specify where the Application
object should write
* the output to. If you call the setOutput()
method, the build(boolean)
method builds and
* writes directly to the location specified by the setOutput()
method. For example:
*
*
* app.setOutput(new File("MyApp.swf"));
* app.build(true);
*
*
* If you do not call the setOutput()
method, the client can use the build(OutputStream, boolean)
method
* which requires the client to provide a buffered output stream. For example:
*
*
* app.build(new BufferedOutputStream(new FileOutputStream("MyApp.swf")), true);
*
*
* Before the Application
object is thrown away, it is possible to save the compilation
* data for reuse by using the save(OutputStream)
method. Subsequent compilations can use the
* load(OutputStream)
method to get the old data into the Application
object.
*
*
* app.save(new BufferedOutputStream(new FileOutputStream("MyApp.incr")));
*
*
* When a cache file (such as MyApp.incr
) is available from a previous compilation, the client can
* call the load(OutputStream)
method before calling the build(boolean)
method. For example:
*
*
* app.load(new BufferedInputStream(FileInputStream("MyApp.incr")));
* app.build();
*
*
* The build(false)
and build(OutputStream, false)
methods always rebuild the application. If the Application
* object is new, the first build(true)/build(OutputStream, true)
method call performs a full build, which
* is equivalent to build(false)/build(OutputStream, false)
, respectively. After a call to the clean()
method,
* the Application
object always performs a full build.
*
*
* The clean()
method not only cleans up compilation data in the Application
object, but also the output
* file if the setOutput()
method was called.
*
*
* The Application
class also supports building applications from a combination of source
* files from the file system and in-memory, dynamically-generated source objects. The client
* must use the Application(String, VirtualLocalFile)
or Application(String, VirtualLocalFile[])
constructors.
*
*
* The Application
class can be part of a Project
. For more information, see the Project
class's description.
*
* @see flex2.tools.oem.Configuration
* @see flex2.tools.oem.Project
* @version 2.0.1
* @author Clement Wong
*/
public class Application implements Builder
{
static
{
// find all the compiler temp files.
File[] list = null;
try
{
File tempDir = File.createTempFile("Flex2_", "").getParentFile();
list = tempDir.listFiles(new FilenameFilter()
{
public boolean accept(File dir, String name)
{
return name.startsWith("Flex2_");
}
});
}
catch (Exception e)
{
}
// get rid of compiler temp files.
for (int i = 0, len = list == null ? 0 : list.length; i < len; i++)
{
try { list[i].delete(); } catch (Exception t) {}
}
// use the protection domain to find the location of flex-compiler-oem.jar.
URL url = Application.class.getProtectionDomain().getCodeSource().getLocation();
try
{
File f = new File(new URI(url.toExternalForm()));
if (f.getAbsolutePath().endsWith("flex-compiler-oem.jar"))
{
// use the location of flex-compiler-oem.jar to set application.home
// assume that the jar file is in /lib/flex-compiler-oem.jar
String applicationHome = f.getParentFile().getParent();
System.setProperty("application.home", applicationHome);
}
}
catch (URISyntaxException ex)
{
}
catch (IllegalArgumentException ex)
{
}
}
/**
* Constructor.
*
* @param file The target source file.
* @throws FileNotFoundException Thrown when the specified source file does not exist.
*/
public Application(File file) throws FileNotFoundException
{
this(file, null);
}
/**
* Constructor.
*
* @param file The target source file.
* @param libraryCache A reference to a LibraryCache object. After
* building this Application object the cache may be saved
* and used to compile another Application object that uses
* a similar library path.
* @throws FileNotFoundException Thrown when the specified source file does not exist.
* @since 3.0
*/
public Application(File file, LibraryCache libraryCache) throws FileNotFoundException
{
if (file.exists())
{
init(new VirtualFile[] { new LocalFile(FileUtil.getCanonicalFile(file)) });
}
else
{
throw new FileNotFoundException(FileUtil.getCanonicalPath(file));
}
this.libraryCache = libraryCache;
}
/**
* Constructor.
*
* @param file An in-memory source object.
*/
public Application(VirtualLocalFile file)
{
init(new VirtualFile[] { file });
}
/**
* Constructor.
*
* @param files An array of in-memory source objects. The last element in the array is the target source object.
*/
public Application(VirtualLocalFile[] files)
{
init(files);
}
/**
* Constructor. Use to build resource modules which don't have a target
* source file.
*
*/
public Application()
{
init(new VirtualFile[0]);
}
/**
*
* @param files
*/
@SuppressWarnings("unchecked")
private void init(VirtualFile[] files)
{
this.files = new ArrayList(files.length);
for (int i = 0, length = files.length; i < length; i++)
{
this.files.add(files[i]);
}
oemConfiguration = null;
logger = null;
output = null;
mimeMappings = new MimeMappings();
meter = null;
resolver = null;
cc = new CompilerControl();
//isGeneratedTargetFile = false;
//data = null;
cacheName = null;
configurationReport = null;
messages = new ArrayList();
}
private List files;
private OEMConfiguration oemConfiguration;
private Logger logger;
private File output;
private MimeMappings mimeMappings;
private ProgressMeter meter;
protected PathResolver resolver;
private CompilerControl cc;
//private boolean isGeneratedTargetFile;
private ApplicationCache applicationCache;
private LibraryCache libraryCache;
// clean() would null out the following variables.
private String cacheName, configurationReport;
private List messages;
private boolean setOutputCalled;
private int loaded = 0;
/**
* @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.getApplicationConfiguration(constructCommandLine(null), false, false,
OEMUtil.getLogger(logger, messages), resolver,
mimeMappings, processDefaults);
}
/**
* @inheritDoc
*/
public Map getCompilerBenchmarks()
{
return null;
}
/**
* @inheritDoc
*/
public Benchmark getBenchmark()
{
return null;
}
/**
* @inheritDoc
*/
public Configuration getConfiguration()
{
return oemConfiguration;
}
/**
* @inheritDoc
*/
public void setLogger(Logger logger)
{
this.logger = logger;
}
/**
* @inheritDoc
*/
public Logger getLogger()
{
return logger;
}
/**
* Sets the location of the compiler's output. This method is necessary if you call the build(boolean)
method.
* If you use the build(OutputStream, boolean)
method, in which an output stream
* is provided, there is no need to use this method.
*
* @param output An instance of the java.io.File
class.
*/
public void setOutput(File output)
{
setOutputCalled = true;
this.output = output;
}
/**
* Gets the output destination. This method returns null
if the setOutput()
method was not called.
*
* @return An instance of the java.io.File
class, or null
if you did not call the setOutput()
method.
*/
public File getOutput()
{
return output;
}
/**
* @inheritDoc
*/
public void setSupportedFileExtensions(String mimeType, String[] extensions)
{
mimeMappings.set(mimeType, extensions);
}
/**
* @inheritDoc
*/
public void setProgressMeter(ProgressMeter meter)
{
this.meter = meter;
}
/**
* @inheritDoc
* @since 3.0
*/
public void setPathResolver(PathResolver resolver)
{
this.resolver = resolver;
}
/**
* @inheritDoc
*/
// IMPORTANT: If you make changes here, you probably want to mirror them in Library.build()
public long build(boolean incremental) throws IOException
{
if (output != null)
{
InputStream tempIn = null;
ByteArrayOutputStream tempOut = null;
OutputStream out = null;
long size = 0;
//TODO PERFORMANCE: A lot of unnecessary recopying and buffering here
try
{
@SuppressWarnings("unused")
int result = compile(incremental);
return size;
}
catch (Exception e)
{
ThreadLocalToolkit.logError(e.getLocalizedMessage());
return 0;
}
finally
{
if (tempIn != null) { try { tempIn.close(); } catch (Exception ex) {} }
if (tempOut != null) { try { tempOut.close(); } catch (Exception ex) {} }
if (out != null) { try { out.close(); } catch (Exception ex) {} }
//runExtensions();
clean(false /* cleanData */,
false /* cleanCache */,
false /* cleanOutput */,
true /* cleanConfig */,
false /* cleanMessages */,
true /* cleanThreadLocals */);
}
}
else
{
return 0;
}
}
/*
private void runExtensions()
{
if (oemConfiguration != null)
{
Set extensions = ExtensionManager.getApplicationExtensions(oemConfiguration.getExtensions());
for ( IApplicationExtension extension : extensions )
{
if (ThreadLocalToolkit.errorCount() == 0)
{
extension.run( (Configuration) oemConfiguration.clone() );
}
}
}
}
*/
/**
* @inheritDoc
*/
public long build(OutputStream out, boolean incremental) throws IOException
{
try
{
@SuppressWarnings("unused")
int result = compile(incremental);
/*
if (result == OK || result == LINK)
{
runExtensions();
return link(out);
}
else if (result == SKIP)
{
runExtensions();
return encode(out);
}
else
{
*/
return 0;
// }
}
finally
{
clean(false /* cleanData */,
false /* cleanCache */,
false /* cleanOutput */,
true /* cleanConfig */,
false /* cleanMessages */,
true /* cleanThreadLocals */);
}
}
/**
* @inheritDoc
*/
public void stop()
{
cc.stop();
}
/**
* @inheritDoc
*/
public void clean()
{
// assuming FB takes care of deleting bin-release and bin-debug, we want to delete bin.
// but this also gets called when quitting FB.
setOutputCalled = false;
}
/**
* @inheritDoc
*/
public void load(InputStream in) throws IOException
{
loaded++;
}
/**
* @inheritDoc
*/
public long save(OutputStream out) throws IOException
{
loaded--;
return 1;
}
/**
* @inheritDoc
*/
public Report getReport()
{
//OEMUtil.setupLocalizationManager();
return new OEMReport( sources,
movie,
null,
sourceList,
configurationReport,
messages);
}
/**
* Compiles the Application
object. This method does not link the Application
.
*
* @param incremental If true
, build incrementally; if false
, 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.getApplicationConfiguration(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 */,
true /* cleanConfig */,
false /* cleanMessages */,
false /* cleanThreadLocals */);
return FAIL;
}
else if (oemConfiguration != null && oemConfiguration.keepConfigurationReport())
{
configurationReport = OEMUtil.formatConfigurationBuffer(tempOEMConfiguration.cfgbuf);
}
if (oemConfiguration != null)
{
oemConfiguration.cfgbuf = tempOEMConfiguration.cfgbuf;
}
// initialize some ThreadLocal variables...
cc.run();
OEMUtil.init(OEMUtil.getLogger(logger, messages), mimeMappings, meter, resolver, cc);
//Map licenseMap = OEMUtil.getLicenseMap(tempOEMConfiguration.configuration);
mxmljsc = new MXMLJSC();
mxmljsc.noLink = true;
//int returnValue = mxmlc.mainCompileOnly(constructCommandLine2(tempOEMConfiguration.configuration), null);
int returnValue = mxmljsc.mainNoExit(constructCommandLine(oemConfiguration), null, true);
if (returnValue == 0 || returnValue == 2)
returnValue = OK;
else
returnValue = FAIL;
processMXMLCReport(mxmljsc, tempOEMConfiguration);
clean(returnValue == FAIL /* cleanData */,
false /* cleanCache */,
false /* cleanOutput */,
true /* cleanConfig */,
false /* cleanMessages */,
false /* cleanThreadLocals */);
return returnValue;
}
finally
{
// clean thread locals
OEMUtil.clean();
}
}
public long link(OutputStream output)
{
return mxmljsc.writeSWF(output);
}
private MXMLJSC mxmljsc = new MXMLJSC();
private List