News and Notes from the Makers of Nexus | Sonatype Blog

On Maven Model Transforms and C#

Written by Shane Isbell | November 07, 2008

Maven 3.0 does all project model processing on a canonical data format, consisting of nearly 500 property values. Using a canonical data format makes it easy to do bidirectional transforms between the Maven pom and other model types.

To transform from an XML format to the property format, you can take any stream containing data in XML and pass it the static method ModelMarshaller.marshallXmlToModelProperties. In the case of the Visual Studio's C# project file, it would look something like:

("http://microsoft.com/Project", "null")
("http://microsoft.com/Project/ItemGroup", "null")
("http://microsoft.com/Project/ItemGroup/Reference", "null")
("http://microsoft.com/Project/ItemGroup/Reference#property/Include", "NGenerics, Version=1.3.0.0, Culture=neutral, processorArchitecture=MSIL")
("http://microsoft.com/Project/ItemGroup/Reference/SpecificVersion", "False")
("http://microsoft.com/Project/ItemGroup/Reference/HintPath", "..libNGenerics.dll")
("http://microsoft.com/Project/ItemGroup/Reference", "")
("http://microsoft.com/Project/ItemGroup/Reference#property/Include", "System")
("http://microsoft.com/Project/Import", "")
("http://microsoft.com/Project/Import#property/Project", "$(MSBuildBinPath)Microsoft.CSharp.targets")

The next step is to transform from the above properties to the canonical format that Maven understands. This requires creating rules that rename (and possibly reorder) the URI elements of each property. For example, to create the dependency section of the pom we would transform assembly references from the C# project file to a Maven project dependency:

List p = new ArrayList();
p.add(new ModelProperty(ProjectUri.xUri, null));
p.add(new ModelProperty(ProjectUri.Dependencies.xUri, null));
for(ModelProperty mp : modelProperties) {
  if(mp.getUri().equals(CSharpUri.ItemGroup.Reference.include)) {
    p.add(new ModelProperty(ProjectUri.Dependencies.Dependency.xUri, null));
    p.add(new ModelProperty(ProjectUri.Dependencies.Dependency.groupId, mp.getValue()));
    p.add(new ModelProperty(ProjectUri.Dependencies.Dependency.artifactId, mp.getValue()));
  }
}

When the transformed list of properties above are fed back into ModelMarshaller.unmarshalModelPropertiesToXml, we would see an XML snippet similar to

<dependencies>
  <dependency>
    <groupId>System</groupId>
    <artifactId>System</artifactId>
  </dependency>
</dependencies>

In this way, we can construct an entire Maven pom from a C# project file or vice versa. You could even translate a hierarchy of C# project files into a multi-module Maven build and have the Maven project builder process the inheritance and interpolation of the C# project files.

Maven handles transforms similar to a messaging bus. Say you build the bidirectional transforms from VS 2005 project files to the Maven canonical format. Now say someone else builds the transforms for VS 2008 (or 2003) project files. You can now do transforms to and from VS 2005 and 2008 project files with no additional work.