Java tutorial
/* * * 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.clients; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.apache.flex.compiler.clients.problems.ProblemPrinter; import org.apache.flex.compiler.clients.problems.ProblemQuery; import org.apache.flex.compiler.clients.problems.WorkspaceProblemFormatter; import org.apache.flex.compiler.codegen.as.IASWriter; import org.apache.flex.compiler.codegen.js.IJSPublisher; import org.apache.flex.compiler.common.VersionInfo; import org.apache.flex.compiler.config.Configuration; import org.apache.flex.compiler.config.ConfigurationBuffer; import org.apache.flex.compiler.config.Configurator; import org.apache.flex.compiler.config.ICompilerSettingsConstants; import org.apache.flex.compiler.driver.IBackend; import org.apache.flex.compiler.driver.js.IJSApplication; import org.apache.flex.compiler.exceptions.ConfigurationException; import org.apache.flex.compiler.exceptions.ConfigurationException.IOError; import org.apache.flex.compiler.exceptions.ConfigurationException.MustSpecifyTarget; import org.apache.flex.compiler.exceptions.ConfigurationException.OnlyOneSource; import org.apache.flex.compiler.internal.codegen.js.JSPublisher; import org.apache.flex.compiler.internal.codegen.js.JSSharedData; import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogPublisher; import org.apache.flex.compiler.internal.driver.as.ASBackend; import org.apache.flex.compiler.internal.driver.js.JSBackend; import org.apache.flex.compiler.internal.driver.js.amd.AMDBackend; import org.apache.flex.compiler.internal.driver.js.goog.GoogBackend; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.projects.ISourceFileHandler; import org.apache.flex.compiler.internal.targets.JSTarget; import org.apache.flex.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.flex.compiler.internal.units.SourceCompilationUnitFactory; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.problems.ConfigurationProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InternalCompilerProblem; import org.apache.flex.compiler.problems.UnableToBuildSWFProblem; import org.apache.flex.compiler.problems.UnexpectedExceptionProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.targets.ITarget; import org.apache.flex.compiler.targets.ITargetSettings; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.utils.FileUtils; import org.apache.flex.utils.FilenameNormalization; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** * @author Michael Schmalle */ public class MXMLJSC { /* * JS output type enumerations. */ public enum JSOutputType { FLEXJS("flexjs"), GOOG("goog"), AMD("amd"); private String text; JSOutputType(String text) { this.text = text; } public String getText() { return this.text; } public static JSOutputType fromString(String text) { for (JSOutputType jsOutputType : JSOutputType.values()) { if (text.equalsIgnoreCase(jsOutputType.text)) return jsOutputType; } return GOOG; } } /* * Exit code enumerations. */ static enum ExitCode { SUCCESS(0), PRINT_HELP(1), FAILED_WITH_PROBLEMS(2), FAILED_WITH_EXCEPTIONS(3), FAILED_WITH_CONFIG_PROBLEMS( 4); ExitCode(int code) { this.code = code; } final int code; } /** * Java program entry point. * * @param args command line arguments */ public static void main(final String[] args) { long startTime = System.nanoTime(); IBackend backend = new ASBackend(); for (String s : args) { if (s.contains("-js-output-type")) { JSOutputType outputType = JSOutputType.fromString(s.split("=")[1]); switch (outputType) { case AMD: backend = new AMDBackend(); break; case FLEXJS: backend = new JSBackend(); break; case GOOG: backend = new GoogBackend(); break; } break; } } final MXMLJSC mxmlc = new MXMLJSC(backend); final Set<ICompilerProblem> problems = new HashSet<ICompilerProblem>(); final int exitCode = mxmlc.mainNoExit(args, problems, true); long endTime = System.nanoTime(); JSSharedData.instance.stdout((endTime - startTime) / 1e9 + " seconds"); System.exit(exitCode); } private Workspace workspace; private FlexProject project; private ProblemQuery problems; private ISourceFileHandler asFileHandler; private Configuration config; private Configurator projectConfigurator; private ConfigurationBuffer configBuffer; private ICompilationUnit mainCU; private ITarget target; private ITargetSettings targetSettings; private IJSApplication jsTarget; private JSOutputType jsOutputType; private IJSPublisher jsPublisher; protected MXMLJSC(IBackend backend) { JSSharedData.backend = backend; workspace = new Workspace(); project = new FlexProject(workspace); problems = new ProblemQuery(); JSSharedData.OUTPUT_EXTENSION = backend.getOutputExtension(); JSSharedData.workspace = workspace; asFileHandler = backend.getSourceFileHandlerInstance(); } public int mainNoExit(final String[] args, Set<ICompilerProblem> problems, Boolean printProblems) { int exitCode = -1; try { exitCode = _mainNoExit(fixArgs(args), problems); } catch (Exception e) { JSSharedData.instance.stderr(e.toString()); } finally { if (problems != null && !problems.isEmpty()) { if (printProblems) { final WorkspaceProblemFormatter formatter = new WorkspaceProblemFormatter(workspace); final ProblemPrinter printer = new ProblemPrinter(formatter); printer.printProblems(problems); } } } return exitCode; } /** * Entry point that doesn't call <code>System.exit()</code>. This is for * unit testing. * * @param args command line arguments * @return exit code */ private int _mainNoExit(final String[] args, Set<ICompilerProblem> outProblems) { ExitCode exitCode = ExitCode.SUCCESS; try { final boolean continueCompilation = configure(args); if (outProblems != null && !config.isVerbose()) JSSharedData.STDOUT = JSSharedData.STDERR = null; if (continueCompilation) { compile(); if (problems.hasFilteredProblems()) exitCode = ExitCode.FAILED_WITH_PROBLEMS; } else if (problems.hasFilteredProblems()) { exitCode = ExitCode.FAILED_WITH_CONFIG_PROBLEMS; } else { exitCode = ExitCode.PRINT_HELP; } } catch (Exception e) { if (outProblems == null) JSSharedData.instance.stderr(e.getMessage()); else { final ICompilerProblem unexpectedExceptionProblem = new UnexpectedExceptionProblem(e); problems.add(unexpectedExceptionProblem); } exitCode = ExitCode.FAILED_WITH_EXCEPTIONS; } finally { waitAndClose(); if (outProblems != null && problems.hasFilteredProblems()) { for (ICompilerProblem problem : problems.getFilteredProblems()) { outProblems.add(problem); } } } return exitCode.code; } /** * Main body of this program. This method is called from the public static * method's for this program. * * @return true if compiler succeeds * @throws IOException * @throws InterruptedException */ protected boolean compile() { boolean compilationSuccess = false; try { setupJS(); if (!setupTargetFile()) return false; //if (config.isDumpAst()) // dumpAST(); buildArtifact(); if (jsTarget != null) { jsOutputType = JSOutputType.fromString(((JSConfiguration) config).getJSOutputType()); Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); // Don't create a swf if there are errors unless a // developer requested otherwise. if (!config.getCreateTargetWithErrors()) { problems.getErrorsAndWarnings(errors, warnings); if (errors.size() > 0) return false; } File outputFolder = null; // output type specific pre-compile actions switch (jsOutputType) { case AMD: { // break; } case FLEXJS: { // break; } case GOOG: { jsPublisher = new JSGoogPublisher(config); outputFolder = jsPublisher.getOutputFolder(); if (outputFolder.exists()) org.apache.commons.io.FileUtils.deleteQuietly(outputFolder); break; } default: { jsPublisher = new JSPublisher(config); outputFolder = new File(getOutputFilePath()).getParentFile(); } } List<ICompilationUnit> reachableCompilationUnits = project .getReachableCompilationUnitsInSWFOrder(ImmutableSet.of(mainCU)); for (final ICompilationUnit cu : reachableCompilationUnits) { if (cu.getCompilationUnitType() == ICompilationUnit.UnitType.AS_UNIT) { final File outputClassFile = getOutputClassFile(cu.getQualifiedNames().get(0), outputFolder); System.out.println("Compiling file: " + outputClassFile); ICompilationUnit unit = cu; IASWriter jswriter = JSSharedData.backend.createWriter(project, (List<ICompilerProblem>) errors, unit, false); // XXX (mschmalle) hack what is CountingOutputStream? BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outputClassFile)); jswriter.writeTo(out); out.flush(); out.close(); jswriter.close(); } } // output type specific post-compile actions switch (jsOutputType) { case AMD: { // break; } case FLEXJS: { // break; } case GOOG: { // break; } } jsPublisher.publish(); compilationSuccess = true; } } catch (Exception e) { final ICompilerProblem problem = new InternalCompilerProblem(e); problems.add(problem); } return compilationSuccess; } /** * Build target artifact. * * @throws InterruptedException threading error * @throws IOException IO error * @throws ConfigurationException */ protected void buildArtifact() throws InterruptedException, IOException, ConfigurationException { jsTarget = buildJSTarget(); } private IJSApplication buildJSTarget() throws InterruptedException, FileNotFoundException, ConfigurationException { final List<ICompilerProblem> problemsBuildingSWF = new ArrayList<ICompilerProblem>(); final IJSApplication app = buildApplication(project, config.getMainDefinition(), mainCU, problemsBuildingSWF); problems.addAll(problemsBuildingSWF); if (app == null) { ICompilerProblem problem = new UnableToBuildSWFProblem(getOutputFilePath()); problems.add(problem); } //reportRequiredRSLs(target); return app; } /** * Replaces FlexApplicationProject::buildSWF() * * @param applicationProject * @param rootClassName * @param problems * @return * @throws InterruptedException */ private IJSApplication buildApplication(CompilerProject applicationProject, String rootClassName, ICompilationUnit mainCU, Collection<ICompilerProblem> problems) throws InterruptedException, ConfigurationException, FileNotFoundException { Collection<ICompilerProblem> fatalProblems = applicationProject.getFatalProblems(); if (!fatalProblems.isEmpty()) { problems.addAll(fatalProblems); return null; } return ((JSTarget) target).build(mainCU, problems); } /** * Get the output file path. If {@code -output} is specified, use its value; * otherwise, use the same base name as the target file. * * @return output file path */ private String getOutputFilePath() { if (config.getOutput() == null) { final String extension = "." + JSSharedData.OUTPUT_EXTENSION; return FilenameUtils.removeExtension(config.getTargetFile()).concat(extension); } else return config.getOutput(); } /** * @author Erik de Bruin * * Get the output class file. This includes the (sub)directory in which the * original class file lives. If the directory structure doesn't exist, it * is created. * * @param qname * @param outputFolder * @return output class file path */ private File getOutputClassFile(String qname, File outputFolder) { String[] cname = qname.split("\\."); String sdirPath = outputFolder + File.separator; if (cname.length > 0) { for (int i = 0, n = cname.length - 1; i < n; i++) { sdirPath += cname[i] + File.separator; } File sdir = new File(sdirPath); if (!sdir.exists()) sdir.mkdirs(); qname = cname[cname.length - 1]; } return new File(sdirPath + qname + "." + JSSharedData.OUTPUT_EXTENSION); } /** * Mxmlc uses target file as the main compilation unit and derive the output * SWF file name from this file. * * @return true if successful, false otherwise. * @throws OnlyOneSource * @throws InterruptedException */ protected boolean setupTargetFile() throws InterruptedException { final String mainFileName = config.getTargetFile(); final String normalizedMainFileName = FilenameNormalization.normalize(mainFileName); final SourceCompilationUnitFactory compilationUnitFactory = project.getSourceCompilationUnitFactory(); File normalizedMainFile = new File(normalizedMainFileName); if (compilationUnitFactory.canCreateCompilationUnit(normalizedMainFile)) { // adds the source path to the sourceListManager project.addIncludeSourceFile(normalizedMainFile); // just using the basename is obviously wrong: // final String mainQName = FilenameUtils.getBaseName(normalizedMainFile); final List<String> sourcePath = config.getCompilerSourcePath(); String mainQName = null; if (sourcePath != null && !sourcePath.isEmpty()) { for (String path : sourcePath) { final String otherPath = new File(path).getAbsolutePath(); if (mainFileName.startsWith(otherPath)) { mainQName = mainFileName.substring(otherPath.length() + 1); mainQName = mainQName.replaceAll("\\\\", "/"); mainQName = mainQName.replaceAll("\\/", "."); if (mainQName.endsWith(".as")) mainQName = mainQName.substring(0, mainQName.length() - 3); break; } } } if (mainQName == null) mainQName = FilenameUtils.getBaseName(mainFileName); Collection<ICompilationUnit> mainFileCompilationUnits = workspace .getCompilationUnits(normalizedMainFileName, project); //assert mainFileCompilationUnits.size() == 1; mainCU = Iterables.getOnlyElement(mainFileCompilationUnits); //assert ((DefinitionPriority)mainCU.getDefinitionPriority()).getBasePriority() == DefinitionPriority.BasePriority.SOURCE_LIST; // Use main source file name as the root class name. config.setMainDefinition(mainQName); } Preconditions.checkNotNull(mainCU, "Main compilation unit can't be null"); // if (getTargetSettings() == null) // return false; target = JSSharedData.backend.createTarget(project, getTargetSettings(), null); return true; } private ITargetSettings getTargetSettings() { if (targetSettings == null) targetSettings = projectConfigurator.getTargetSettings(null); return targetSettings; } private void setupJS() throws IOException, InterruptedException { // JSSharedData.instance.reset(); project.getSourceCompilationUnitFactory().addHandler(asFileHandler); // JSSharedData.instance.setVerbose(config.isVerbose()); //JSSharedData.DEBUG = config.debug(); //JSSharedData.OPTIMIZE = !config.debug() && config.optimize(); //--- final Set<ICompilationUnit> compilationUnits = new HashSet<ICompilationUnit>(); // XXX // add builtins? registerSWCs(project); // XXX is this needed? } public static void registerSWCs(CompilerProject project) throws InterruptedException { // final JSSharedData sharedData = JSSharedData.instance; // // // collect all SWCCompilationUnit in swcUnits // final List<ICompilationUnit> swcUnits = new ArrayList<ICompilationUnit>(); // for (ICompilationUnit cu : project.getCompilationUnits()) // { // // if (cu instanceof SWCCompilationUnit) // // swcUnits.add(cu); // // // // final List<IDefinition> defs = getDefinitions(cu, false); // // for (IDefinition def : defs) // // { // // sharedData.registerDefinition(def); // // } // } } /** * Create a new Configurator. This method may be overridden to allow * Configurator subclasses to be created that have custom configurations. * * @return a new instance or subclass of {@link Configurator}. */ protected Configurator createConfigurator() { return JSSharedData.backend.createConfigurator(); } /** * Load configurations from all the sources. * * @param args command line arguments * @return True if mxmlc should continue with compilation. */ protected boolean configure(final String[] args) { project.getSourceCompilationUnitFactory().addHandler(asFileHandler); projectConfigurator = createConfigurator(); try { // // Print brief usage if no arguments provided. // if (args.length == 0) // { // final String usage = CommandLineConfigurator.brief( // getProgramName(), DEFAULT_VAR, // LocalizationManager.get(), L10N_CONFIG_PREFIX); // if (usage != null) // println(usage); // return false; // } // projectConfigurator.setConfiguration(args, ICompilerSettingsConstants.FILE_SPECS_VAR); projectConfigurator.applyToProject(project); problems = new ProblemQuery(projectConfigurator.getCompilerProblemSettings()); // Get the configuration and configBuffer which are now initialized. config = projectConfigurator.getConfiguration(); configBuffer = projectConfigurator.getConfigurationBuffer(); problems.addAll(projectConfigurator.getConfigurationProblems()); // Print version if "-version" is present. if (configBuffer.getVar("version") != null) //$NON-NLS-1$ { println(VersionInfo.buildMessage() + " (" + JSSharedData.COMPILER_VERSION + ")"); return false; } // // // Print help if "-help" is present. // final List<ConfigurationValue> helpVar = configBuffer // .getVar("help"); //$NON-NLS-1$ // if (helpVar != null) // { // processHelp(helpVar); // return false; // } // // for (String fileName : projectConfigurator // .getLoadedConfigurationFiles()) // { // JSSharedData.instance.stdout("Loading configuration: " // + fileName); // } // // if (config.isVerbose()) // { // for (final IFileSpecification themeFile : project // .getThemeFiles()) // { // JSSharedData.instance.stdout(String.format( // "Found theme file %s", themeFile.getPath())); // } // } // // If we have configuration errors then exit before trying to // validate the target. if (problems.hasErrors()) return false; validateTargetFile(); return true; } catch (ConfigurationException e) { final ICompilerProblem problem = new ConfigurationProblem(e); problems.add(problem); return false; } catch (Exception e) { final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage()); problems.add(problem); return false; } finally { // If we couldn't create a configuration, then create a default one // so we can exit without throwing an exception. if (config == null) { config = new Configuration(); configBuffer = new ConfigurationBuffer(Configuration.class, Configuration.getAliases()); } } } /** * Validate target file. * * @throws MustSpecifyTarget * @throws IOError */ protected void validateTargetFile() throws ConfigurationException { if (mainCU instanceof ResourceModuleCompilationUnit) return; //when compiling a Resource Module, no target file is defined. final String targetFile = config.getTargetFile(); if (targetFile == null) throw new ConfigurationException.MustSpecifyTarget(null, null, -1); final File file = new File(targetFile); if (!file.exists()) throw new ConfigurationException.IOError(targetFile); } private void println(String string) { // TODO Auto-generated method stub } /** * Wait till the workspace to finish compilation and close. */ protected void waitAndClose() { workspace.startIdleState(); try { workspace.close(); } finally { workspace.endIdleState(Collections.<ICompilerProject, Set<ICompilationUnit>>emptyMap()); } } /** * Force terminate the compilation process. */ protected void close() { workspace.close(); } // workaround for Falcon bug. // Input files with relative paths confuse the algorithm that extracts the root class name. protected static String[] fixArgs(final String[] args) { String[] newArgs = args; if (args.length > 1) { String targetPath = args[args.length - 1]; if (targetPath.startsWith(".")) { targetPath = FileUtils.getTheRealPathBecauseCanonicalizeDoesNotFixCase(new File(targetPath)); newArgs = new String[args.length]; for (int i = 0; i < args.length - 1; ++i) newArgs[i] = args[i]; newArgs[args.length - 1] = targetPath; } } return newArgs; } }