IDEA-100272 Maven: support for implicit properties
authorSergey Evdokimov <sergey.evdokimov@jetbrains.com>
Mon, 12 Aug 2013 09:23:55 +0000 (13:23 +0400)
committerSergey Evdokimov <sergey.evdokimov@jetbrains.com>
Mon, 12 Aug 2013 09:23:55 +0000 (13:23 +0400)
plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenModelPropertiesPatcher.java
plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginDescriptor.java
plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginParamInfo.java
plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPropertiesGenerator.java [new file with mode: 0644]
plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/buildHelper/MavenBuildHelperPropertyGenerator.java [new file with mode: 0644]
plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java
plugins/maven/src/main/resources/META-INF/plugin.xml
plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/MavenBuildHelperPluginTest.groovy [new file with mode: 0644]

index c16b84fba0b0af14a1114aceeebddde1cd2498f3..71243eb945ad61feed52386e8349ccebb0b0f23a 100644 (file)
@@ -1,70 +1,61 @@
 package org.jetbrains.idea.maven.plugins.api;
 
-import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jdom.Element;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.idea.maven.model.MavenPlugin;
+import org.jetbrains.plugins.groovy.util.ClassInstanceCache;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
 
 /**
  * @author Sergey Evdokimov
  */
 public class MavenModelPropertiesPatcher {
 
-  private static volatile Map<String, Map<String, String[]>> ourMap;
-
-  private static Map<String, Map<String, String[]>> getMap() {
-    Map<String, Map<String, String[]>> res = ourMap;
+  /*
+   * Add properties those should be added by plugins.
+   */
+  public static void patch(Properties modelProperties, @Nullable Collection<MavenPlugin> plugins) {
+    if (plugins == null) return;
 
-    if (res == null) {
-      res = new HashMap<String, Map<String, String[]>>();
+    Map<String, Map<String, Map<String, List<MavenPluginDescriptor>>>> map = MavenPluginDescriptor.getDescriptorsMap();
 
-      for (MavenPluginDescriptor pluginDescriptor : MavenPluginDescriptor.EP_NAME.getExtensions()) {
-        if (pluginDescriptor.properties != null && pluginDescriptor.properties.length > 0) {
-          Pair<String, String> pluginId = MavenPluginDescriptor.parsePluginId(pluginDescriptor.mavenId);
+    for (MavenPlugin plugin : plugins) {
+      Map<String, Map<String, List<MavenPluginDescriptor>>> groupMap = map.get(plugin.getArtifactId());
+      if (groupMap != null) {
+        Map<String, List<MavenPluginDescriptor>> goalsMap = groupMap.get(plugin.getGroupId());
 
-          String[] properties = new String[pluginDescriptor.properties.length];
-          for (int i = 0; i < pluginDescriptor.properties.length; i++) {
-            properties[i] = pluginDescriptor.properties[i].name;
-          }
+        patch(modelProperties, goalsMap.get(null), null, plugin.getConfigurationElement(), plugin);
 
-          Map<String, String[]> groupMap = res.get(pluginId.second);// pluginId.second is artifactId
-          if (groupMap == null) {
-            groupMap = new HashMap<String, String[]>();
-            res.put(pluginId.second, groupMap);
+        for (MavenPlugin.Execution execution : plugin.getExecutions()) {
+          for (String goal : execution.getGoals()) {
+            patch(modelProperties, goalsMap.get(goal), goal, execution.getConfigurationElement(), plugin);
           }
-
-          groupMap.put(pluginId.first, properties); // pluginId.first is groupId
         }
       }
-
-      ourMap = res;
     }
-
-    return res;
   }
 
-  /*
-   * Add properties those should be added by plugins.
-   */
-  public static void patch(Properties modelProperties, @Nullable Collection<MavenPlugin> plugins) {
-    if (plugins == null) return;
-
-    Map<String, Map<String, String[]>> map = getMap();
+  private static void patch(Properties modelProperties, @Nullable List<MavenPluginDescriptor> descriptors, @Nullable String goal, Element cfgElement, MavenPlugin plugin) {
+    if (descriptors == null) return;
 
-    for (MavenPlugin plugin : plugins) {
-      Map<String, String[]> groupMap = map.get(plugin.getArtifactId());
-      if (groupMap != null) {
-        String[] properties = groupMap.get(plugin.getGroupId());
-
-        if (properties != null) {
-          for (String property : properties) {
-            if (!modelProperties.containsKey(property)) {
-              modelProperties.setProperty(property, "");
-            }
+    for (MavenPluginDescriptor descriptor : descriptors) {
+      if (descriptor.properties != null) {
+        for (MavenPluginDescriptor.ModelProperty property : descriptor.properties) {
+          if (StringUtil.isNotEmpty(property.name)) {
+            modelProperties.setProperty(property.name, "");
           }
         }
       }
+
+      if (descriptor.propertyGenerator != null) {
+        MavenPropertiesGenerator generator = ClassInstanceCache.getInstance(descriptor.propertyGenerator, descriptor.getLoaderForClass());
+        generator.generate(modelProperties, goal, plugin, cfgElement);
+      }
     }
   }
 
index c0663282a6d34df0569918f10020d339861852eb..56bc0fdf0c33a9d8b5b894293f09b6e809b4f15e 100644 (file)
@@ -18,11 +18,17 @@ package org.jetbrains.idea.maven.plugins.api;
 import com.intellij.openapi.extensions.AbstractExtensionPointBean;
 import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.util.Pair;
+import com.intellij.util.SmartList;
 import com.intellij.util.xml.Required;
 import com.intellij.util.xmlb.annotations.AbstractCollection;
 import com.intellij.util.xmlb.annotations.Attribute;
 import com.intellij.util.xmlb.annotations.Property;
 import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.idea.maven.utils.MavenUtil;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Sergey Evdokimov
@@ -31,6 +37,12 @@ public class MavenPluginDescriptor extends AbstractExtensionPointBean {
 
   public static final ExtensionPointName<MavenPluginDescriptor> EP_NAME = new ExtensionPointName<MavenPluginDescriptor>("org.jetbrains.idea.maven.pluginDescriptor");
 
+  // Map artifactId -> groupId -> goal -> List<MavenPluginDescriptor>
+  public static volatile Map<String, Map<String, Map<String, List<MavenPluginDescriptor>>>> ourDescriptorsMap;
+
+  @Attribute("goal")
+  public String goal;
+
   @Attribute("mavenId")
   @Required
   public String mavenId;
@@ -43,6 +55,9 @@ public class MavenPluginDescriptor extends AbstractExtensionPointBean {
   @AbstractCollection(surroundWithTag = false)
   public ModelProperty[] properties;
 
+  @Attribute("propertyGenerator")
+  public String propertyGenerator;
+
   @Tag("property")
   public static class ModelProperty {
     @Attribute("name")
@@ -57,9 +72,6 @@ public class MavenPluginDescriptor extends AbstractExtensionPointBean {
     @Required
     public String name;
 
-    @Attribute("goal")
-    public String goal;
-
     /**
      * Class name of reference provider. The reference provider must implement MavenParamReferenceProvider or PsiReferenceProvider.
      */
@@ -106,4 +118,30 @@ public class MavenPluginDescriptor extends AbstractExtensionPointBean {
     return new Pair<String, String>(mavenId.substring(0, idx), mavenId.substring(idx + 1));
   }
 
+  public static Map<String, Map<String, Map<String, List<MavenPluginDescriptor>>>> getDescriptorsMap() {
+    Map<String, Map<String, Map<String, List<MavenPluginDescriptor>>>> res = ourDescriptorsMap;
+    if (res == null) {
+      res = new HashMap<String, Map<String, Map<String, List<MavenPluginDescriptor>>>>();
+
+      for (MavenPluginDescriptor pluginDescriptor : MavenPluginDescriptor.EP_NAME.getExtensions()) {
+        Pair<String, String> pluginId = parsePluginId(pluginDescriptor.mavenId);
+
+        Map<String, Map<String, List<MavenPluginDescriptor>>> groupMap = MavenUtil.getOrCreate(res, pluginId.second);// pluginId.second is artifactId
+
+        Map<String, List<MavenPluginDescriptor>> goalsMap = MavenUtil.getOrCreate(groupMap, pluginId.first);// pluginId.first is groupId
+
+        List<MavenPluginDescriptor> descriptorList = goalsMap.get(pluginDescriptor.goal);
+        if (descriptorList == null) {
+          descriptorList = new SmartList<MavenPluginDescriptor>();
+          goalsMap.put(pluginDescriptor.goal, descriptorList);
+        }
+
+        descriptorList.add(pluginDescriptor);
+      }
+
+      ourDescriptorsMap = res;
+    }
+
+    return res;
+  }
 }
index a987c944d8b4729232fcc7dd185cb7670056f672..ba5a944b360c570a72db291e01854e7ccc5f22e9 100644 (file)
@@ -17,6 +17,7 @@ import com.intellij.util.xml.DomManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.idea.maven.dom.model.*;
+import org.jetbrains.idea.maven.utils.MavenUtil;
 
 import java.util.*;
 
@@ -49,18 +50,21 @@ public class MavenPluginParamInfo {
           Map pluginsMap = res;
 
           for (int i = paramPath.length - 1; i >= 0; i--) {
-            pluginsMap = getOrCreate(pluginsMap, paramPath[i]);
+            pluginsMap = MavenUtil.getOrCreate(pluginsMap, paramPath[i]);
           }
 
           ParamInfo paramInfo = new ParamInfo(pluginDescriptor.getPluginDescriptor().getPluginClassLoader(), param);
 
-          Map<String, ParamInfo> goalsMap = getOrCreate(pluginsMap, pluginId);
+          Map<String, ParamInfo> goalsMap = MavenUtil.getOrCreate(pluginsMap, pluginId);
 
-          ParamInfo oldValue = goalsMap.put(param.goal, paramInfo);
+          String goal = pluginDescriptor.goal;
+          assert goal == null || !goal.isEmpty();
+
+          ParamInfo oldValue = goalsMap.put(goal, paramInfo);
           if (oldValue != null) {
             LOG.error("Duplicated maven plugin parameter descriptor: "
                       + pluginId.first + ':' + pluginId.second + " -> "
-                      + (param.goal != null ? "[" + param.goal + ']' : "") + param.name);
+                      + (goal != null ? "[" + goal + ']' : "") + param.name);
           }
         }
       }
@@ -71,17 +75,6 @@ public class MavenPluginParamInfo {
     return res;
   }
 
-  @NotNull
-  private static <K, V extends Map> V getOrCreate(Map map, K key) {
-    Map res = (Map)map.get(key);
-    if (res == null) {
-      res = new HashMap();
-      map.put(key, res);
-    }
-
-    return (V)res;
-  }
-
   public static boolean isSimpleText(@NotNull XmlText paramValue) {
     PsiElement prevSibling = paramValue.getPrevSibling();
     if (!(prevSibling instanceof LeafPsiElement) || ((LeafPsiElement)prevSibling).getElementType() != XmlTokenType.XML_TAG_END) {
diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPropertiesGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPropertiesGenerator.java
new file mode 100644 (file)
index 0000000..46b149a
--- /dev/null
@@ -0,0 +1,20 @@
+package org.jetbrains.idea.maven.plugins.api;
+
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.model.MavenPlugin;
+
+import java.util.Properties;
+
+/**
+ * @author Sergey Evdokimov
+ */
+public abstract class MavenPropertiesGenerator {
+
+  public abstract void generate(@NotNull Properties modelProperties,
+                                @Nullable String goal,
+                                @NotNull MavenPlugin plugin,
+                                @Nullable Element cfgElement);
+
+}
diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/buildHelper/MavenBuildHelperPropertyGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/buildHelper/MavenBuildHelperPropertyGenerator.java
new file mode 100644 (file)
index 0000000..50f13d0
--- /dev/null
@@ -0,0 +1,34 @@
+package org.jetbrains.idea.maven.plugins.buildHelper;
+
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.model.MavenPlugin;
+import org.jetbrains.idea.maven.plugins.api.MavenPropertiesGenerator;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author Sergey Evdokimov
+ */
+public class MavenBuildHelperPropertyGenerator extends MavenPropertiesGenerator {
+  @Override
+  public void generate(@NotNull Properties modelProperties,
+                       @Nullable String goal,
+                       @NotNull MavenPlugin plugin,
+                       @Nullable Element cfgElement) {
+    if (cfgElement == null) return;
+
+    Element portNames = cfgElement.getChild("portNames");
+    if (portNames == null) return;
+
+    List<Element> portName = portNames.getChildren("portName");
+    for (Element element : portName) {
+      String propertyName = element.getTextTrim();
+      if (!propertyName.isEmpty()) {
+        modelProperties.setProperty(propertyName, "");
+      }
+    }
+  }
+}
index ad2270960dbf455bc7e701fd261fbab73ae04cc5..1aa0de6dea835f5514f98154e911a56e933be26e 100644 (file)
@@ -906,4 +906,16 @@ public class MavenUtil {
 
     return ModuleRootManager.getInstance(module).getSdk();
   }
+
+  @NotNull
+  public static <K, V extends Map> V getOrCreate(Map map, K key) {
+    Map res = (Map)map.get(key);
+    if (res == null) {
+      res = new HashMap();
+      map.put(key, res);
+    }
+
+    return (V)res;
+  }
+
 }
index 07fb2430e032f97bcfc2d180b2acbafc6daa8ac0..a5aab8ba17379c36349fe4a2a2efdf4b3fa93ddf 100644 (file)
       <property name="timestamp"/>
     </pluginDescriptor>
 
+    <pluginDescriptor mavenId="org.codehaus.mojo:build-helper-maven-plugin" goal="reserve-network-port"
+                      propertyGenerator="org.jetbrains.idea.maven.plugins.buildHelper.MavenBuildHelperPropertyGenerator"/>
+
     <pluginDescriptor mavenId="org.codehaus.mojo:sql-maven-plugin">
       <param name="sqlCommand" language="SQL"/>
     </pluginDescriptor>
diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/MavenBuildHelperPluginTest.groovy b/plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/MavenBuildHelperPluginTest.groovy
new file mode 100644 (file)
index 0000000..400c259
--- /dev/null
@@ -0,0 +1,102 @@
+package org.jetbrains.idea.maven.plugins
+
+import org.jetbrains.idea.maven.dom.MavenDomTestCase
+
+/**
+ * @author Sergey Evdokimov
+ */
+class MavenBuildHelperPluginTest extends MavenDomTestCase {
+
+  public void testCompletion() {
+    importProject("""
+<groupId>test</groupId>
+<artifactId>project</artifactId>
+<version>1</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>yyy</id>
+            <goals>
+              <goal>reserve-network-port</goal>
+            </goals>
+            <configuration>
+              <portNames>
+                <portName>someNewProperty1</portName>
+                <portName>someNewProperty2</portName>
+              </portNames>
+            </configuration>
+          </execution>
+          <execution>
+            <id>xxx</id>
+            <goals>
+              <goal>foo</goal>
+            </goals>
+            <configuration>
+              <portNames>
+                <portName>someNewProperty3</portName>
+              </portNames>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <properties>
+    <aaa>\${someNew}</aaa>
+  </properties>
+""")
+
+    createProjectPom("""
+<groupId>test</groupId>
+<artifactId>project</artifactId>
+<version>1</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>yyy</id>
+            <goals>
+              <goal>reserve-network-port</goal>
+            </goals>
+            <configuration>
+              <portNames>
+                <portName>someNewProperty1</portName>
+                <portName>someNewProperty2</portName>
+              </portNames>
+            </configuration>
+          </execution>
+          <execution>
+            <id>xxx</id>
+            <goals>
+              <goal>foo</goal>
+            </goals>
+            <configuration>
+              <portNames>
+                <portName>someNewProperty3</portName>
+              </portNames>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <properties>
+    <aaa>\${someNew<caret>}</aaa>
+  </properties>
+""")
+
+    assertCompletionVariants(myProjectPom, "someNewProperty1", "someNewProperty2")
+  }
+
+}