I updating our current project to use the gradle-idea-plugin to generate our
IDEA projects. We needed to insert some custom XML pieces, so I wrote this:
withXml { node ->
def gradleSettings = node.component.find{ i...@name == 'GradleSettings' }
if (!gradleSettings) {
def builder = new NodeBuilder()
node.append(
builder.component(name: 'GradleSettings') {
option(name: 'GRADLE_LIB_NAME', value: 'Gradle at Work')
}
)
}
}
This seems very complicated, and when I started to do a large set of
changes, did not scale well. Here is what I ended up with:
withXml { node ->
def builder = new CreateOrModifyBuilder(node)
builder.component(name: 'GradleSettings') {
option(name: 'GRADLE_LIB_NAME')....@value = 'Gradle at Work'
}
}
This is built around the idea of a "CreateOrModifyBuilder" (I am terrible at
naming things). That class is given below. The thing to note is that
"createNode" will either find a node that matches the criteria and return
it, or create such a node. Then the "builder" code can update it. So, the
snippet above means "find or create a component with a "name" attribute
called "GradleSettings". Then find or create an option with name
"GRADLE_LIB_NAME". Whether it already existed or not, set the value to
"Gradle at Work". This is very different behavior than "option(name:
'GRADLE_LIB_NAME', value: 'Gradle at Work')" which would either find or
create that node. Here is what I mean:
Assume the existing XML contained:
<option name="GRADLE_LIB_NAME" value="Whatever"/>
Using the CreateOrModifyBuilder with option(name: 'GRADLE_LIB_NAME')....@value
= 'Gradle at Work' results in
<option name="GRADLE_LIB_NAME" value="Gradle at Work"/>
But using option(name: 'GRADLE_LIB_NAME', value: 'Gradle at Work') results
in
<option name="GRADLE_LIB_NAME" value="Whatever"/>
<option name="GRADLE_LIB_NAME" value="Gradle at Work"/>
In other words, both nodes are in the result XML.
The "Builder" class looks like this:
public class CreateOrModifyBuilder extends BuilderSupport
{
private Node root
public CreateOrModifyBuilder(Node root)
{
this.root = root
}
protected void setParent(Object parent, Object child)
{
}
protected Object createNode(Object name)
{
def node = currentNode."$name"
if (!node)
node = new Node(currentNode, name, new ArrayList());
return node;
}
protected Object createNode(Object name, Object value)
{
throw new Exception("Cannot handle values");
}
protected Object createNode(Object name, Map attributes)
{
def node = currentNode."$name".find { testNode ->
attributes.every { key, value ->
testNode.@"$key" == value
}
}
if (!node)
node = new Node(currentNode, name, attributes, new ArrayList());
return node;
}
protected Object createNode(Object name, Map attributes, Object value)
{
throw new Exception("Cannot handle values");
}
private Node getCurrentNode()
{
return (Node) (getCurrent() ? getCurrent() : root);
}
}
*Does anyone have a better idea for solving this problem? If not, I would
like the gradle-idea-plugin to provide such a builder perhaps via a
"withXMLBuilder" type method. What do you think?*
--
John Murph
Automated Logic Research Team