package ca.uhn.fhir.tinder;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.http.ParseException;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import ca.uhn.fhir.tinder.AbstractGenerator.ExecutionException;
import ca.uhn.fhir.tinder.AbstractGenerator.FailureException;
import ca.uhn.fhir.tinder.GeneratorContext.ProfileFileDefinition;
import ca.uhn.fhir.tinder.TinderStructuresMojo.ValueSetFileDefinition;
import ca.uhn.fhir.tinder.parser.DatatypeGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.ProfileParser;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.TargetType;
/**
* Generate files from FHIR resource/composite metadata using Velocity templates.
* <p>
* Generates either source or resource files for each selected resource or
* composite data type. One file is generated for each selected entity. The
* files are generated using a Velocity template that can be taken from
* inside the hapi-timder-plugin project or can be located in other projects
* <p>
* The following Maven plug-in configuration properties are used with this plug-in
* <p>
* <table border="1" cellpadding="2" cellspacing="0">
* <tr>
* <td valign="top"><b>Attribute</b></td>
* <td valign="top"><b>Description</b></td>
* <td align="center" valign="top"><b>Required</b></td>
* </tr>
* <tr>
* <td valign="top">version</td>
* <td valign="top">The FHIR version whose resource metadata
* is to be used to generate the files<br>
* Valid values: <code><b>dstu</b></code> | <code><b>dstu2</b></code> | <code><b>dstu3</b></code></td>
* <td valign="top" align="center">Yes</td>
* </tr>
* <tr>
* <td valign="top">baseDir</td>
* <td valign="top">The Maven project's base directory. This is used to
* possibly locate other assets within the project used in file generation.</td>
* <td valign="top" align="center">No. Defaults to: <code>${project.build.directory}/..</code></td>
* </tr>
* <tr>
* <td valign="top">generateResources</td>
* <td valign="top">Should files be generated from FHIR resource metadata?<br>
* Valid values: <code><b>true</b></code> | <code><b>false</b></code></td>
* <td valign="top" align="center" rowspan="4">One of these four options must be specified</td>
* </tr>
* <tr>
* <td valign="top">generateDataTypes</td>
* <td valign="top">Should files be generated from FHIR composite data type metadata?<br>
* Valid values: <code><b>true</b></code> | <code><b>false</b></code></td>
* </tr>
* <tr>
* <td valign="top">generateValueSets</td>
* <td valign="top">Should files be generated from FHIR value set metadata?<br>
* Valid values: <code><b>true</b></code> | <code><b>false</b></code></td>
* </tr>
* <tr>
* <td valign="top">generateProfiles</td>
* <td valign="top">Should files be generated from FHIR profile metadata?<br>
* Valid values: <code><b>true</b></code> | <code><b>false</b></code></td>
* </tr>
* <tr>
* <td colspan="3" />
* </tr>
* <tr>
* <td valign="top" colspan="3">Java source files can be generated
* for FHIR resources or composite data types. There is one file
* generated for each selected entity. The following configuration
* properties control the naming of the generated source files:<br>
* <targetSourceDirectory>/<targetPackage>/<filenamePrefix><i>element-name</i><filenameSuffix><br>
* where: <i>element-name</i> is the "title-case" name of the selected resource or composite data type.<br>
* Note that all dots in the targetPackage will be replaced by the path separator character when building the
* actual source file location. Also note that <code>.java</code> will be added to the filenameSuffix if it is not already included.
* </td>
* </tr>
* <tr>
* <td valign="top">targetSourceDirectory</td>
* <td valign="top">The Maven source directory to contain the generated files.</td>
* <td valign="top" align="center">Yes when Java source files are to be generated</td>
* </tr>
* <tr>
* <td valign="top">targetPackage</td>
* <td valign="top">The Java package that will contain the generated classes.
* This package is generated in the <targetSourceDirectory> if needed.</td>
* <td valign="top" align="center">Yes when <i>targetSourceDirectory</i> is specified</td>
* </tr>
* <tr>
* <td valign="top">filenamePrefix</td>
* <td valign="top">The prefix string that is to be added onto the
* beginning of the resource or composite data type name to become
* the Java class name or resource file name.</td>
* <td valign="top" align="center">No</td>
* </tr>
* <tr>
* <td valign="top">filenameSuffix</td>
* <td valign="top">Suffix that will be added onto the end of the resource
* or composite data type name to become the Java class name or resource file name.</td>
* <td valign="top" align="center">No.</code></td>
* </tr>
* <tr>
* <td colspan="3" />
* </tr>
* <tr>
* <td valign="top" colspan="3">Maven resource files can also be generated
* for FHIR resources or composite data types. The following configuration
* properties control the naming of the generated resource files:<br>
* <targetResourceDirectory>/<targetFolder>/<filenamePrefix><i>element-name</i><filenameSuffix><br>
* where: <i>element-name</i> is the "title-case" name of the selected resource or composite data type.
* </td>
* </tr>
* <tr>
* <td valign="top">targetResourceDirectory</td>
* <td valign="top">The Maven resource directory to contain the generated files.</td>
* <td valign="top" align="center">Yes when resource files are to be generated</td>
* </tr>
* <tr>
* <td valign="top">targetFolder</td>
* <td valign="top">The folder within the targetResourceDirectory where the generated files will be placed.
* This folder is generated in the <targetResourceDirectory> if needed.</td>
* <td valign="top" align="center">No</td>
* </tr>
* <tr>
* <td colspan="3" />
* </tr>
* <tr>
* <td valign="top">template</td>
* <td valign="top">The path of one of the <i>Velocity</i> templates
* contained within the <code>hapi-tinder-plugin</code> Maven plug-in
* classpath that will be used to generate the files.</td>
* <td valign="top" align="center" rowspan="2">One of these two options must be configured</td>
* </tr>
* <tr>
* <td valign="top">templateFile</td>
* <td valign="top">The full path to the <i>Velocity</i> template that is
* to be used to generate the files.</td>
* </tr>
* <tr>
* <td valign="top">velocityPath</td>
* <td valign="top">When using the <code>templateFile</code> option, this property
* can be used to specify where Velocity macros and other resources are located.</td>
* <td valign="top" align="center">No. Defaults to same directory as the template file.</td>
* </tr>
* <tr>
* <td valign="top">velocityProperties</td>
* <td valign="top">Specifies the full path to a java properties file
* containing Velocity configuration properties</td>
* <td valign="top" align="center">No.</td>
* </tr>
* <tr>
* <td valign="top">includeResources</td>
* <td valign="top">A list of the names of the resources or composite data types that should
* be used in the file generation</td>
* <td valign="top" align="center">No. Defaults to all defined resources except for DSTU2,
* the <code>Binary</code> resource is excluded and
* for DSTU3, the <code>Conformance</code> resource is excluded.</td>
* </tr>
* <tr>
* <td valign="top">excludeResources</td>
* <td valign="top">A list of the names of the resources or composite data types that should
* excluded from the file generation</td>
* <td valign="top" align="center">No.</td>
* </tr>
* <tr>
* <td valign="top">valueSetFiles</td>
* <td valign="top">A list of files containing value-set resource definitions
* to be used.</td>
* <td valign="top" align="center">No. Defaults to all defined value-sets that
* are referenced from the selected resources.</td>
* </tr>
* <tr>
* <td valign="top">profileFiles</td>
* <td valign="top">A list of files containing profile definitions
* to be used.</td>
* <td valign="top" align="center">No. Defaults to the default profile
* for each selected resource</td>
* </tr>
* </table>
*
*
*
* @author Bill.Denton
*
*/
@Mojo(name = "generate-multi-files", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class TinderGenericMultiFileMojo extends AbstractMojo {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TinderGenericMultiFileMojo.class);
@Parameter(required = true)
private String version;
@Parameter(required = true, defaultValue = "${project.build.directory}/..")
private String baseDir;
@Parameter(required = false, defaultValue="false")
private boolean generateResources;
@Parameter(required = false, defaultValue = "false")
private boolean generateDatatypes;
@Parameter(required = false, defaultValue = "false")
private boolean generateValueSets;
@Parameter(required = false, defaultValue = "false")
private boolean generateProfiles;
@Parameter(required = false)
private File targetSourceDirectory;
@Parameter(required = false)
private String targetPackage;
@Parameter(required = false)
private String filenamePrefix;
@Parameter(required = false)
private String filenameSuffix;
@Parameter(required = false)
private File targetResourceDirectory;
@Parameter(required = false)
private String targetFolder;
// one of these two is required
@Parameter(required = false)
private String template;
@Parameter(required = false)
private File templateFile;
@Parameter(required = false)
private String velocityPath;
@Parameter(required = false)
private String velocityProperties;
@Parameter(required = false)
private List<String> includeResources;
@Parameter(required = false)
private List<String> excludeResources;
@Parameter(required = false)
private List<ValueSetFileDefinition> valueSetFiles;
@Parameter(required = false)
private List<ProfileFileDefinition> profileFiles;
@Component
private MavenProject myProject;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
GeneratorContext context = new GeneratorContext();
context.setVersion(version);
context.setBaseDir(baseDir);
context.setIncludeResources(includeResources);
context.setExcludeResources(excludeResources);
context.setValueSetFiles(valueSetFiles);
context.setProfileFiles(profileFiles);
Generator generator = new Generator();
try {
generator.prepare(context);
} catch (ExecutionException e) {
throw new MojoExecutionException(e.getMessage(), e.getCause());
} catch (FailureException e) {
throw new MojoFailureException(e.getMessage(), e.getCause());
}
/*
* Deal with the generation target
*/
TargetType targetType = null;
File targetDirectory = null;
if (targetSourceDirectory != null) {
if (targetResourceDirectory != null) {
throw new MojoFailureException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
targetType = TargetType.SOURCE;
if (null == targetPackage) {
throw new MojoFailureException("The [targetPackage] property must be specified when generating Java source code.");
}
targetDirectory = new File(targetSourceDirectory, targetPackage.replace('.', File.separatorChar));
} else
if (targetResourceDirectory != null) {
if (targetSourceDirectory != null) {
throw new MojoFailureException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
targetType = TargetType.RESOURCE;
if (targetFolder != null) {
targetDirectory = new File(targetResourceDirectory, targetFolder);
} else {
targetDirectory = targetResourceDirectory;
}
if (null == targetPackage) {
targetPackage = "";
}
} else {
throw new MojoFailureException("Either [targetSourceDirectory] or [targetResourceDirectory] must be specified.");
}
targetDirectory.mkdirs();
ourLog.info(" * Output ["+targetType.toString()+"] Directory: " + targetDirectory.getAbsolutePath());
/*
* Write resources if selected
*/
ResourceGeneratorUsingSpreadsheet rp = context.getResourceGenerator();
if (generateResources && rp != null) {
ourLog.info("Writing Resources...");
rp.setFilenamePrefix(filenamePrefix);
rp.setFilenameSuffix(filenameSuffix);
rp.setTemplate(template);
rp.setTemplateFile(templateFile);
rp.setVelocityPath(velocityPath);
rp.setVelocityProperties(velocityProperties);
rp.writeAll(targetType, targetDirectory, null, targetPackage);
}
/*
* Write composite datatypes
*/
DatatypeGeneratorUsingSpreadsheet dtp = context.getDatatypeGenerator();
if (generateDatatypes && dtp != null) {
ourLog.info("Writing Composite Datatypes...");
dtp.setFilenamePrefix(filenamePrefix);
dtp.setFilenameSuffix(filenameSuffix);
dtp.setTemplate(template);
dtp.setTemplateFile(templateFile);
dtp.setVelocityPath(velocityPath);
dtp.setVelocityProperties(velocityProperties);
dtp.writeAll(targetType, targetDirectory, null, targetPackage);
}
/*
* Write valuesets
*/
ValueSetGenerator vsp = context.getValueSetGenerator();
if (generateValueSets && vsp != null) {
ourLog.info("Writing ValueSet Enums...");
vsp.setFilenamePrefix(filenamePrefix);
vsp.setFilenameSuffix(filenameSuffix);
vsp.setTemplate(template);
vsp.setTemplateFile(templateFile);
vsp.setVelocityPath(velocityPath);
vsp.setVelocityProperties(velocityProperties);
vsp.writeMarkedValueSets(targetType, targetDirectory, targetPackage);
}
/*
* Write profiles
*/
ProfileParser pp = context.getProfileParser();
if (generateProfiles && pp != null) {
ourLog.info("Writing Profiles...");
pp.setFilenamePrefix(filenamePrefix);
pp.setFilenameSuffix(filenameSuffix);
pp.setTemplate(template);
pp.setTemplateFile(templateFile);
pp.setVelocityPath(velocityPath);
pp.setVelocityProperties(velocityProperties);
pp.writeAll(targetType, targetDirectory, null, targetPackage);
}
switch (targetType) {
case SOURCE: {
myProject.addCompileSourceRoot(targetSourceDirectory.getAbsolutePath());
break;
}
case RESOURCE: {
Resource resource = new Resource();
resource.setDirectory(targetResourceDirectory.getAbsolutePath());
if (targetFolder != null) {
resource.addInclude(targetFolder+"/*");
} else {
resource.addInclude("*");
}
myProject.addResource(resource);
break;
}
default:
}
}
public static void main(String[] args) throws ParseException, IOException, MojoFailureException, MojoExecutionException {
// PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
// HttpClientBuilder builder = HttpClientBuilder.create();
// builder.setConnectionManager(connectionManager);
// CloseableHttpClient client = builder.build();
//
// HttpGet get = new HttpGet("http://fhir.healthintersections.com.au/open/metadata");
// CloseableHttpResponse response = client.execute(get);
//
// String metadataString = EntityUtils.toString(response.getEntity());
//
// ourLog.info("Metadata String: {}", metadataString);
// String metadataString = IOUtils.toString(new FileInputStream("src/test/resources/healthintersections-metadata.xml"));
// Conformance conformance = new FhirContext(Conformance.class).newXmlParser().parseResource(Conformance.class, metadataString);
TinderGenericMultiFileMojo mojo = new TinderGenericMultiFileMojo();
mojo.myProject = new MavenProject();
mojo.version = "dstu2";
mojo.targetPackage = "ca.uhn.test";
mojo.template = "/vm/jpa_resource_provider.vm";
mojo.targetSourceDirectory = new File("target/generated/valuesets");
mojo.execute();
}
class Generator extends AbstractGenerator {
@Override
protected void logInfo(String message) {
ourLog.info(message);
}
@Override
protected void logDebug(String message) {
ourLog.debug(message);
}
}
}