Context:
This proposal is to add support for
- Tranforms between different models
- Mixins, includes
- Well-defined construction rules
- Extending of pom with additional meta-data
Solution
Sorting
The framework assigns each field within the pom a URI key and a value, which may be null. The following is an example of a model property list where the ordering from parent to child model is: [C, B, A]. All processing is done on this canonical model.
The framework would do an initial sort of the concatenated list, with the following rules:
(1) If there is any duplicate URI that does not contain a #collection, only the first URI will be maintained in the list.
(2) If there is any duplicate URI that ends in collection, only the first URI will be maintained in the list.
(3) If a URI contains a #collection in its direct parent of the URI tag, it will be placed directly after the parent.
In the sort, (3) will reverse the dependency list of collections so that the most general model element will be first under the collection. For example, org.apache.maven.dep:artificat-dep:1.4, which is from pom C, would first in the dependency management collection.
Processing collection:
The next step would be to apply a policy for the dependency management URIs:
(1) Extract the list of only URIs that have the root: http://apache.org/model/project/dependencyManagement/dependencies#collection
(2) Reverse the collection so that the list of model properties starts with the most specialized model
(3) Place list into work queue
Work Queue:
(4) Push first set of uris on result stack. Call the set X:
(5) Test against each of the remaining 3 entries: Y[i]
A single scan of the list:
For example:
X would match to the version, artifactId and groupId of Y[2], so the framework would remove all of Y[2] elements from the work queue and push elements of the difference of the set Y[2]/X onto the result stack.
Since Y[2] has the same groupId and artifactId as X, but a different version, Y[2] is tossed off of the work queue, leaving the result stack unchanged.
The final result stack is:
(6) The framework removes the old management dependencies from the model property list and copies in the result stack.
Interpolation
Interpolation comes after sorting and merging the inheritence tree.To interpolate a property, say ${project.artifactId} requires scanning the list of model properties for the URI: http://apache.org/model/project/artifactId and obtaining the value. Thus there is easy mapping between URIs and project properties, without the need of reflection.
(1) Timestamp policy: scan list for values with ${build.timestamp} replacing values
(2) Build property policies:
* ${build.directory}
* ${build.outputDirectory}
* ${build.testOutputDirectory}
* ${build.sourceDirectory}
* ${build.testSourceDirectory}
* ${build.scriptSourceDirectory}
* ${reporting.outputDirectory}
* ${basedir}
First, the values of the respective URIs are resolved. If any of the above property values do not reference another property, then their absolute path is resolved and placed back in as the value. For example:
Uri = http://apache.org/model/project/build/outputDirectory, Value = target
Would resolve to:
Uri = http://apache.org/model/project/build/outputDirectory, Value = C:\project\moduleA\target
If the property references another property then the other property is resolved first.
(3) UserProperty (command line values) policy: scan list for URIs that map to user property values.
(4) Environmental policy: scan list for values containing ${env.*} and replacing them
(5) Model Property policy: scan list for any remaining values containing ${project.*} and replacing them
(6) Illegal values policies: scan list for any illegal values (self-referential)
(7) System property policy
Rules
General Rules:
(1) If it is a singleton URI (one per model) with a value, then the most specialized model value will be used.
(2) If it is a singleton URI node with no value and does not contain #collection in the URI, children will be joined. (A v B)/(A & B). If there are multiple values in the inheritence, the operation is applied from most specialized to least specialized model, meaning most specialized values take precedence.
(3) If it is a singleton URI node with no value but containing #collection in the URI, all values will initially be joined: (A v B). Policies may be applied to handle duplicates.
Policies
(A) Dependencies
(B) Profiles
(C) DependencyManagement
(D) DistributionManagement
(E) Plugin Management
(F) Plugins
Transforming Between Models
Transforming between XML based models involves changing the URIs. For example to change between the canonical model (which is based on the pom) and a C# project file, you would rename the URIs.
You can then have a standard marshaller that knows how to handle mapping of URIs to XML tags do the output of the C# project file. The framework developer's primary responsiblity is to handle manipulating of the model property list.
Since we are using a canonical data model, we can also do transforms between any models. For example, if say there exists bidirectional transforms from
(1) Model A to canonical model
(2) Model B to canonical model
and then I add my own
(3) model C to canonical model
then I can do transforms A<>C, B<>C, even though I know nothing about A and B models.
Extending Models
You can easily add information to the canonical model by defining new URIs. For example, we could introduce toolchains:
Since the standard pom model transformer doesn't recognize the toolchain URI, it ignores it, keeping compatibility. However, an extended pom model implementation could read the toolchain info and expose it to the application.
Versioning Models
Versioning can be handled between the canonical models by introducing a version with the URIs:
Uri = http://apache.org/model/project/v1
The would allow the framework to easily detect the model and handle the URIs differently.
Prototype
The following is a rough prototype of these concepts: https://svn.apache.org/repos/asf/maven/sandbox/trunk/other/maven-project
