package com.intellij.javascript.flex.maven; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.project.*; import org.jetbrains.idea.maven.utils.MavenProcessCanceledException; import org.jetbrains.idea.maven.utils.MavenProgressIndicator; import org.jetbrains.idea.maven.utils.MavenUtil; import java.io.IOException; import java.util.Collection; import static com.intellij.flex.build.FlexCompilerConfigFileUtilBase.FLEX_CONFIG; import static com.intellij.flex.build.FlexCompilerConfigFileUtilBase.PATH_ELEMENT; import static com.intellij.lang.javascript.flex.build.FlexCompilerConfigFileUtil.FILE_SPECS; import static com.intellij.lang.javascript.flex.build.FlexCompilerConfigFileUtil.OUTPUT; public class RuntimeModulesGenerateConfigTask extends MavenProjectsProcessorBasicTask { static class RLMInfo { final String myRLMName; final String myMainClass; final String myMainClassRelativePath; final String myOutputFileName; final String myOutputFolderPath; final String myConfigFilePath; RLMInfo(final String RLMName, final String mainClass, final String mainClassRelativePath, final String outputFileName, final String outputFolderPath, final String configFilePath) { myRLMName = RLMName; myMainClass = mainClass; myMainClassRelativePath = mainClassRelativePath; myOutputFileName = outputFileName; myOutputFolderPath = outputFolderPath; myConfigFilePath = configFilePath; } } private final Module myModule; private final String myMainConfigFilePath; private final Collection<RLMInfo> myRlmInfos; // compilation of modules should not overwrtite report files produced by main application compilation so some compiler options should be excluded private static final String[] ELEMENTS_TO_REMOVE = {"dump-config", "link-report", "resource-bundle-list"}; /** * @param rlmInfos Pairs where <b>first</b> is relative path to RLM main file, <b>second</b> is absolute path to its compiler config file */ public RuntimeModulesGenerateConfigTask(final Module module, final MavenProject mavenProject, final MavenProjectsTree mavenProjectsTree, final String mainConfigFilePath, final Collection<RLMInfo> rlmInfos) { super(mavenProject, mavenProjectsTree); myModule = module; myMainConfigFilePath = mainConfigFilePath; myRlmInfos = rlmInfos; } @Override public void perform(final Project project, final MavenEmbeddersManager embeddersManager, final MavenConsole console, final MavenProgressIndicator indicator) throws MavenProcessCanceledException { final Element mainConfigRootElement = getClonedRootElementOfMainConfigFile(myMainConfigFilePath); if (mainConfigRootElement == null) return; for (RLMInfo info : myRlmInfos) { indicator.setText("Generating Flex compiler configuration file for " + info.myRLMName); final String mainClassAbsolutePath = findAbsolutePath(myModule, info.myMainClassRelativePath); changeInputAndOutputFilePaths(mainConfigRootElement, mainClassAbsolutePath, info.myOutputFolderPath + "/" + info.myOutputFileName); removeChildrenWithNames(mainConfigRootElement, ELEMENTS_TO_REMOVE); // TODO: to be fully equivalent to flexmojos we need also to add 'load-externs' parameter to module config file, 'link-report' parameter to main application config file and care about compilation order // and similar but more complicated thing with resource-bundle-list / include-resource-bundle ? try { JDOMUtil.writeDocument(mainConfigRootElement.getDocument(), info.myConfigFilePath, CodeStyleSettingsManager.getSettings(project).getLineSeparator()); } catch (IOException ignored) {/**/} } MavenUtil.invokeAndWaitWriteAction(project, () -> { // need to refresh externally created file for (RLMInfo info : myRlmInfos) { final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(info.myConfigFilePath); if (file != null) { file.refresh(false, false); } } }); } @Nullable private static Element getClonedRootElementOfMainConfigFile(final String filePath) { final VirtualFile configFile = LocalFileSystem.getInstance().findFileByPath(filePath); if (configFile != null) { try { final Document document = JDOMUtil.loadDocument(configFile.getInputStream()); final Element clonedRootElement = document.clone().getRootElement(); if (clonedRootElement.getName().equals(FLEX_CONFIG)) { return clonedRootElement; } } catch (JDOMException ignored) {/*ignore*/} catch (IOException ignored) {/*ignore*/} } return null; } @NotNull private static String findAbsolutePath(final Module module, final String relativeToSourceRoot) { for (final VirtualFile root : ModuleRootManager.getInstance(module).getSourceRoots()) { final VirtualFile file = VfsUtilCore.findRelativeFile(relativeToSourceRoot, root); if (file != null) { return file.getPath(); } } return relativeToSourceRoot; } private static void changeInputAndOutputFilePaths(final Element configElement, final String mainClassAbsolutePath, final String outputFilePath) { Element fileSpecsElement = configElement.getChild(FILE_SPECS, configElement.getNamespace()); if (fileSpecsElement == null) { fileSpecsElement = new Element(FILE_SPECS, configElement.getNamespace()); configElement.addContent(fileSpecsElement); } Element pathElement = fileSpecsElement.getChild(PATH_ELEMENT, fileSpecsElement.getNamespace()); if (pathElement == null) { pathElement = new Element(PATH_ELEMENT, fileSpecsElement.getNamespace()); fileSpecsElement.addContent(pathElement); } pathElement.setText(mainClassAbsolutePath); Element outputElement = configElement.getChild(OUTPUT, configElement.getNamespace()); if (outputElement == null) { outputElement = new Element(OUTPUT, configElement.getNamespace()); configElement.addContent(outputElement); } outputElement.setText(outputFilePath); } private static void removeChildrenWithNames(final Element rootElement, final String[] elementNamesToRemove) { for (final String elementName : elementNamesToRemove) { rootElement.removeChildren(elementName, rootElement.getNamespace()); } } }