package net.jangaroo.ide.idea.exml; import com.intellij.javaee.ExternalResourceManager; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PairConsumer; import net.jangaroo.exml.api.Exmlc; import net.jangaroo.exml.config.ValidationMode; import net.jangaroo.ide.idea.jps.exml.ExmlcConfigurationBean; import net.jangaroo.utils.CompilerUtils; import org.jdom.Element; import org.jetbrains.idea.maven.importing.FacetImporter; import org.jetbrains.idea.maven.importing.MavenRootModelAdapter; import org.jetbrains.idea.maven.model.MavenPlugin; import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectChanges; import org.jetbrains.idea.maven.project.MavenProjectsProcessorTask; import org.jetbrains.idea.maven.project.MavenProjectsTree; import org.jetbrains.jps.model.java.JavaResourceRootType; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static net.jangaroo.ide.idea.JangarooFacetImporter.EXML_MAVEN_PLUGIN_ARTIFACT_ID; import static net.jangaroo.ide.idea.jps.JpsJangarooSdkType.JANGAROO_GROUP_ID; import static net.jangaroo.ide.idea.JangarooFacetImporter.findDeclaredJangarooPlugin; import static net.jangaroo.ide.idea.jps.util.IdeaFileUtils.toIdeaUrl; import static net.jangaroo.ide.idea.jps.util.IdeaFileUtils.toPath; /** * A Facet-from-Maven Importer for the EXML Facet type. */ public class ExmlFacetImporter extends FacetImporter<ExmlFacet, ExmlFacetConfiguration, ExmlFacetType> { private static final String DEFAULT_EXML_FACET_NAME = "EXML"; public ExmlFacetImporter() { super(JANGAROO_GROUP_ID, EXML_MAVEN_PLUGIN_ARTIFACT_ID, ExmlFacetType.INSTANCE, DEFAULT_EXML_FACET_NAME); } public boolean isApplicable(MavenProject mavenProjectModel) { return findExmlMavenPlugin(mavenProjectModel) != null; } private MavenPlugin findExmlMavenPlugin(MavenProject mavenProjectModel) { return findDeclaredJangarooPlugin(mavenProjectModel, EXML_MAVEN_PLUGIN_ARTIFACT_ID); } protected void setupFacet(ExmlFacet exmlFacet, MavenProject mavenProjectModel) { //System.out.println("setupFacet called!"); } private String getConfigurationValue(MavenProject mavenProjectModel, String configName, String defaultValue) { String value = getConfigurationValue(configName, mavenProjectModel.getPluginConfiguration(JANGAROO_GROUP_ID, EXML_MAVEN_PLUGIN_ARTIFACT_ID)); if (value == null) { value = getConfigurationValue(configName, mavenProjectModel.getPluginGoalConfiguration(JANGAROO_GROUP_ID, EXML_MAVEN_PLUGIN_ARTIFACT_ID, "exml")); } return value == null ? defaultValue : value; } private String getConfigurationValue(String configName, Element compileConfiguration) { if (compileConfiguration != null) { Element compileConfigurationChild = compileConfiguration.getChild(configName); if (compileConfigurationChild != null) { return compileConfigurationChild.getTextTrim(); } } return null; } @Override protected void reimportFacet(IdeModifiableModelsProvider modelsProvider, Module module, MavenRootModelAdapter rootModel, ExmlFacet exmlFacet, MavenProjectsTree mavenTree, MavenProject mavenProjectModel, MavenProjectChanges changes, Map<MavenProject, String> mavenProjectToModuleName, List<MavenProjectsProcessorTask> postTasks) { //System.out.println("reimportFacet called!"); ExmlFacetConfiguration exmlFacetConfiguration = exmlFacet.getConfiguration(); ExmlcConfigurationBean exmlConfig = exmlFacetConfiguration.getState(); exmlConfig.setGeneratedSourcesDirectory(toIdeaUrl(mavenProjectModel.getGeneratedSourcesDirectory(false) + "/joo")); exmlConfig.setGeneratedTestSourcesDirectory(toIdeaUrl(mavenProjectModel.getGeneratedSourcesDirectory(true) + "/joo")); exmlConfig.setGeneratedResourcesDirectory(toIdeaUrl(getTargetOutputPath(mavenProjectModel, "generated-resources"))); String configClassPackage = getConfigurationValue(mavenProjectModel, "configClassPackage", ""); exmlConfig.setConfigClassPackage(configClassPackage); String validationMode = getConfigurationValue(mavenProjectModel, "validationMode", "off"); try { exmlConfig.validationMode = ValidationMode.valueOf(validationMode.toUpperCase()); } catch (IllegalArgumentException e) { Notifications.Bus.notify(new Notification("Maven", "Invalid Jangaroo EXML Configuration", "Illegal value for <validationMode>: '" + validationMode + "' in Maven POM " + mavenProjectModel.getDisplayName() +", falling back to 'off'.", NotificationType.WARNING)); exmlConfig.validationMode = ValidationMode.OFF; } final Map<String, String> resourceMap = getXsdResourcesOfModule(module); ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { ExternalResourceManager externalResourceManager = ExternalResourceManager.getInstance(); for (Map.Entry<String, String> uri2filename : resourceMap.entrySet()) { externalResourceManager.removeResource(uri2filename.getKey()); externalResourceManager.addResource(uri2filename.getKey(), uri2filename.getValue()); } } }); } }); } @Override public void collectSourceRoots(MavenProject mavenProject, PairConsumer<String, JpsModuleSourceRootType<?>> result) { // TODO: peek into Maven config of ext-xml goal! // result.consume("target/generated-sources/joo", JavaSourceRootType.SOURCE); // seems to be built-in result.consume("target/generated-test-sources/joo", JavaSourceRootType.TEST_SOURCE); result.consume("target/generated-resources", JavaResourceRootType.RESOURCE); } private Map<String, String> getXsdResourcesOfModule(Module module) { // Collect the XSD resource mappings of this modules and all its dependent component suites. //System.out.println("Scanning dependencies of " + moduleName + " for component suite XSDs..."); final Map<String, String> resourceMap = new LinkedHashMap<String, String>(); OrderEntry[] orderEntries = ModuleRootManager.getInstance(module).getOrderEntries(); for (OrderEntry orderEntry : orderEntries) { try { if (orderEntry instanceof ModuleOrderEntry) { ExmlcConfigurationBean exmlConfig = ExmlFacet.getExmlConfig(((ModuleOrderEntry)orderEntry).getModule()); mapXsdResources(resourceMap, exmlConfig); } else { String zipFileName = findDependentModuleZipFileName(orderEntry); if (zipFileName != null) { ZipFile zipFile = new ZipFile(zipFileName); String zipFilePath = zipFileName + "!/"; Set<ZipEntry> xsdZipEntries = findXsdZipEntries(zipFile); for (ZipEntry xsdZipEntry : xsdZipEntries) { mapXsdResource(resourceMap, zipFilePath, xsdZipEntry.getName()); } } } } catch (IOException e) { // ignore } } ExmlcConfigurationBean exmlConfig = ExmlFacet.getExmlConfig(module); mapXsdResources(resourceMap, exmlConfig); return resourceMap; } private static void mapXsdResources(Map<String, String> resourceMap, ExmlcConfigurationBean exmlConfig) { if (exmlConfig != null) { String generatedResourcesPath = toPath(exmlConfig.getGeneratedResourcesDirectory()) + File.separator; File generatedResourcesDirectory = new File(generatedResourcesPath); if (generatedResourcesDirectory.exists()) { String[] xsdFiles = generatedResourcesDirectory.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(XSD.FILE_SUFFIX); } }); for (String xsdFile : xsdFiles) { mapXsdResource(resourceMap, generatedResourcesPath, xsdFile); } } } } private static void mapXsdResource(Map<String, String> resourceMap, String path, String xsdFileName) { String namespace = Exmlc.EXML_CONFIG_URI_PREFIX + CompilerUtils.removeExtension(xsdFileName); resourceMap.put(namespace, path + xsdFileName); } private static String findDependentModuleZipFileName(OrderEntry orderEntry) throws IOException { VirtualFile[] files = orderEntry.getFiles(OrderRootType.CLASSES); // check that library is not empty: for (VirtualFile file : files) { // TODO: make it work for classes, not only for jars! String filename = file.getPath(); if (filename.endsWith("!/")) { // it is a jar: return filename.substring(0, filename.length() - "!/".length()); } } return null; } private static Set<ZipEntry> findXsdZipEntries(ZipFile zipFile) throws IOException { // find a *.xsd in jar's root folder: Enumeration<? extends ZipEntry> enumeration = zipFile.entries(); Set<ZipEntry> result = new LinkedHashSet<ZipEntry>(); while (enumeration.hasMoreElements()) { ZipEntry zipEntry = enumeration.nextElement(); if (!zipEntry.isDirectory() && zipEntry.getName().indexOf('/') == -1 && zipEntry.getName().endsWith(".xsd")) { result.add(zipEntry); } } return result; } }