private Pair<String, ValueSource> getValueAndSource(final CompilerOptionInfo info) {
    assert !info.isGroup() : info.DISPLAY_NAME;

    final String bcLevelValue = myBC.getCompilerOptions().getOption(info.ID);
    if (bcLevelValue != null) return Pair.create(bcLevelValue, ValueSource.BC);

    final String moduleLevelValue = myModuleLevelCompilerOptions.getOption(info.ID);
    if (moduleLevelValue != null) return Pair.create(moduleLevelValue, ValueSource.ModuleDefault);

    final String projectLevelValue = myProjectLevelCompilerOptions.getOption(info.ID);
    if (projectLevelValue != null)
      return Pair.create(projectLevelValue, ValueSource.ProjectDefault);

    return Pair.create(
        info.getDefaultValue(
            mySdk.getVersionString(), myBC.getNature(), myBC.getDependencies().getComponentSet()),
        ValueSource.GlobalDefault);
  }
 /** Adds options that get incorrect default values inside compiler code if not set explicitly. */
 private void handleOptionsWithSpecialValues(final Element rootElement) {
   for (final CompilerOptionInfo info : CompilerOptionInfo.getOptionsWithSpecialValues()) {
     final Pair<String, ValueSource> valueAndSource = getValueAndSource(info);
     final boolean themeForPureAS = myBC.isPureAs() && "compiler.theme".equals(info.ID);
     if (valueAndSource.second == ValueSource.GlobalDefault
         && (!valueAndSource.first.isEmpty() || themeForPureAS)) {
       // do not add empty preloader to Web/Desktop, let compiler take default itself
       // (mx.preloaders.SparkDownloadProgressBar when -compatibility-version >= 4.0 and
       // mx.preloaders.DownloadProgressBar when -compatibility-version < 4.0)
       addOption(rootElement, info, valueAndSource.first);
     }
   }
 }
  private void addOtherOptions(final Element rootElement) {
    final Map<String, String> options =
        new THashMap<String, String>(myProjectLevelCompilerOptions.getAllOptions());
    options.putAll(myModuleLevelCompilerOptions.getAllOptions());
    options.putAll(myBC.getCompilerOptions().getAllOptions());

    final String addOptions =
        myProjectLevelCompilerOptions.getAdditionalOptions()
            + " "
            + myModuleLevelCompilerOptions.getAdditionalOptions()
            + " "
            + myBC.getCompilerOptions().getAdditionalOptions();
    final List<String> contextRootInAddOptions =
        FlexCommonUtils.getOptionValues(addOptions, "context-root", "compiler.context-root");

    if (options.get("compiler.context-root") == null && contextRootInAddOptions.isEmpty()) {
      final List<String> servicesInAddOptions =
          FlexCommonUtils.getOptionValues(addOptions, "services", "compiler.services");
      if (options.get("compiler.services") != null || !servicesInAddOptions.isEmpty()) {
        options.put("compiler.context-root", "");
      }
    }

    for (final Map.Entry<String, String> entry : options.entrySet()) {
      addOption(rootElement, CompilerOptionInfo.getOptionInfo(entry.getKey()), entry.getValue());
    }

    final String namespacesRaw = options.get("compiler.namespaces.namespace");
    if (namespacesRaw != null && myBC.getOutputType() == OutputType.Library) {
      final String namespaces =
          FlexCommonUtils.replacePathMacros(
              namespacesRaw, myModule, myFlexmojos ? "" : mySdk.getHomePath());
      final StringBuilder buf = new StringBuilder();
      for (final String listEntry :
          StringUtil.split(namespaces, CompilerOptionInfo.LIST_ENTRIES_SEPARATOR)) {
        final int tabIndex = listEntry.indexOf(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR);
        assert tabIndex != -1 : namespaces;
        final String namespace = listEntry.substring(0, tabIndex);
        if (buf.length() > 0) buf.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
        buf.append(namespace);
      }

      if (buf.length() > 0) {
        addOption(rootElement, CompilerOptionInfo.INCLUDE_NAMESPACES_INFO, buf.toString());
      }
    }
  }
  private void addNamespaces(final Element rootElement) {
    final StringBuilder namespaceBuilder = new StringBuilder();
    FlexCommonUtils.processStandardNamespaces(
        myBC,
        new PairConsumer<String, String>() {
          @Override
          public void consume(final String namespace, final String relativePath) {
            if (namespaceBuilder.length() > 0) {
              namespaceBuilder.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
            }
            namespaceBuilder
                .append(namespace)
                .append(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR)
                .append(CompilerOptionInfo.FLEX_SDK_MACRO + "/")
                .append(relativePath);
          }
        });

    if (namespaceBuilder.length() == 0) return;
    final CompilerOptionInfo info =
        CompilerOptionInfo.getOptionInfo("compiler.namespaces.namespace");
    addOption(rootElement, info, namespaceBuilder.toString());
  }
  private void addOption(
      final Element rootElement, final CompilerOptionInfo info, final String rawValue) {
    if (!info.isApplicable(mySdk.getVersionString(), myBC.getNature())) {
      return;
    }

    final String value =
        FlexCommonUtils.replacePathMacros(
            rawValue, myModule, myFlexmojos ? "" : mySdk.getHomePath());

    final String pathInFlexConfig =
        info.ID.startsWith("compiler.debug") ? "compiler.debug" : info.ID;
    final List<String> elementNames = StringUtil.split(pathInFlexConfig, ".");
    Element parentElement = rootElement;

    for (int i1 = 0; i1 < elementNames.size() - 1; i1++) {
      parentElement = getOrCreateElement(parentElement, elementNames.get(i1));
    }

    final String elementName = elementNames.get(elementNames.size() - 1);

    switch (info.TYPE) {
      case Boolean:
      case String:
      case Int:
      case File:
        final Element simpleElement = new Element(elementName, parentElement.getNamespace());
        simpleElement.setText(value);
        parentElement.addContent(simpleElement);
        break;
      case List:
        if (info.LIST_ELEMENTS.length == 1) {
          final Element listHolderElement = getOrCreateElement(parentElement, elementName);
          for (final String listElementValue :
              StringUtil.split(value, CompilerOptionInfo.LIST_ENTRIES_SEPARATOR)) {
            final Element child =
                new Element(info.LIST_ELEMENTS[0].NAME, listHolderElement.getNamespace());
            child.setText(listElementValue);
            listHolderElement.addContent(child);
          }
        } else {
          for (final String listEntry :
              StringUtil.split(value, String.valueOf(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR))) {
            final Element repeatableListHolderElement =
                new Element(elementName, parentElement.getNamespace());

            final List<String> values =
                StringUtil.split(
                    listEntry, CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR, true, false);
            assert info.LIST_ELEMENTS.length == values.size() : info.ID + "=" + value;

            for (int i = 0; i < info.LIST_ELEMENTS.length; i++) {
              final Element child =
                  new Element(
                      info.LIST_ELEMENTS[i].NAME, repeatableListHolderElement.getNamespace());
              child.setText(values.get(i));
              repeatableListHolderElement.addContent(child);
            }

            parentElement.addContent(repeatableListHolderElement);
          }
        }
        break;
      default:
        assert false : info.DISPLAY_NAME;
    }
  }
  private void addSourcePaths(final Element rootElement) {
    final String localeValue =
        getValueAndSource(CompilerOptionInfo.getOptionInfo("compiler.locale")).first;
    final List<String> locales =
        StringUtil.split(localeValue, CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
    // when adding source paths we respect locales set both in UI and in Additional compiler options
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myProjectLevelCompilerOptions.getAdditionalOptions(), "locale", "compiler.locale"));
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myModuleLevelCompilerOptions.getAdditionalOptions(), "locale", "compiler.locale"));
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myBC.getCompilerOptions().getAdditionalOptions(), "locale", "compiler.locale"));

    final Set<String> sourcePathsWithLocaleToken =
        new THashSet<String>(); // Set - to avoid duplication of paths like "locale/{locale}"
    final List<String> sourcePathsWithoutLocaleToken = new LinkedList<String>();

    for (JpsModuleSourceRoot srcRoot : myModule.getSourceRoots(JavaSourceRootType.SOURCE)) {
      final String srcRootPath = JpsPathUtil.urlToPath(srcRoot.getUrl());
      if (locales.contains(PathUtilRt.getFileName(srcRootPath))) {
        sourcePathsWithLocaleToken.add(
            PathUtilRt.getParentPath(srcRootPath) + "/" + FlexCommonUtils.LOCALE_TOKEN);
      } else {
        sourcePathsWithoutLocaleToken.add(srcRootPath);
      }
    }

    if (includeTestRoots()) {
      for (JpsModuleSourceRoot srcRoot : myModule.getSourceRoots(JavaSourceRootType.TEST_SOURCE)) {
        final String srcRootPath = JpsPathUtil.urlToPath(srcRoot.getUrl());
        if (locales.contains(PathUtilRt.getFileName(srcRootPath))) {
          sourcePathsWithLocaleToken.add(
              PathUtilRt.getParentPath(srcRootPath) + "/" + FlexCommonUtils.LOCALE_TOKEN);
        } else {
          sourcePathsWithoutLocaleToken.add(srcRootPath);
        }
      }
    }

    final StringBuilder sourcePathBuilder = new StringBuilder();

    if (myCSS) {
      final String cssFolderPath = PathUtilRt.getParentPath(myBC.getMainClass());
      if (!sourcePathsWithoutLocaleToken.contains(cssFolderPath)) {
        sourcePathBuilder.append(cssFolderPath);
      }
    }

    for (final String sourcePath : sourcePathsWithLocaleToken) {
      if (sourcePathBuilder.length() > 0) {
        sourcePathBuilder.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
      }
      sourcePathBuilder.append(sourcePath);
    }

    for (final String sourcePath : sourcePathsWithoutLocaleToken) {
      if (sourcePathBuilder.length() > 0) {
        sourcePathBuilder.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
      }
      sourcePathBuilder.append(sourcePath);
    }

    addOption(rootElement, CompilerOptionInfo.SOURCE_PATH_INFO, sourcePathBuilder.toString());
  }
  private void addRootsFromSdk(final Element rootElement) {
    final CompilerOptionInfo localeInfo = CompilerOptionInfo.getOptionInfo("compiler.locale");
    if (!getValueAndSource(localeInfo).first.isEmpty()) {
      addOption(
          rootElement,
          CompilerOptionInfo.LIBRARY_PATH_INFO,
          mySdk.getHomePath() + "/frameworks/locale/{locale}");
    }

    final Map<String, String> libNameToRslInfo = new THashMap<String, String>();

    for (final String swcUrl : mySdk.getParent().getRootUrls(JpsOrderRootType.COMPILED)) {
      final String swcPath = JpsPathUtil.urlToPath(swcUrl);
      if (!swcPath.toLowerCase().endsWith(".swc")) {
        Logger.getInstance(CompilerConfigGeneratorRt.class.getName())
            .warn("Unexpected URL in Flex SDK classes: " + swcUrl);
        continue;
      }

      LinkageType linkageType = FlexCommonUtils.getSdkEntryLinkageType(swcPath, myBC);

      // check applicability
      if (linkageType == null) continue;
      // resolve default
      if (linkageType == LinkageType.Default)
        linkageType = myBC.getDependencies().getFrameworkLinkage();
      if (linkageType == LinkageType.Default) {
        linkageType =
            FlexCommonUtils.getDefaultFrameworkLinkage(mySdk.getVersionString(), myBC.getNature());
      }
      if (myCSS && linkageType == LinkageType.Include) linkageType = LinkageType.Merged;

      final CompilerOptionInfo info =
          linkageType == LinkageType.Merged
              ? CompilerOptionInfo.LIBRARY_PATH_INFO
              : linkageType == LinkageType.RSL
                  ? CompilerOptionInfo.LIBRARY_PATH_INFO
                  : linkageType == LinkageType.External
                      ? CompilerOptionInfo.EXTERNAL_LIBRARY_INFO
                      : linkageType == LinkageType.Include
                          ? CompilerOptionInfo.INCLUDE_LIBRARY_INFO
                          : null;

      assert info != null : swcPath + ": " + linkageType.getShortText();

      addOption(rootElement, info, swcPath);

      if (linkageType == LinkageType.RSL) {
        final List<String> rslUrls = RslUtil.getRslUrls(mySdk.getHomePath(), swcPath);
        if (rslUrls.isEmpty()) continue;

        final StringBuilder rslBuilder = new StringBuilder();
        final String firstUrl = rslUrls.get(0);
        rslBuilder
            .append(swcPath)
            .append(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR)
            .append(firstUrl)
            .append(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR);
        if (firstUrl.startsWith("http://")) {
          rslBuilder.append("http://fpdownload.adobe.com/pub/swz/crossdomain.xml");
        }

        if (rslUrls.size() > 1) {
          final String secondUrl = rslUrls.get(1);
          rslBuilder
              .append(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR)
              .append(secondUrl)
              .append(CompilerOptionInfo.LIST_ENTRY_PARTS_SEPARATOR);
          if (secondUrl.startsWith("http://")) {
            rslBuilder.append("http://fpdownload.adobe.com/pub/swz/crossdomain.xml");
          }
        }

        final String swcName = PathUtilRt.getFileName(swcPath);
        final String libName = swcName.substring(0, swcName.length() - ".swc".length());
        libNameToRslInfo.put(libName, rslBuilder.toString());
      }
    }

    if (myBC.getNature().isLib()) {
      final String theme =
          getValueAndSource(CompilerOptionInfo.getOptionInfo("compiler.theme")).first;
      if (theme != null && theme.toLowerCase().endsWith(".swc")) {
        addOption(rootElement, CompilerOptionInfo.LIBRARY_PATH_INFO, theme);
      }
    }

    addRslInfo(rootElement, libNameToRslInfo);
  }