18

I'm creating a maven archetype and in the projects generated a want a class that is named after the artifact id of the generated project.

The artifact id will be formatted like: the-project-name and the class should be named TheProjectNameMain.

I've tried to do this in my archetype-metadata.xml but I can't get it right.

<archetype-descriptor>
    <requiredProperties>
        <requiredProperty key="classNamePrefix">
            <defaultValue>${WordUtils.capitalize(artifactId.replaceAll("-", " ")).replaceAll(" ", "")}</defaultValue>
        </requiredProperty>        
    </requiredProperties>
</archetype-descriptor>

As you can see i tried to use WordUtils (from apache-commons) but i'm guessing this is not available because i'm getting an error. Error merging velocity templates:.... . I also tried different combinations of .replaceAll but i couldn't get the right format.

Does anyone know of a way to go from a-hypenated-string to a CamelCaseClassName in this case?

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
Bob Brinks
  • 1,372
  • 1
  • 10
  • 19
  • 3
    [similar question here](http://stackoverflow.com/questions/16701772/how-can-i-provide-custom-logic-in-a-maven-archetype). I'm very interested. – logoff Apr 28 '14 at 10:56

3 Answers3

23

There is no access to arbitrary java classes from Velocity, but you can call methods of existing objects. In the context of Maven archetype you can use methods from java.lang.String and a Velocity loop to get the job done.

#macro( ccase $str )
#foreach( $word in $str.split('-') )$word.substring(0,1).toUpperCase()$word.substring(1)#end
#end
#set( $classNamePrefix = "#ccase( $artifactId )" )

public class ${classNamePrefix}Application {
    // ...
}

If you are using a fileSet tag, add filtered="true" property to make sure source files are processed with Velocity.

See also:

There's updated documentation for version 2.0: http://velocity.apache.org/engine/2.0/user-guide.html#loops

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
anttix
  • 7,709
  • 1
  • 24
  • 25
  • 1
    This works well for the classname in the file but I still have to manually change the filename. I used `__classNamePrefix__` in the filename. This is not a big issue as Intellij automaticly tries to correct it, but it would be better if this step was automated by maven. Any ideas? – Bob Brinks May 05 '14 at 08:34
  • Velocity directives do not work in archetype-metadata.xml and using that file is the only way to change generated file names that I am aware of. You may try to prompt user for the prefix by creating a required property that has no default value if that's acceptable. – anttix May 05 '14 at 18:35
  • After generating the class, you could create a `META-INF/archetype-post-generate.groovy` script, as stated in [this post](https://stackoverflow.com/questions/19564804/is-there-a-way-to-post-process-project-generated-from-archetype), and rename the file. – flags Nov 14 '18 at 17:01
10

I wanted to be able to do this in file names, so I came up with a hack for a camel case artifactId property:

<requiredProperty key="artifactIdCamelCase">
  <defaultValue>${artifactId.replaceAll("^a|-a", "A").replaceAll("^b|-b", "B").replaceAll("^c|-c", "C").replaceAll("^d|-d", "D").replaceAll("^e|-e", "E").replaceAll("^f|-f", "F").replaceAll("^g|-g", "G").replaceAll("^h|-h", "H").replaceAll("^i|-i", "I").replaceAll("^j|-j", "J").replaceAll("^k|-k", "K").replaceAll("^l|-l", "L").replaceAll("^m|-m", "M").replaceAll("^n|-n", "N").replaceAll("^o|-o", "O").replaceAll("^p|-p", "P").replaceAll("^q|-q", "Q").replaceAll("^r|-r", "R").replaceAll("^s|-s", "S").replaceAll("^t|-t", "T").replaceAll("^u|-u", "U").replaceAll("^v|-v", "V").replaceAll("^w|-w", "W").replaceAll("^x|-x", "X").replaceAll("^y|-y", "Y").replaceAll("^z|-z", "Z")}</defaultValue>
</requiredProperty>

That will convert any artifactId that follows the naming convention of hyphenated, lower case a-z to camel case. From some limited testing, the format is fragile so small edits such as line breaks and regex additions may stop Velocity from replacing the property.

Should work on Maven versions after 2.1 (when this bug was fixed). My version:

> mvn -v
Apache Maven 3.2.5 (12a6b3acb947671f09b81f49094c53f426d8cea1; 2014-12-14T11:29:23-06:00)

Also here is a sometimes-useful, unhyphenated version of artifactId:

<requiredProperty key="artifactIdUnhyphenated">
  <defaultValue>${artifactId.replace("-","")}</defaultValue>
</requiredProperty>
Matt Eckert
  • 1,946
  • 14
  • 16
  • 3
    Very strange - When running archetype:generate with -DartifactId=... it doesn't work anymore, because the string transformation is applied on the archetype's (!) artifact id (I.e. for -DartifactId=my-project artifactIdCamelCase = "Myartifact" instead of "MyProject"). This strange behavior does not occur if I specify the artifact ID later, when maven asks for it. – fishbone Nov 13 '15 at 11:03
  • @fishbone I had the same issue. Did you finally fix it? – Jian Chen Dec 09 '18 at 00:02
5

To variabilize your file name, you can set a requiredProperty and use other properties to build it

<requiredProperty key="routeBuilderFileName"><defaultValue>RouteBuilder${flowName.toUpperCase()}${flowWay.substring(0,1).toUpperCase()}${flowWay.substring(1)}</defaultValue></requiredProperty>

Then name the template file : __routeBuilderFileName__.java

yodamad
  • 1,452
  • 14
  • 24
  • 1
    Unfortunately it doesn't work when the base name (`flowName` in this example) is provided in command line (`-DflowName=something`). You must provide it in interactive mode - then it will work. – Furgas Apr 29 '16 at 12:05
  • You can use the artifactId for instance instead of flowName – Youssef NAIT Jan 04 '19 at 13:47