package com.intellij.flex; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; public class FcshLauncher { // For each element XXXX of this array class com.intellij.flex.Fcsh4WithFixXXXX must exist private static final int[] FLEX_4_SDK_BUILDS_THAT_HAVE_FIX = new int[]{0}; // For each element XXXX of this array class com.intellij.flex.Fcsh45WithFixXXXX must exist private static final int[] FLEX_45_SDK_BUILDS_THAT_HAVE_FIX = new int[]{0}; public static void main(final String[] args) { try { final String flexSdkVersion = getFlexSdkVersion(); if (flexSdkVersion == null) { launchFcshWithoutFix(args); } else { launchFcshWithFix(flexSdkVersion, args); } } catch (OutOfMemoryError oomError) { System.out.println("(fcsh) out of memory"); System.exit(0); } } private static void launchFcshWithoutFix(final String[] args) { Class entryPointClass; entryPointClass = findClass("flex2.tools.Fcsh"); if (entryPointClass == null) { entryPointClass = findClass("flex2.tools.SimpleShell"); } if (entryPointClass != null) { try { final Method main = entryPointClass.getMethod("main", args.getClass()); main.invoke(null, new Object[]{args}); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { if (e.getCause() instanceof VirtualMachineError) { throw (VirtualMachineError) e.getCause(); } else { e.printStackTrace(); } } } else { System.err.println("Flex SDK is corrupted."); } } private static void launchFcshWithFix(final String flexSdkVersion, final String[] args) { final Class entryPointClass = getEntryPointClass(flexSdkVersion); if (entryPointClass == null) { launchFcshWithoutFix(args); } else { try { final Method main = entryPointClass.getMethod("main", args.getClass()); try { main.invoke(null, new Object[]{args}); } catch (InvocationTargetException e) { if (e.getCause() instanceof VirtualMachineError) { throw (VirtualMachineError) e.getCause(); } else { throw e; } } } catch (VirtualMachineError e) { // OutOfMemoryError will be will handled in main() throw e; } catch (Throwable t) { System.out.println("(fcsh) need to repeat command"); launchFcshWithoutFix(args); } } } private static Class getEntryPointClass(final String flexSdkVersion) { if (flexSdkVersion.startsWith("3")) { return findClass("com.intellij.flex.SimpleShellWithFix"); } else if (flexSdkVersion.startsWith("4")) { if (flexSdkVersion.startsWith("4.0") || flexSdkVersion.startsWith("4.1")) { return getFcshWithFixClass(flexSdkVersion, "com.intellij.flex.Fcsh4WithFix", FLEX_4_SDK_BUILDS_THAT_HAVE_FIX); } else if (flexSdkVersion.startsWith("4.5") || flexSdkVersion.startsWith("4.6")) { return getFcshWithFixClass(flexSdkVersion, "com.intellij.flex.Fcsh45WithFix", FLEX_45_SDK_BUILDS_THAT_HAVE_FIX); } } return null; } private static Class getFcshWithFixClass(final String flexSdkVersion, final String classNamePrefix, final int[] sdkBuildsThatHaveFix) { try { int build = Integer.parseInt(flexSdkVersion.substring(flexSdkVersion.indexOf(" build ") + " build ".length())); if (build == 0) { System.out.println("Warning: Flex SDK build can not be 0."); } else { for (int i = 0; i < sdkBuildsThatHaveFix.length; i++) { int buildWithFix = sdkBuildsThatHaveFix[i]; if (sdkBuildsThatHaveFix.length == i + 1 || build < sdkBuildsThatHaveFix[i + 1]) { return findClass(classNamePrefix + buildWithFix); } } } } catch (NumberFormatException ignored) { } return null; } private static String getFlexSdkVersion() { String version = readVersionUsingAPI(); if (!checkFlexSdkVersion(version)) { version = readVersionFromFlexSdkDescriptionXml(); } return checkFlexSdkVersion(version) ? version : null; } private static boolean checkFlexSdkVersion(final String flexSdkVersion) { return flexSdkVersion != null && flexSdkVersion.matches("[0-9][.][0-9].* build [0-9]+"); } private static String readVersionUsingAPI() { String version = null; try { final Class versionInfoClass = findClass("flex2.tools.VersionInfo"); final Method buildMessageMethod = versionInfoClass == null ? null : versionInfoClass.getMethod("buildMessage"); version = buildMessageMethod == null ? null : (String) buildMessageMethod.invoke(null); if (version != null && version.startsWith("Version ")) { version = version.substring("Version ".length()); } } catch (Throwable ignored) { } return version; } private static String readVersionFromFlexSdkDescriptionXml() { final String flexSdkHome = System.getProperty("application.home"); if (flexSdkHome == null) { return null; } final File flexSdkDescriptionFile = new File(flexSdkHome + File.separatorChar + "flex-sdk-description.xml"); if (!flexSdkDescriptionFile.exists()) { return null; } final String versionElement = "<flex-sdk-description><version>"; final String buildElement = "<flex-sdk-description><build>"; FileInputStream xmlInputStream = null; try { xmlInputStream = new FileInputStream(flexSdkDescriptionFile); final Map<String, List<String>> versionInfo = findXMLElements(xmlInputStream, Arrays.asList(versionElement, buildElement)); return (versionInfo.get(versionElement).isEmpty() ? null : versionInfo.get(versionElement).get(0)) + (versionInfo.get(buildElement).isEmpty() ? "" : (" build " + versionInfo.get(buildElement).get(0))); } catch (IOException e) { return null; } finally { close(xmlInputStream); } } /** * Looks through input stream containing XML document and finds all entries of XML elements listed in <code>xmlElements</code>. * Content of these elements is put to result map. XML namespaces are not taken into consideration. * * @param xmlInputStream input stream with xml content to parse * @param xmlElements list of XML elements to look for. * Format is: <code>"<root_element><child_element><subelement_to_look_for>"</code>. * Listed XML elements MUST NOT contain subelements, otherwise result is undefined * @return map, keys are XML elements listed in <code>xmlElements</code>, * values are all entries of respective element (may be empty list) */ private static Map<String, List<String>> findXMLElements(final InputStream xmlInputStream, final List<String> xmlElements) { final Map<String, List<String>> resultMap = new HashMap<String, List<String>>(); for (final String element : xmlElements) { resultMap.put(element, new ArrayList<String>()); } try { SAXParserFactory.newInstance().newSAXParser().parse(xmlInputStream, new DefaultHandler() { private String currentElement = ""; private StringBuilder currentElementContent = new StringBuilder(); public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { currentElement += "<" + qName + ">"; } public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (xmlElements.contains(currentElement)) { resultMap.get(currentElement).add(currentElementContent.toString()); currentElementContent.delete(0, currentElementContent.length()); } assert currentElement.endsWith("<" + qName + ">"); currentElement = currentElement.substring(0, currentElement.length() - (qName.length() + 2)); } public void characters(char[] ch, int start, int length) throws SAXException { if (xmlElements.contains(currentElement)) { currentElementContent.append(ch, start, length); } } }); } catch (SAXException ignored) { } catch (IOException ignored) { } catch (ParserConfigurationException ignored) { } return resultMap; } private static Class findClass(final String className) { try { return Class.forName(className); } catch (Throwable t) { return null; } } private static void close(final Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException ignored) { } } } }