<commit_before>package org.jetbrains.jps.idea

import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jps.artifacts.Artifact

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future

import org.jetbrains.jps.*

/**
 * @author max
 */
public class IdeaProjectLoader {
  private int libraryCount = 0
  Project project
  private String projectOutputPath
  private String projectLanguageLevel
  private Map<String, String> pathVariables
  private ProjectMacroExpander projectMacroExpander
  private ProjectLoadingErrorReporter errorReporter
  private static final List<AdditionalRootsProviderService> rootsProviderLoader = OwnServiceLoader.load(AdditionalRootsProviderService.class).collect()
  private final XmlParser xmlParser = new XmlParser(false, false)
  private static final ExecutorService ourThreadPool = Executors.newFixedThreadPool(2 * Runtime.getRuntime().availableProcessors())

  public static String guessHome(Script script) {
    File home = new File(script["gant.file"].substring("file:".length()))

    while (home != null) {
      if (home.isDirectory()) {
        if (new File(home, ".idea").exists()) return home.getCanonicalPath()
      }

      home = home.getParentFile()
    }

    return null
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables) {
    return loadFromPath(project, path, pathVariables, "")
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables, String script) {
    return loadFromPath(project, path, pathVariables, script, new SystemOutErrorReporter(true))
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables, String script, ProjectLoadingErrorReporter errorReporter) {
    def loader = new IdeaProjectLoader(project, pathVariables, errorReporter)

    loader.doLoadFromPath(path);

    if (script != null) {
      if (script.startsWith ("@")) {
        new GroovyShell(new Binding(project:project)).evaluate(new File (script.substring(1)))
      };
      else {
        new GroovyShell(new Binding(project:project)).evaluate(script)
      }
    }

    return loader.projectMacroExpander;
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, String script) {
    return loadFromPath(project, path, [:], script);
  }

  private def IdeaProjectLoader(Project project, Map<String, String> pathVariables, ProjectLoadingErrorReporter errorReporter) {
    this.project = project;
    this.pathVariables = pathVariables
    this.errorReporter = errorReporter
  }

  private def doLoadFromPath(String path) {
    def fileAtPath = new File(path)

    if (fileAtPath.isFile() && path.endsWith(".ipr")) {
      loadFromIpr(path)
      return
    }
    else {
      File directoryBased = null;
      if (".idea".equals(fileAtPath.getName())) {
        directoryBased = fileAtPath;
      }
      else {
        def child = new File(fileAtPath, ".idea")
        if (child.exists()) {
          directoryBased = child
        }
      }

      if (directoryBased != null) {
        loadFromDirectoryBased(directoryBased.getCanonicalFile())
        return
      }
    }

    errorReporter.error("Cannot find IntelliJ IDEA project files at $path")
  }

  def loadFromIpr(String path) {
    def iprFile = new File(path).getCanonicalFile()
    project.projectName = StringUtil.trimEnd(iprFile.getName(), ".ipr")
    project.locationHash = iprFile.absolutePath.hashCode()
    projectMacroExpander = new ProjectMacroExpander(pathVariables, iprFile.parentFile.absolutePath)

    def root = xmlParser.parse(iprFile)
    loadProjectJdkAndOutput(root)
    loadCompilerConfiguration(root)
    loadProjectFileEncodings(root)
    loadWorkspaceConfiguration(new File(iprFile.parentFile, iprFile.name[0..-4]+"iws"))
    loadProjectLibraries(getComponent(root, "libraryTable"))
    loadModules(getComponent(root, "ProjectModuleManager"))
    loadUiDesignerConfiguration(root)
    loadArtifacts(getComponent(root, "ArtifactManager"))
    loadRunConfigurations(getComponent(root, "ProjectRunConfigurationManager"))
  }

  def loadFromDirectoryBased(File dir) {
    project.projectName = getDirectoryBaseProjectName(dir)
    project.locationHash = dir.absolutePath.hashCode()
    projectMacroExpander = new ProjectMacroExpander(pathVariables, dir.parentFile.absolutePath)
    def miscXml = new File(dir, "misc.xml")
    if (miscXml.exists()) {
      loadProjectJdkAndOutput(xmlParser.parse(miscXml))
    }
    else {
      errorReporter.error("Cannot find misc.xml in $dir")
    }

    def encodingsXml = new File(dir, "encodings.xml")
    if (encodingsXml.exists()) {
      loadProjectFileEncodings(xmlParser.parse(encodingsXml))
    }

    def compilerXml = new File(dir, "compiler.xml")
    if (compilerXml.exists()) {
      loadCompilerConfiguration(xmlParser.parse(compilerXml))
    }
    loadWorkspaceConfiguration(new File(dir, "workspace.xml"))

    def librariesFolder = new File(dir, "libraries")
    if (librariesFolder.isDirectory()) {
      librariesFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          Node librariesComponent = xmlParser.parse(file)
          loadProjectLibraries(librariesComponent)
        }
      }
    }

    def modulesXml = new File(dir, "modules.xml")
    if (modulesXml.exists()) {
      Node modulesXmlRoot = xmlParser.parse(modulesXml)
      loadModules(modulesXmlRoot.component[0])
    }
    else {
      errorReporter.error("Cannot find modules.xml in $dir")
    }

    def uiDesignerXml = new File(dir, "uiDesigner.xml")
    if (uiDesignerXml.exists()) {
      loadUiDesignerConfiguration(xmlParser.parse(uiDesignerXml))
    }

    def artifactsFolder = new File(dir, "artifacts")
    if (artifactsFolder.isDirectory()) {
      artifactsFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          def artifactsComponent = xmlParser.parse(file)
          loadArtifacts(artifactsComponent)
        }
      }
    }

    def runConfFolder = new File(dir, "runConfigurations")
    if (runConfFolder.isDirectory()) {
      runConfFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          def runConfManager = xmlParser.parse(file);
          loadRunConfigurations(runConfManager);
        }
      }
    }
  }

  def getDirectoryBaseProjectName(File dir) {
    File nameFile = new File(dir, ".name")
    if (nameFile.isFile()) {
      return FileUtil.loadFile(nameFile).trim()
    }
    return StringUtil.replace(dir.parentFile.name, ":", "")
  }

  boolean isXmlFile(File file) {
    return file.isFile() && StringUtil.endsWithIgnoreCase(file.name, ".xml")
  }

  private def loadWorkspaceConfiguration(File workspaceFile) {
    if (!workspaceFile.exists()) return

    def root = xmlParser.parse(workspaceFile)
    def options = loadOptions(getComponent(root, "CompilerWorkspaceConfiguration"))
    // compatibility: in older projects this setting was stored in workspace
    if (project.compilerConfiguration.addNotNullAssertions == true) { // if is the same as default value
      project.compilerConfiguration.addNotNullAssertions = parseBoolean(options["ASSERT_NOT_NULL"], true);
    }
    project.compilerConfiguration.clearOutputDirectoryOnRebuild = parseBoolean(options["CLEAR_OUTPUT_DIRECTORY"], true);
  }

  private def loadCompilerConfiguration(Node root) {
    def rawPatterns = []
    def includePatterns = []
    def excludePatterns = []
    def componentTag = getComponent(root, "CompilerConfiguration")

    componentTag?.wildcardResourcePatterns?.getAt(0)?.entry?.each {Node entryTag ->
      String pattern = entryTag."@name"
      rawPatterns << pattern;
      if (pattern.startsWith("!")) {
        excludePatterns << convertPattern(pattern.substring(1))
      }
      else {
        includePatterns << convertPattern(pattern)
      }
    }
    CompilerConfiguration configuration = project.compilerConfiguration
    if (!includePatterns.isEmpty() || !excludePatterns.isEmpty()) {
      configuration.resourceIncludePatterns = includePatterns
      configuration.resourceExcludePatterns = excludePatterns
    }
    if (!rawPatterns.isEmpty()) {
      configuration.resourcePatterns = rawPatterns;
    }

    def excludesTag = componentTag?.excludeFromCompile?.getAt(0)
    if (excludesTag != null) {
      excludesTag.file?.each {
        configuration.excludes.addExcludedFile(getFileByUrl(it."@url"))
      }
      excludesTag.directory?.each {
        configuration.excludes.addExcludedDirectory(getFileByUrl(it."@url"), Boolean.parseBoolean(it."@includeSubdirectories"))
      }
    }

    configuration.javacOptions.putAll(loadOptions(getComponent(root, "JavacSettings")))

    def annotationProcessingTag = componentTag?.annotationProcessing
    if (annotationProcessingTag != null) {
      configuration.annotationProcessing.enabled = parseBoolean(annotationProcessingTag."@enabled", false)
      configuration.annotationProcessing.obtainProcessorsFromClasspath = parseBoolean(annotationProcessingTag."@useClasspath", true)
      List<String> processorPaths = []
      annotationProcessingTag.processorPath?.each {
        processorPaths << projectMacroExpander.expandMacros(it."@value")
      }
      configuration.annotationProcessing.processorsPath = processorPaths.join(File.pathSeparator)
      annotationProcessingTag.processor?.each {
        configuration.annotationProcessing.processorsOptions[it."@name"] = it."@options" ?: ""
      }
      annotationProcessingTag.processModule?.each {
        configuration.annotationProcessing.processModule[it."@name"] = it."@generatedDirName"
      }
    }

    def targetLevels = componentTag?.bytecodeTargetLevel
    if (!targetLevels.isEmpty()) {
      def targetLevelTag = targetLevels.first()
      configuration.bytecodeTarget.projectBytecodeTarget = targetLevelTag."@target" ?: null;
      targetLevelTag.module?.each {
        configuration.bytecodeTarget.modulesBytecodeTarget[it."@name"] = it."@target" ?: ""
      }
    }

    def addNotNullTag = componentTag?.addNotNullAssertions
    if (addNotNullTag != null) {
      project.compilerConfiguration.addNotNullAssertions = parseBoolean(addNotNullTag."@enabled", true);
    }
  }

  private def loadUiDesignerConfiguration(Node root) {
    def options = loadOptions(getComponent(root, "uidesigner-configuration"))
    project.uiDesignerConfiguration.copyFormsRuntimeToOutput = parseBoolean(options["COPY_FORMS_RUNTIME_TO_OUTPUT"], true)
  }

  private File getFileByUrl(final String url) {
    return new File(FileUtil.toCanonicalPath(projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url))))
  }

  private static boolean parseBoolean(Object value, boolean defaultValue) {
    if (value instanceof NodeList) {
      if (value.isEmpty()) return defaultValue
      value = value[0]
    }
    return value != null ? Boolean.parseBoolean((String)value) : defaultValue
  }
  
  private Map<String, String> loadOptions(Node optionsTag) {
    def result = new HashMap<String, String>()
    optionsTag?.option?.each {Node optionTag ->
      result[optionTag."@name"] = optionTag."@value";
    }
    return result
  }

  private String convertPattern(String pattern) {
    if (pattern.indexOf('/') == -1) {
      return "**/" + pattern
    }
    if (pattern.startsWith("/")) {
      return pattern.substring(1)
    }
    return pattern
  }

  private def loadProjectJdkAndOutput(Node root) {
    def componentTag = getComponent(root, "ProjectRootManager")
    def sdkName = componentTag?."@project-jdk-name"
    def sdk = project.sdks[sdkName]
    if (sdk == null) {
      errorReporter.info("Project SDK '$sdkName' is not defined. Embedded javac will be used")
    }
    def outputTag = componentTag?.output?.getAt(0)
    String outputPath = outputTag != null ? IdeaProjectLoadingUtil.pathFromUrl(outputTag.'@url') : null
    projectOutputPath = outputPath != null && outputPath.length() > 0 ? FileUtil.toCanonicalPath(projectMacroExpander.expandMacros(outputPath)) : null
    project.projectSdk = sdk
    projectLanguageLevel = componentTag?."@languageLevel"
  }

  private def loadProjectFileEncodings(Node root) {
    def componentTag = getComponent(root, "Encoding");
    if (componentTag == null) return;
    componentTag.file?.each {Node fileNode ->
      def url = fileNode."@url";
      def charset = fileNode."@charset";

      if ("PROJECT".equals(url)) {
        project.projectCharset = charset;
      }
      else {
        def path = projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url));
        project.filePathToCharset[path] = charset;
      }
    }
  }

  private NodeList loadProjectLibraries(Node librariesComponent) {
    return (NodeList)librariesComponent?.library?.each {Node libTag ->
      initLibrary(libTag, projectMacroExpander, project.createLibrary(libTag."@name", Closure.IDENTITY))
    }
  }

  def loadArtifacts(Node artifactsComponent) {
    if (artifactsComponent == null) return;
    ArtifactLoader artifactLoader = new ArtifactLoader(project, projectMacroExpander, errorReporter)
    artifactsComponent.artifact.each {Node artifactTag ->
      def artifactName = artifactTag."@name"
      def outputPath = projectMacroExpander.expandMacros(artifactTag."output-path"[0]?.text())
      def root = artifactLoader.loadLayoutElement(artifactTag.root[0], artifactName)
      def options = artifactLoader.loadOptions(artifactTag, artifactName)
      def artifact = new Artifact(name: artifactName, rootElement: root, outputPath: outputPath, properties: options);
      project.artifacts[artifact.name] = artifact;
    }
  }

  def loadRunConfigurations(Node runConfManager) {<commit_msg>[jeka] ignore empty encoding attribute<commit_after>package org.jetbrains.jps.idea

import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jps.artifacts.Artifact

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future

import org.jetbrains.jps.*

/**
 * @author max
 */
public class IdeaProjectLoader {
  private int libraryCount = 0
  Project project
  private String projectOutputPath
  private String projectLanguageLevel
  private Map<String, String> pathVariables
  private ProjectMacroExpander projectMacroExpander
  private ProjectLoadingErrorReporter errorReporter
  private static final List<AdditionalRootsProviderService> rootsProviderLoader = OwnServiceLoader.load(AdditionalRootsProviderService.class).collect()
  private final XmlParser xmlParser = new XmlParser(false, false)
  private static final ExecutorService ourThreadPool = Executors.newFixedThreadPool(2 * Runtime.getRuntime().availableProcessors())

  public static String guessHome(Script script) {
    File home = new File(script["gant.file"].substring("file:".length()))

    while (home != null) {
      if (home.isDirectory()) {
        if (new File(home, ".idea").exists()) return home.getCanonicalPath()
      }

      home = home.getParentFile()
    }

    return null
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables) {
    return loadFromPath(project, path, pathVariables, "")
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables, String script) {
    return loadFromPath(project, path, pathVariables, script, new SystemOutErrorReporter(true))
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, Map<String, String> pathVariables, String script, ProjectLoadingErrorReporter errorReporter) {
    def loader = new IdeaProjectLoader(project, pathVariables, errorReporter)

    loader.doLoadFromPath(path);

    if (script != null) {
      if (script.startsWith ("@")) {
        new GroovyShell(new Binding(project:project)).evaluate(new File (script.substring(1)))
      };
      else {
        new GroovyShell(new Binding(project:project)).evaluate(script)
      }
    }

    return loader.projectMacroExpander;
  }

  public static ProjectMacroExpander loadFromPath(Project project, String path, String script) {
    return loadFromPath(project, path, [:], script);
  }

  private def IdeaProjectLoader(Project project, Map<String, String> pathVariables, ProjectLoadingErrorReporter errorReporter) {
    this.project = project;
    this.pathVariables = pathVariables
    this.errorReporter = errorReporter
  }

  private def doLoadFromPath(String path) {
    def fileAtPath = new File(path)

    if (fileAtPath.isFile() && path.endsWith(".ipr")) {
      loadFromIpr(path)
      return
    }
    else {
      File directoryBased = null;
      if (".idea".equals(fileAtPath.getName())) {
        directoryBased = fileAtPath;
      }
      else {
        def child = new File(fileAtPath, ".idea")
        if (child.exists()) {
          directoryBased = child
        }
      }

      if (directoryBased != null) {
        loadFromDirectoryBased(directoryBased.getCanonicalFile())
        return
      }
    }

    errorReporter.error("Cannot find IntelliJ IDEA project files at $path")
  }

  def loadFromIpr(String path) {
    def iprFile = new File(path).getCanonicalFile()
    project.projectName = StringUtil.trimEnd(iprFile.getName(), ".ipr")
    project.locationHash = iprFile.absolutePath.hashCode()
    projectMacroExpander = new ProjectMacroExpander(pathVariables, iprFile.parentFile.absolutePath)

    def root = xmlParser.parse(iprFile)
    loadProjectJdkAndOutput(root)
    loadCompilerConfiguration(root)
    loadProjectFileEncodings(root)
    loadWorkspaceConfiguration(new File(iprFile.parentFile, iprFile.name[0..-4]+"iws"))
    loadProjectLibraries(getComponent(root, "libraryTable"))
    loadModules(getComponent(root, "ProjectModuleManager"))
    loadUiDesignerConfiguration(root)
    loadArtifacts(getComponent(root, "ArtifactManager"))
    loadRunConfigurations(getComponent(root, "ProjectRunConfigurationManager"))
  }

  def loadFromDirectoryBased(File dir) {
    project.projectName = getDirectoryBaseProjectName(dir)
    project.locationHash = dir.absolutePath.hashCode()
    projectMacroExpander = new ProjectMacroExpander(pathVariables, dir.parentFile.absolutePath)
    def miscXml = new File(dir, "misc.xml")
    if (miscXml.exists()) {
      loadProjectJdkAndOutput(xmlParser.parse(miscXml))
    }
    else {
      errorReporter.error("Cannot find misc.xml in $dir")
    }

    def encodingsXml = new File(dir, "encodings.xml")
    if (encodingsXml.exists()) {
      loadProjectFileEncodings(xmlParser.parse(encodingsXml))
    }

    def compilerXml = new File(dir, "compiler.xml")
    if (compilerXml.exists()) {
      loadCompilerConfiguration(xmlParser.parse(compilerXml))
    }
    loadWorkspaceConfiguration(new File(dir, "workspace.xml"))

    def librariesFolder = new File(dir, "libraries")
    if (librariesFolder.isDirectory()) {
      librariesFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          Node librariesComponent = xmlParser.parse(file)
          loadProjectLibraries(librariesComponent)
        }
      }
    }

    def modulesXml = new File(dir, "modules.xml")
    if (modulesXml.exists()) {
      Node modulesXmlRoot = xmlParser.parse(modulesXml)
      loadModules(modulesXmlRoot.component[0])
    }
    else {
      errorReporter.error("Cannot find modules.xml in $dir")
    }

    def uiDesignerXml = new File(dir, "uiDesigner.xml")
    if (uiDesignerXml.exists()) {
      loadUiDesignerConfiguration(xmlParser.parse(uiDesignerXml))
    }

    def artifactsFolder = new File(dir, "artifacts")
    if (artifactsFolder.isDirectory()) {
      artifactsFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          def artifactsComponent = xmlParser.parse(file)
          loadArtifacts(artifactsComponent)
        }
      }
    }

    def runConfFolder = new File(dir, "runConfigurations")
    if (runConfFolder.isDirectory()) {
      runConfFolder.eachFile {File file ->
        if (isXmlFile(file)) {
          def runConfManager = xmlParser.parse(file);
          loadRunConfigurations(runConfManager);
        }
      }
    }
  }

  def getDirectoryBaseProjectName(File dir) {
    File nameFile = new File(dir, ".name")
    if (nameFile.isFile()) {
      return FileUtil.loadFile(nameFile).trim()
    }
    return StringUtil.replace(dir.parentFile.name, ":", "")
  }

  boolean isXmlFile(File file) {
    return file.isFile() && StringUtil.endsWithIgnoreCase(file.name, ".xml")
  }

  private def loadWorkspaceConfiguration(File workspaceFile) {
    if (!workspaceFile.exists()) return

    def root = xmlParser.parse(workspaceFile)
    def options = loadOptions(getComponent(root, "CompilerWorkspaceConfiguration"))
    // compatibility: in older projects this setting was stored in workspace
    if (project.compilerConfiguration.addNotNullAssertions == true) { // if is the same as default value
      project.compilerConfiguration.addNotNullAssertions = parseBoolean(options["ASSERT_NOT_NULL"], true);
    }
    project.compilerConfiguration.clearOutputDirectoryOnRebuild = parseBoolean(options["CLEAR_OUTPUT_DIRECTORY"], true);
  }

  private def loadCompilerConfiguration(Node root) {
    def rawPatterns = []
    def includePatterns = []
    def excludePatterns = []
    def componentTag = getComponent(root, "CompilerConfiguration")

    componentTag?.wildcardResourcePatterns?.getAt(0)?.entry?.each {Node entryTag ->
      String pattern = entryTag."@name"
      rawPatterns << pattern;
      if (pattern.startsWith("!")) {
        excludePatterns << convertPattern(pattern.substring(1))
      }
      else {
        includePatterns << convertPattern(pattern)
      }
    }
    CompilerConfiguration configuration = project.compilerConfiguration
    if (!includePatterns.isEmpty() || !excludePatterns.isEmpty()) {
      configuration.resourceIncludePatterns = includePatterns
      configuration.resourceExcludePatterns = excludePatterns
    }
    if (!rawPatterns.isEmpty()) {
      configuration.resourcePatterns = rawPatterns;
    }

    def excludesTag = componentTag?.excludeFromCompile?.getAt(0)
    if (excludesTag != null) {
      excludesTag.file?.each {
        configuration.excludes.addExcludedFile(getFileByUrl(it."@url"))
      }
      excludesTag.directory?.each {
        configuration.excludes.addExcludedDirectory(getFileByUrl(it."@url"), Boolean.parseBoolean(it."@includeSubdirectories"))
      }
    }

    configuration.javacOptions.putAll(loadOptions(getComponent(root, "JavacSettings")))

    def annotationProcessingTag = componentTag?.annotationProcessing
    if (annotationProcessingTag != null) {
      configuration.annotationProcessing.enabled = parseBoolean(annotationProcessingTag."@enabled", false)
      configuration.annotationProcessing.obtainProcessorsFromClasspath = parseBoolean(annotationProcessingTag."@useClasspath", true)
      List<String> processorPaths = []
      annotationProcessingTag.processorPath?.each {
        processorPaths << projectMacroExpander.expandMacros(it."@value")
      }
      configuration.annotationProcessing.processorsPath = processorPaths.join(File.pathSeparator)
      annotationProcessingTag.processor?.each {
        configuration.annotationProcessing.processorsOptions[it."@name"] = it."@options" ?: ""
      }
      annotationProcessingTag.processModule?.each {
        configuration.annotationProcessing.processModule[it."@name"] = it."@generatedDirName"
      }
    }

    def targetLevels = componentTag?.bytecodeTargetLevel
    if (!targetLevels.isEmpty()) {
      def targetLevelTag = targetLevels.first()
      configuration.bytecodeTarget.projectBytecodeTarget = targetLevelTag."@target" ?: null;
      targetLevelTag.module?.each {
        configuration.bytecodeTarget.modulesBytecodeTarget[it."@name"] = it."@target" ?: ""
      }
    }

    def addNotNullTag = componentTag?.addNotNullAssertions
    if (addNotNullTag != null) {
      project.compilerConfiguration.addNotNullAssertions = parseBoolean(addNotNullTag."@enabled", true);
    }
  }

  private def loadUiDesignerConfiguration(Node root) {
    def options = loadOptions(getComponent(root, "uidesigner-configuration"))
    project.uiDesignerConfiguration.copyFormsRuntimeToOutput = parseBoolean(options["COPY_FORMS_RUNTIME_TO_OUTPUT"], true)
  }

  private File getFileByUrl(final String url) {
    return new File(FileUtil.toCanonicalPath(projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url))))
  }

  private static boolean parseBoolean(Object value, boolean defaultValue) {
    if (value instanceof NodeList) {
      if (value.isEmpty()) return defaultValue
      value = value[0]
    }
    return value != null ? Boolean.parseBoolean((String)value) : defaultValue
  }
  
  private Map<String, String> loadOptions(Node optionsTag) {
    def result = new HashMap<String, String>()
    optionsTag?.option?.each {Node optionTag ->
      result[optionTag."@name"] = optionTag."@value";
    }
    return result
  }

  private String convertPattern(String pattern) {
    if (pattern.indexOf('/') == -1) {
      return "**/" + pattern
    }
    if (pattern.startsWith("/")) {
      return pattern.substring(1)
    }
    return pattern
  }

  private def loadProjectJdkAndOutput(Node root) {
    def componentTag = getComponent(root, "ProjectRootManager")
    def sdkName = componentTag?."@project-jdk-name"
    def sdk = project.sdks[sdkName]
    if (sdk == null) {
      errorReporter.info("Project SDK '$sdkName' is not defined. Embedded javac will be used")
    }
    def outputTag = componentTag?.output?.getAt(0)
    String outputPath = outputTag != null ? IdeaProjectLoadingUtil.pathFromUrl(outputTag.'@url') : null
    projectOutputPath = outputPath != null && outputPath.length() > 0 ? FileUtil.toCanonicalPath(projectMacroExpander.expandMacros(outputPath)) : null
    project.projectSdk = sdk
    projectLanguageLevel = componentTag?."@languageLevel"
  }

  private def loadProjectFileEncodings(Node root) {
    def componentTag = getComponent(root, "Encoding");
    if (componentTag == null) return;
    componentTag.file?.each {Node fileNode ->
      String url = fileNode."@url";
      String charset = fileNode."@charset";

      if (!StringUtil.isEmptyOrSpaces(charset)) {
        if ("PROJECT".equals(url)) {
          project.projectCharset = charset;
        }
        else {
          def path = projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url));
          project.filePathToCharset[FileUtil.toCanonicalPath(path)] = charset;
        }
      }
    }
  }

  private NodeList loadProjectLibraries(Node librariesComponent) {
    return (NodeList)librariesComponent?.library?.each {Node libTag ->
      initLibrary(libTag, projectMacroExpander, project.createLibrary(libTag."@name", Closure.IDENTITY))
    }
  }

  def loadArtifacts(Node artifactsComponent) {
    if (artifactsComponent == null) return;
    ArtifactLoader artifactLoader = new ArtifactLoader(project, projectMacroExpander, errorReporter)
    artifactsComponent.artifact.each {Node artifactTag ->
      def artifactName = artifactTag."@name"
      def outputPath = projectMacroExpander.expandMacros(artifactTag."output-path"[0]?.text())
      def root = artifactLoader.loadLayoutElement(artifactTag.root[0], artifactName)
      def options = artifactLoader.loadOptions(artifactTag, artifactName)
      def artifact = new Artifact(name: artifactName, rootElement: root, outputPath: outputPath, properties: options);
      project.artifacts[artifact.name] = artifact;
    }
  }

  def loadRunConfigurations(Node runConfManager) {
{'fraction_non_alphanumeric': 0.10529536700385184, 'fraction_numerical': 0.0011519493142301738, 'mean_word_length': 3.9316527605183738, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 40}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '372893', 'n_tokens_mistral': 7911, 'n_tokens_neox': 7700, 'n_words': 1516}