-
Notifications
You must be signed in to change notification settings - Fork 19
Add a velocity template based generator #269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
04f7ef2
40e30c8
f966521
5ba45a2
9210d10
f055226
2a8ee3a
02af0a9
92c6a1d
f9ce870
8072734
ea0cf4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package org.codehaus.modello.maven; | ||
|
||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import java.io.File; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Properties; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apache.maven.plugins.annotations.LifecyclePhase; | ||
import org.apache.maven.plugins.annotations.Mojo; | ||
import org.apache.maven.plugins.annotations.Parameter; | ||
import org.codehaus.modello.plugin.velocity.VelocityGenerator; | ||
|
||
/** | ||
* Creates files from the model using Velocity templates. | ||
* <p> | ||
* This mojo can be given a list of templates and a list of parameters. | ||
* Each template from the {@link #templates} property will be run with the following context: | ||
* <ul> | ||
* <li>{@code version}: the version of the model to generate</li> | ||
* <li>{@code model}: the modello model</li> | ||
* <li>{@code Helper}: a {@link org.codehaus.modello.plugin.velocity.Helper} object instance</li> | ||
* <li>any additional parameters specified using the {@link #params} property</li> | ||
* </ul> | ||
* The output file is controlled from within the template using the {@code #MODELLO-VELOCITY#SAVE-OUTPUT-TO} | ||
* <a href="https://velocity.apache.org/engine/2.3/vtl-reference.html#directives">VTL directive</a>. | ||
* This allows a single template to generate multiple files. For example, the following | ||
* directive will redirect further output from the template to a file named | ||
* {@code org/apache/maven/api/model/Plugin.java} if the variable {@code package} is set to | ||
* {@code org.apache.maven.api.model} and the variable {@code className} is set to {@code Plugin}. | ||
* <p> | ||
* {@code #MODELLO-VELOCITY#SAVE-OUTPUT-TO ${package.replace('.','/')}/${className}.java} | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gnodet nice documentation, I'm starting to understand There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gnodet looking at the only use case I know of https://github.com/apache/maven/tree/master/maven-model/src/main/mdo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are mostly new generators not supported by modello. The v4 model is immutable which is not supported, the v3 wraps the immutable v4 model, which is not supported, the reader returns the v4 immutable model , which is not supported, the transformer did not exist in maven 3.x, and the merger was hand written... So that's not tweaking the modello code, those are really new generators. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, there's still a problem with the current state in that the templates are duplicated instead of being reused. This need to be fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had started working on an IT a few weeks ago, but my laptop died with the unfinished work on it (and I can't even turn that laptop on anymore....). I'll see if I can give in another try in the coming weeks. |
||
@Mojo( name = "velocity", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true ) | ||
public class ModelloVelocityMojo | ||
extends AbstractModelloGeneratorMojo | ||
hboutemy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
/** | ||
* The output directory of the generated files. | ||
*/ | ||
@Parameter( defaultValue = "${project.build.directory}/generated-sources/modello", required = true ) | ||
private File outputDirectory; | ||
|
||
/** | ||
* A list of template files to be run against the loaded modello model. | ||
* Those are {@code .vm} files as described in the | ||
* <a href="https://velocity.apache.org/engine/devel/user-guide.html">Velocity Users Guide</a>. | ||
*/ | ||
@Parameter | ||
private List<File> templates; | ||
|
||
/** | ||
* A list of parameters using the syntax {@code key=value}. | ||
* Those parameters will be made accessible to the templates. | ||
*/ | ||
@Parameter | ||
private List<String> params; | ||
|
||
protected String getGeneratorType() | ||
{ | ||
return "velocity"; | ||
} | ||
|
||
protected void customizeParameters( Properties parameters ) | ||
{ | ||
super.customizeParameters( parameters ); | ||
Map<String, String> params = this.params != null ? this.params.stream().collect( Collectors.toMap( | ||
s -> s.substring( 0, s.indexOf( '=' ) ), s -> s.substring( s.indexOf( '=' ) + 1 ) | ||
) ) : Collections.emptyMap(); | ||
parameters.put( "basedir", Objects.requireNonNull( getBasedir(), "basedir is null" ) ); | ||
Path basedir = Paths.get( getBasedir() ); | ||
gnodet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
parameters.put( VelocityGenerator.VELOCITY_TEMPLATES, templates.stream() | ||
.map( File::toPath ) | ||
.map( basedir::relativize ) | ||
.map( Path::toString ) | ||
.collect( Collectors.joining( "," ) ) ); | ||
parameters.put( VelocityGenerator.VELOCITY_PARAMETERS, params ); | ||
} | ||
|
||
protected boolean producesCompilableResult() | ||
{ | ||
return true; | ||
} | ||
|
||
public File getOutputDirectory() | ||
{ | ||
return outputDirectory; | ||
} | ||
|
||
public void setOutputDirectory( File outputDirectory ) | ||
{ | ||
this.outputDirectory = outputDirectory; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>modello-plugins</artifactId> | ||
<groupId>org.codehaus.modello</groupId> | ||
<version>2.1.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
<artifactId>modello-plugin-velocity</artifactId> | ||
<name>Modello Velocity Plugin</name> | ||
<description> | ||
Modello Velocity Plugin generates files from the Modello model using Velocity templates. | ||
</description> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.codehaus.modello</groupId> | ||
<artifactId>modello-plugin-xml</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.codehaus.plexus</groupId> | ||
<artifactId>plexus-utils</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.velocity</groupId> | ||
<artifactId>velocity-engine-core</artifactId> | ||
<version>2.3</version> | ||
</dependency> | ||
</dependencies> | ||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package org.codehaus.modello.plugin.velocity; | ||
|
||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import java.util.ArrayList; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
|
||
import org.codehaus.modello.ModelloRuntimeException; | ||
import org.codehaus.modello.model.ModelAssociation; | ||
import org.codehaus.modello.model.ModelClass; | ||
import org.codehaus.modello.model.ModelField; | ||
import org.codehaus.modello.model.Version; | ||
import org.codehaus.modello.plugin.AbstractModelloGenerator; | ||
import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata; | ||
import org.codehaus.modello.plugins.xml.metadata.XmlClassMetadata; | ||
import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata; | ||
import org.codehaus.plexus.util.StringUtils; | ||
|
||
/** | ||
* Helper class to use inside velocity templates. | ||
* <p> | ||
* This will be made available using {@code ${Helper}} inside the template. | ||
* For example, the following line will return the list of ancestors for a given modello class: | ||
* </p> | ||
* <p> | ||
* {@code #set ( $ancestors = $Helper.ancestors( $class ) )} | ||
* </p> | ||
*/ | ||
@SuppressWarnings( "unused" ) | ||
public class Helper | ||
{ | ||
private final Version version; | ||
|
||
public Helper( Version version ) | ||
{ | ||
this.version = version; | ||
} | ||
|
||
/** | ||
* Returns the capitalised version of the given string. | ||
*/ | ||
public String capitalise( String str ) | ||
{ | ||
return StringUtils.isEmpty( str ) ? str : Character.toTitleCase( str.charAt( 0 ) ) + str.substring( 1 ); | ||
} | ||
|
||
/** | ||
* Returns the uncapitalised version of the given string. | ||
*/ | ||
public String uncapitalise( String str ) | ||
{ | ||
return StringUtils.isEmpty( str ) ? str : Character.toLowerCase( str.charAt( 0 ) ) + str.substring( 1 ); | ||
} | ||
|
||
/** | ||
* Returns the singular name for the given string. | ||
*/ | ||
public String singular( String str ) | ||
{ | ||
return AbstractModelloGenerator.singular( str ); | ||
} | ||
|
||
/** | ||
* Returns the list of ancestors for the given {@code ModelClass}. | ||
*/ | ||
public List<ModelClass> ancestors( ModelClass clazz ) | ||
{ | ||
List<ModelClass> ancestors = new ArrayList<>(); | ||
for ( ModelClass cl = clazz; cl != null; cl = cl.getSuperClass() != null | ||
? cl.getModel().getClass( cl.getSuperClass(), version ) : null ) | ||
{ | ||
ancestors.add( 0, cl ); | ||
} | ||
return ancestors; | ||
} | ||
|
||
/** | ||
* Returns the {@code XmlClassMetadata} for the given {@code ModelClass}. | ||
*/ | ||
public XmlClassMetadata xmlClassMetadata( ModelClass clazz ) | ||
{ | ||
return (XmlClassMetadata) clazz.getMetadata( XmlClassMetadata.ID ); | ||
} | ||
|
||
/** | ||
* Returns the {@code XmlFieldMetadata} for the given {@code ModelField}. | ||
*/ | ||
public XmlFieldMetadata xmlFieldMetadata( ModelField field ) | ||
{ | ||
return (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); | ||
} | ||
|
||
/** | ||
* Returns the {@code XmlAssociationMetadata} for the given {@code ModelField}. | ||
*/ | ||
public XmlAssociationMetadata xmAssociationMetadata( ModelField field ) | ||
{ | ||
return (XmlAssociationMetadata) ( (ModelAssociation) field ) | ||
.getAssociationMetadata( XmlAssociationMetadata.ID ); | ||
} | ||
|
||
/** | ||
* Checks if the given {@code ModelField} is a flat item. | ||
*/ | ||
public boolean isFlatItems( ModelField field ) | ||
{ | ||
return field instanceof ModelAssociation && xmAssociationMetadata( field ).isFlatItems(); | ||
} | ||
|
||
/** | ||
* Returns a list of all {@code ModelField} for a given {@code ModelClass}. | ||
* The list will contain all fields defined on the class and on its parents, | ||
* excluding any field flagged as being xml transient. | ||
*/ | ||
public List<ModelField> xmlFields( ModelClass modelClass ) | ||
{ | ||
List<ModelClass> classes = new ArrayList<>(); | ||
// get the full inheritance | ||
while ( modelClass != null ) | ||
{ | ||
classes.add( modelClass ); | ||
String superClass = modelClass.getSuperClass(); | ||
if ( superClass != null ) | ||
{ | ||
// superClass can be located outside (not generated by modello) | ||
modelClass = modelClass.getModel().getClass( superClass, version, true ); | ||
} | ||
else | ||
{ | ||
modelClass = null; | ||
} | ||
} | ||
List<ModelField> fields = new ArrayList<>(); | ||
for ( int i = classes.size() - 1; i >= 0; i-- ) | ||
{ | ||
modelClass = classes.get( i ); | ||
Iterator<ModelField> parentIter = fields.iterator(); | ||
fields = new ArrayList<>(); | ||
for ( ModelField field : modelClass.getFields( version ) ) | ||
{ | ||
XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); | ||
if ( xmlFieldMetadata.isTransient() ) | ||
{ | ||
// just ignore xml.transient fields | ||
continue; | ||
} | ||
if ( xmlFieldMetadata.getInsertParentFieldsUpTo() != null ) | ||
{ | ||
// insert fields from parent up to the specified field | ||
boolean found = false; | ||
while ( !found && parentIter.hasNext() ) | ||
{ | ||
ModelField parentField = parentIter.next(); | ||
fields.add( parentField ); | ||
found = parentField.getName().equals( xmlFieldMetadata.getInsertParentFieldsUpTo() ); | ||
} | ||
if ( !found ) | ||
{ | ||
// interParentFieldsUpTo not found | ||
throw new ModelloRuntimeException( "parent field not found: class " | ||
+ modelClass.getName() + " xml.insertParentFieldUpTo='" | ||
+ xmlFieldMetadata.getInsertParentFieldsUpTo() + "'" ); | ||
} | ||
} | ||
fields.add( field ); | ||
} | ||
// add every remaining fields from parent class | ||
while ( parentIter.hasNext() ) | ||
{ | ||
fields.add( parentIter.next() ); | ||
} | ||
} | ||
return fields; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.