/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.config; import java.io.File; import java.io.Reader; import java.util.ArrayList; import java.util.Stack; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.io.IOUtils; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import org.apache.flex.compiler.exceptions.ConfigurationException; import org.apache.flex.compiler.filespecs.FileSpecification; /** * A utility class, which is used to parse an XML file of configuration options * and populate a ConfigurationBuffer. A counterpart of CommandLineConfigurator * and SystemPropertyConfigurator. * * @see <a href="http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf67670-7ff2.html">Configuration file syntax</a> */ public class FlashBuilderConfigurator { public static class SAXConfigurationException extends SAXParseException { private static final long serialVersionUID = -3388781933743434302L; SAXConfigurationException(ConfigurationException e, Locator locator) { super(null, locator); // ? this.innerException = e; } public ConfigurationException innerException; } /** * Take the command line args which should contain the -use-flashbuilder-project-files * option and an FB project folder, and return a new array of args * based on the FB project files. * @param args original set of args * @return new array of args */ public static String[] computeFlashBuilderArgs(String[] args, String suffix) { String fbFolder = args[args.length - 1]; ArrayList<String> fbArgs = new ArrayList<String>(); boolean isDebug = false; String sdkdir = null; for (String arg : args) { if (arg.equals("-debug") || arg.equals("-debug=true")) isDebug = true; if (arg.contains("+flexlib")) sdkdir = arg.substring(9); } ActionScriptPropertiesReader aspr = new ActionScriptPropertiesReader(); try { aspr.read(fbArgs, fbFolder, isDebug, suffix, sdkdir); } catch (ConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } FlexLibPropertiesReader flpr = new FlexLibPropertiesReader(); if (suffix.equals(".swc")) { try { flpr.read(fbArgs, fbFolder); } catch (ConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (String arg : args) { if (arg.startsWith("-output=")) { // allow command-line -output to override project -output int n = fbArgs.size(); for (int i = 0; i < n; i++) { String fbArg = fbArgs.get(i); if (fbArg.startsWith("-output")) { fbArgs.remove(i); break; } } } fbArgs.add(arg); } // remove last original arg which should have been project folder fbArgs.remove(fbArgs.size() - 1); if (suffix.equals(".swc")) { //fbArgs.add(flpr.classes); } else { // add new last arg which is path to app fbArgs.add(aspr.applicationPath); } System.out.println("using FlashBuilder Project Files"); System.out.println("FlashBuilder settings:"); for (String arg : fbArgs) System.out.println(" " + arg); return fbArgs.toArray(new String[fbArgs.size()]); } private static class ActionScriptPropertiesReader { public String applicationPath; public String additionalCompilerArguments; public void read(ArrayList<String> fbArgs, String fbFolder, boolean isDebug, String suffix, String sdkdir) throws ConfigurationException { String path = fbFolder + "/" + ".actionScriptProperties"; final ASPropertiesHandler h = new ASPropertiesHandler(fbArgs, path, fbFolder, "actionScriptProperties", isDebug, suffix, sdkdir); final SAXParserFactory factory = SAXParserFactory.newInstance(); Reader reader = null; try { FileSpecification fs = new FileSpecification(path); reader = fs.createReader(); final SAXParser parser = factory.newSAXParser(); final InputSource source = new InputSource(reader); parser.parse(source, h); applicationPath = h.applicationPath; if (h.additionalCompilerArguments != null) { String[] parts = h.additionalCompilerArguments.split("\\s+"); for (String part : parts) { if (part.contains("-load-config") && part.contains("..")) { String[] pieces = part.split("="); part = pieces[0] + "=" + h.sourcePath + "/" + pieces[1]; } fbArgs.add(part); } } } catch (SAXConfigurationException e) { throw e.innerException; } catch (SAXParseException e) { throw new ConfigurationException.OtherThrowable(e, null, path, e.getLineNumber()); } catch (Exception e) { throw new ConfigurationException.OtherThrowable(e, null, path, -1); } finally { IOUtils.closeQuietly(reader); } } } /** * SAX handler for .actionScriptProperties XML. */ private static class ASPropertiesHandler extends DefaultHandler { public ASPropertiesHandler(ArrayList<String> fbArgs, String source, String contextPath, String rootElement, boolean isDebug, String suffix, String sdkdir) { this.isDebug = isDebug; this.fbArgs = fbArgs; this.source = source; this.contextPath = contextPath; this.rootElement = rootElement; this.suffix = suffix; this.sdkdir = sdkdir; } private Stack<String> contextStack = new Stack<String>(); private ArrayList<String> fbArgs; private final String source; private final String contextPath; private final String rootElement; private final String suffix; private Locator locator; private boolean isDebug; private String sdkdir; public String applicationPath; private String outputFileName; private boolean inExclude; private String workspacePath; public String additionalCompilerArguments; public String sourcePath; @Override public void startElement(final String uri, final String localName, final String qname, final Attributes attributes) throws SAXException { // Verify and initialize the context stack at root element. if (contextStack.size() == 0) { if (!qname.equals(rootElement)) { throw new SAXConfigurationException( new ConfigurationException.IncorrectElement(rootElement, qname, this.source, locator.getLineNumber()), locator); } contextStack.push(qname); applicationPath = attributes.getValue("mainApplicationPath"); outputFileName = applicationPath.substring(0, applicationPath.lastIndexOf('.')); return; } else { if (qname.equals("compiler")) { String extras = attributes.getValue("additionalCompilerArguments"); if (extras != null && extras.length() > 0) { additionalCompilerArguments = extras; } String srcPath = attributes.getValue("sourceFolderPath"); if (srcPath != null && srcPath.length() > 0) { applicationPath = contextPath + "/" + srcPath + "/" + applicationPath; sourcePath = contextPath + "/" + srcPath; fbArgs.add("-source-path+=" + contextPath + "/" + srcPath); } String isApollo = attributes.getValue("useApolloConfig"); if (isApollo != null && isApollo.length() > 0) { if (isApollo.equals("true")) { fbArgs.add("+configname=air"); } } String isAccessible = attributes.getValue("generateAccessible"); if (isAccessible != null && isAccessible.length() > 0) { if (isAccessible.equals("true")) { fbArgs.add("-compiler.accessible=true"); } } String outputFolder = attributes.getValue("outputFolderPath"); if (outputFolder != null && outputFolder.length() > 0) { // swap bin-debug for bin-release if not debug swf if (!isDebug) outputFolder = outputFolder.replace("debug", "release"); String outFile = "-output="; if (!outputFolder.startsWith("/")) outFile += contextPath + "/"; fbArgs.add(outFile + outputFolder + "/" + outputFileName + suffix); } } else if (qname.equals("sourcePathEntry")) { String kind = attributes.getValue("kind"); if (kind.equals("1")) { String path = attributes.getValue("path"); fbArgs.add("-source-path+=" + contextPath + "/" + path); } } else if (qname.equals("libraryPathEntry")) { if (!inExclude) { String kind = attributes.getValue("kind"); String linkType = attributes.getValue("linkType"); String path = attributes.getValue("path"); if (!kind.equals("4")) // not an sdk reference { if (path.contains("${DOCUMENTS}")) { if (workspacePath == null) { workspacePath = source; File workspaceFile = new File(source); workspacePath = workspaceFile.getParent(); while (workspacePath != null && workspacePath.length() > 0) { workspaceFile = new File(workspacePath + "/.metadata"); if (workspaceFile.exists()) break; workspacePath = workspaceFile.getParentFile().getParent(); } } path = path.replace("${DOCUMENTS}", workspacePath); fbArgs.add(linkType.equals("2") ? "-external-library-path+=" + path : "-library-path+=" + path); } else if (path.contains("${PROJECT_FRAMEWORKS}")) { path = path.replace("${PROJECT_FRAMEWORKS}", sdkdir); fbArgs.add(linkType.equals("2") ? "-external-library-path+=" + path : "-library-path+=" + path); } else { if (path.startsWith("/")) { File f = new File(contextPath); // FB puts a "/" in front of references to other projects String parentPath = f.getParent(); f = new File(parentPath + path); if (f.exists()) fbArgs.add(linkType.equals("2") ? "-external-library-path+=" + parentPath + path : "-library-path+=" + parentPath + path); else fbArgs.add(linkType.equals("2") ? "-external-library-path+=" + path : "-library-path+=" + path); } else fbArgs.add(linkType.equals("2") ? "-external-library-path+=" + contextPath + "/" + path : "-library-path+=" + contextPath + "/" + path); } } } } else if (qname.equals("excludedEntries")) inExclude = true; } } @Override public void endElement(String uri, String localName, String qname) throws SAXException { if (qname.equals("excludedEntries")) inExclude = false; } @Override public void characters(char ch[], int start, int length) { } @Override public void setDocumentLocator(Locator locator) { this.locator = locator; } } private static class FlexLibPropertiesReader { public void read(ArrayList<String> fbArgs, String fbFolder) throws ConfigurationException { String path = fbFolder + "/" + ".flexLibProperties"; final FlexLibPropertiesHandler h = new FlexLibPropertiesHandler(fbArgs, path, fbFolder, "flexLibProperties"); final SAXParserFactory factory = SAXParserFactory.newInstance(); Reader reader = null; try { FileSpecification fs = new FileSpecification(path); reader = fs.createReader(); final SAXParser parser = factory.newSAXParser(); final InputSource source = new InputSource(reader); parser.parse(source, h); if (h.allClasses) { // may need to find and list all classes fbArgs.add("-include-sources=" + fbFolder + "/src"); } else { StringBuilder classList = new StringBuilder(); boolean first = true; for (String cname : h.includedClasses) { if (!first) classList.append(", "); first = false; classList.append(cname); } fbArgs.add("-include-classes=" + classList.toString()); } } catch (SAXConfigurationException e) { throw e.innerException; } catch (SAXParseException e) { throw new ConfigurationException.OtherThrowable(e, null, path, e.getLineNumber()); } catch (Exception e) { throw new ConfigurationException.OtherThrowable(e, null, path, -1); } finally { IOUtils.closeQuietly(reader); } } } /** * SAX handler for .flexLibProperties XML. */ private static class FlexLibPropertiesHandler extends DefaultHandler { public FlexLibPropertiesHandler(ArrayList<String> fbArgs, String source, String contextPath, String rootElement) { this.fbArgs = fbArgs; this.source = source; this.contextPath = contextPath; this.rootElement = rootElement; } private Stack<String> contextStack = new Stack<String>(); private ArrayList<String> fbArgs; private final String source; private final String contextPath; private final String rootElement; private Locator locator; public boolean allClasses; public ArrayList<String> includedClasses = new ArrayList<String>(); @Override public void startElement(final String uri, final String localName, final String qname, final Attributes attributes) throws SAXException { // Verify and initialize the context stack at root element. if (contextStack.size() == 0) { if (!qname.equals(rootElement)) { throw new SAXConfigurationException( new ConfigurationException.IncorrectElement(rootElement, qname, this.source, locator.getLineNumber()), locator); } String all = attributes.getValue("includeAllClasses"); if ("true".equals(all)) allClasses = true; contextStack.push(qname); return; } else { if (qname.equals("classEntry")) { String path = attributes.getValue("path"); includedClasses.add(path); } else if (qname.equals("namespaceManifestEntry")) { String manifest = attributes.getValue("manifest"); String namespace = attributes.getValue("namespace"); fbArgs.add("-namespace"); fbArgs.add(namespace); String mf = contextPath + "/" + manifest; File f = new File(mf); if (!f.exists()) { mf = contextPath + "/src/" + manifest; } fbArgs.add(mf); fbArgs.add("-include-namespaces"); fbArgs.add(namespace); } } } @Override public void endElement(String uri, String localName, String qname) throws SAXException { } @Override public void characters(char ch[], int start, int length) { } @Override public void setDocumentLocator(Locator locator) { this.locator = locator; } } }