Artifact Paths

For an expander, the artifact path and name define where the generated artifact will be located and how it will be named.

Testing the artifact path

To start, look at the {Expander}Test.java file. By default, it will have a test method called test_artifactPath:

@Test
public void test_artifactPath() {
  assertThat(tester.artifactPath(baseModel()),
      isPath("C:/COMPONENT_ROOT/some/file.ext"));
}

The test uses a baseModel method to construct a model to test against. Let’s say we have a dataElement expander, we can then start by defining a data element:

private DataElementComposite baseModel() {
  return specBuilder.buildAndFind(
      component("testComp",
          dataElement("City",
              set("packageName", "net.palver.test"))),
      dataElement("testComp::City"));
}

The buildAndFind() method takes 2 parameters; a specification of the model (here component("testComp", ...)) and a reference to the element you wish to extract from it (dataElement("testComp::City")).

Next, we modify the test to test a relevant path:

@Test
public void test_artifactPath() {
  assertThat(tester.artifactPath(baseModel()),
      isPath("C:/COMPONENT_ROOT/shared/gen/common/src/net/palver/test/CityFoo.java"));
}

If everything is done correctly, the test should fail on a mismatch:

java.lang.AssertionError:
Expected: "a string
C:/COMPONENT_ROOT/logic/gen/common/src/net/palver/test/CityFoo.java"
     but: was "
C:/COMPONENT_ROOT/logic/gen/common/src//"

Defining the artifact path

To define the artifact path and name we edit the {Expander}.xml file.

E.g. we can fix the path to match the test above with the following values:

<artifactName>$dataElement.name$Foo.java</artifactName>
<artifactPath>$componentRoot.directory$/$artifactSubFolders$/$dataElement.packageName;format="toPath"$</artifactPath>

The artifactName and artifactPath are small embedded templates similar to the other templates. They can make use of the following variables:

  • dataElement: Which is the composite of the target element, the name of this variable is based on the element type. (E.g. for a TaskElement expander, this would be taskElement)
  • dataElementContext/expansionContext: A context object of type DataElementExpansionContext with extra information (generally not needed for regular expanders)
  • applicationRoot.directory: The root directory of the application, which resolves to {expansionDirectory}/{application.shortName}
  • componentRoot.directory: The root directory of the component, which resolves to {applicationRoot.directory}/components/{component.name}
  • artifactSubFolders: The sub-folders based on the layer-technology-sourceType settings (E.g. logic/gen/common/src)
  • layerDir: The directory name for the layer. (E.g logic)
  • scriptsRoot.directory: A directory for scripts which resolves to {expansionDirectory}/scripts

Furthermore, you can use formats in the template as follows:

$dataElement.name;format="firstToLower"$

The formats available are:

  • firstToLower: converts the first character to lowerCase
  • firstToUpper: converts the first character to upperCase
  • lower: converts all characters to lowerCase
  • upper converts all characters to upperCase
  • toPath: converts all . characters to /

Advanced usage

Lastly, it’s also possible to define <artifactModifiers> in the xml, which contains a ognl definition of a map with key-value pairs which can be used in the artifactPath and artifactName.

E.g. to remove all vowels from the dataElement name:

<artifactModifiers>#{
    'dataElementNoVowels' : dataElement.name.replaceAll('[aeiouy]', '')
  }
</artifactModifiers>
<artifactName>$dataElementNoVowels$Foo.java</artifactName>
<artifactPath>$componentRoot.directory$/$artifactSubFolders$/$dataElement.packageName;format="toPath"$</artifactPath>

Next

Learn about defining when an expander should be applied with the isApplicable condition