Intelligent Design drives me nuts lucenebook.com
Jan 05

Syncing up your IntelliJ IML files with Maven and Groovy

Builds, Groovy, Tech Add comments

I often use the Maven IntelliJ plugin to create project files (.ipr/.iml/.iws).

A quick:

% maven idea

does the job nicely.

However, when dependencies change, I don’t want to re-run the idea task, as it wacks a lot of project settings that I may have had. What I really want to do is take the current .iml file, and tweak the classpath which has entries such as:

<orderEntry type="module-library">
<library name="asm">
<CLASSES>
<root url="jar://C:/Documents and Settings/Dion/.maven/repository/asm/jars/asm-1.4.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>

So, I put together a quick groovy script which does just this.

#!/bin/env groovy
# -----------------------------------------------------------------------------
# ideaSync.g: Take a given project.xml and an IntelliJ IDEA .iml file,
#             and sync up the dependencies
# -----------------------------------------------------------------------------

import groovy.util.XmlParser
import java.io.File
import java.io.OutputStream
import java.util.*

import org.dom4j.Document
import org.dom4j.io.XMLWriter
import org.dom4j.io.OutputFormat

# -- Enforce the project.xml and dir
if (args.length < 3) {
println """
Usage: ideaSync.g project.xml
.iml /path/to/maven/repo

e.g. ideaSync.g /foo/project.xml /foo/project.iml /www/adigio/maven
"""
System.exit(1)
}

# -- Variables
projectxml   = args[0]
intellijxml  = args[1]
mavenRepoDir = args[2]
indent       = 0

# -- Open up the IntelliJ
.iml file
intellij = new XmlParser().parse(intellijxml)

# -- Open up the project.xml file and get the dependencies, and create an updated XML node
project = new XmlParser().parse(projectxml)

# -- Build the order entries
orderEntries = []

project.dependencies.dependency.each {
groupId    = it.groupId[0].text()
artifactId = it.artifactId[0].text()
version    = it.version[0].text()

orderEntry = buildOrderEntry(groupId, "jar://${mavenRepoDir}/${groupId}/jars/${artifactId}-${version}.jar!/")

orderEntries += orderEntry
}

# -- Grab Order Entries
intellij.module.component.each { | c |
if (c['@name'] == 'NewModuleRootManager') {
oldOrderEntries = c.findAll { !it.orderEntry }
c = orderEntries
}
}

# -- Print the Header
println ''

outputDOM(intellij)

# -----------------------------------------------------------------------------
#  From the given group ID and URL, return an Order Entry node
# -----------------------------------------------------------------------------
def buildOrderEntry(groupId, url) {
# -- Builder
builder = new groovy.util.NodeBuilder()

return builder.orderEntry(type: 'module-library') {
library(name: groupId) {
CLASSES() {
root(url: "jar://${mavenRepoDir}/${groupId}/jars/${artifactId}-${version}.jar!/")
}
JAVADOC()
SOURCES()
}
}
}

# -----------------------------------------------------------------------------
#  Recursive function which outputs a node, and then child nodes
# -----------------------------------------------------------------------------
def outputDOM(node) {
String tagName = node.name()
Object tagValue = node.value()
boolean hasChildren = false;
String attributes = ""

printIndent()

print "<${tagName}"

# -- Print out any attributes
node.attributes().each { |entry|
attributes = " ${entry.key}=\"${entry.value}\"" + attributes
}
print attributes

if (tagValue instanceof String) {
println "${tagValue}"
hasChildren = true;
}

List children = node.children()

if (children != null && !children.isEmpty()) {
hasChildren = true
}

println( (hasChildren) ? ">" : " />" )

for (child in children) {
indent++
outputDOM(child)
indent--
}

if (hasChildren) {
printIndent()
println ""
}
}

# -----------------------------------------------------------------------------
#  Print out the correct indent
# -----------------------------------------------------------------------------
def printIndent() {
print "  " * indent
}

I was hoping that I could use the NodePrinter that is already in Groovy, but it prints out the nodes like foo() { bar(); …. not in XML.

I think it would be good to have an XMLNodePrinter, or just have a toXML() method in the NodePrinter to do that work, as I think this script follows a common pattern:

  • Suck in an XML document into nodes
  • Munge the nodes in some way
  • Print out the new XML document

I would also really like to add this functionality to the maven plugin itself, so I could:

% maven idea:resynciml

or something like that. I think I will wait for m2 though, and see if I can use something other than Jelly for it? ;)

Comments are closed.