Skip to main content

Mapping Imports

Many generated artifactsArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project. have some form of import or include statements that need to be expanded. This guide explains how to manage imports for some of the standard file types. After that, it explains how to add support for other import strategies.

The syntax used here is introduced in version 2023.1.0 of the mapping xsd, so make sure the xmlns attribute point to this version or one more recent:

<?xml version="1.0" encoding="UTF-8" ?>
<mapping xmlns="https://schemas.normalizedsystems.org/xsd/expanders/2023/1/0/mapping">

Next, we need to add the artifactArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project. definition to the head of the mapping file. This definition defines the importStrategy (see below) and the location of the artifactArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project. currently being generated.

<mapping xmlns="https://schemas.normalizedsystems.org/xsd/expanders/2023/1/0/mapping">
<artifact this="'{artifact}'" importStrategy="{strategy}"/>
info

The this attribute is an expression, so you will need to use quotes when defining a String value.

Java Imports​

For java artifactsArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project., define the importStrategy java and the fully qualified class name of the artifactArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project..

<artifact this="dataElement.qualifiedName + 'Bean'" importStrategy="java"/>

Then, add define classes in uses statements. These statements declare that the defined class is being used and thus needs to be imported.

<uses eval="'jakarta.ejb.Stateless'"/>

You can also define them in a group statement. This can be useful if the imports are only added conditionally.

<group name="importMethod" if="dataElement.getOption('io.import').defined">
<uses eval="'net.democritus.upload.ImportFile'"/>
<uses eval="'net.democritus.upload.ImportResult'"/>
</group>

Or, you can define them in a list statement. This can be useful for classes used by LinkFieldsFieldA property of a DataElement expanded to a database column and user interface input element. or ValueFieldsFieldA property of a DataElement expanded to a database column and user interface input element..

<list name="linkVariables" eval="dataElement.fields" param="field"
filter="field.linkField neq null"
unique="field.linkField.targetElement">
<value name="class" eval="classBuilder.from(field.linkField.targetElement)"/>
<uses eval="field.linkField.targetElement.qualifiedName + 'Local'"/>
</list>

Finally, add @imports to the template. The classes will be de-duplicated and filtered to include only the necessary imports.

BeanExpander.stg
base() ::= <<
package <packageName>;

@imports

@Stateless
public class <dataElement.name>Bean {}
>>

StarshipBean.java
package net.demo;

import jakarta.ejb.Stateless;
import net.democritus.upload.ImportFile;
import net.democritus.upload.ImportResult;
import net.demo.people.SpacePortLocal;

@Stateless
public class StarshipThingBean {}
Static Java Imports

Static imports can be defined by adding type="static" to the uses statement.

  <uses type="static" eval="'net.democritus.sys.NullDataRef.EMPTY_DATA_REF'"/>

CommonJS/AMD imports​

For js artifactsArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project., define the importStrategy amd or commonjs.

<artifact
this="component.name + '/' + dataElement.name.firstToLower
+ '/' + dataElement.name.firstToLower + '-std-edit.js'"
importStrategy="commonjs"/>

AMD dependencies are part of the module header. This is how requirejs modules are intended to be written, but you might run into issues when mixing it with the commonjs notation.

EditDialogExpander.stg
base() ::= <<
@imports

function defineEditDialog() {
}
})
>>






starship-std-edit.js
define([
'require',
'knockout',
'nsx/triggers'
], function (
require,
ko,
Trigger
) {

function defineEditDialog() {
}
})

CommonJS dependencies are imported by calling the require() function.

EditDialogExpander.stg
base() ::= <<
define(function (require) {
@imports

function defineEditDialog() {
}
})
>>
starship-std-edit.js
define(function (require) {
const ko = require('knockout');
const Trigger = require('nsx/triggers');

function defineEditDialog() {
}
})

Define imported artifactsArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project. with uses statements.

<!-- Add ` as {var}` to define the library name. -->
<uses eval="'knockout as ko'"/>
<!-- Optionally place `{` and `}` around the module path. -->
<uses eval="'{nsx/triggers} as ko'"/>
<!-- If no variable name is provided, a name is picked based on the filename. -->
<uses eval="'nsx/nsx-application'"/>

Technology-dependent Imports​

In some cases, you may need to be able to change imports when switching technologies. E.g. some libraries may change packages with a new version. For this, you can add a TechnologyLexicon. A TechnologyLexicon is linked to a technology and will only be activated if that technology is present in the ApplicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. (Program).

It defines a number of CoordinateRedirections, which are used to map usages to the correct coordinate.

<dataResource type="expansionControl::TechnologyLexicon">
<technologyLexicon>
<technology name="JAVA_EE"/>
<coordinateRedirections>
<coordinateRedirection>
<coordinate>jakarta.ejb.EJB</coordinate>
<target>javax.ejb.EJB</target>
</coordinateRedirection>
<coordinateRedirection>
<coordinate>jakarta.ejb.Stateless</coordinate>
<target>javax.ejb.Stateless</target>
</coordinateRedirection>
</coordinateRedirections>
</technologyLexicon>
</dataResource>

Providing new ArtifactImportStrategies​

It is possible to provide more import strategy options. You will need to:

  1. Add a DataResource with the new ArtifactImportStrategy
  2. Implement the ArtifactImportResolver interface to process the ArtifactUsages
  3. Implement the conversion to output for the template in an ArtifactImportsDefinition implementation
<dataResource type="expansionControl::ArtifactImportStrategy">
<artifactImportStrategy name="helloWorld">
<implementation>net.democritus.expander.hello.HelloWorldImportResolver</implementation>
</artifactImportStrategy>
</dataResource>

Then, in the artifactArtifactA build or deployment result, such as a Docker container image or Java archive, produced by a software project. definitions, set the importStrategy to the name of the ArtifactImportStrategy.

<?xml version="1.0" encoding="UTF-8" ?>
<mapping xmlns="https://schemas.normalizedsystems.org/xsd/expanders/2023/1/0/mapping">
<artifact this="'Mark'" importStrategy="helloWorld"/>

<uses eval="'Ben'"/>
<uses eval="neighbour"/>
</mapping>

Import Parameters​

It is possible to define parameters in an imports anchorAnchorA marker within generated source code reserved for adding custom logic during re-expansion.:

@imports(param1, param2=value)

These parameters are passed to the ArtifactImportResolver with the setImportsAnchor() method.

This can be used if you need imports in multiple locations in your template. E.g. in Angular Component files, there are imports for other file modules, and imports in the Component declaration.

// file imports
import { Component } from '@angular/core'
import { CommonModule } from '@angular/common'

@Component({
selector: 'my-component',
standalone: true,
imports: [
// component imports
CommonModule
]
})
export class MyComponent {}

The template uses a parameter to keep the imports separate:

base() ::= <<
@imports

@Component({
selector: 'my-component',
standalone: true,
imports: [
@imports(Component)
]
})
export class MyComponent {}
>>

The ArtifactImportResolver then implements the import resolution depending on the provided parameters.

  public ArtifactImportsDefinition resolve(List<ArtifactUsage> usages) {
if (importsAnchor.hasParam("Component")) {
return resolveComponentImports(usages);
} else {
return resolveEsModuleImports(usages);
}
}