full resource/composite generation using generic templates and Ant

This commit is contained in:
Bill Denton 2016-12-02 17:06:27 -08:00
parent a9d7b8d636
commit fd1ec07cde
12 changed files with 1788 additions and 514 deletions

View File

@ -0,0 +1,229 @@
package ca.uhn.fhir.tinder;
/*
* #%L
* HAPI FHIR Tinder Plug-In
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed 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.
* #L%
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
import org.apache.maven.plugin.MojoFailureException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.tinder.GeneratorContext.ProfileFileDefinition;
import ca.uhn.fhir.tinder.parser.DatatypeGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.ProfileParser;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
public abstract class AbstractGenerator {
protected abstract void logInfo (String message);
protected abstract void logDebug (String message);
public void prepare (GeneratorContext context) throws ExecutionException, FailureException {
/*
* Deal with the FHIR spec version
*/
FhirContext fhirContext;
String packageSuffix = "";
if ("dstu".equals(context.getVersion())) {
fhirContext = FhirContext.forDstu1();
} else if ("dstu2".equals(context.getVersion())) {
fhirContext = FhirContext.forDstu2();
} else if ("dstu3".equals(context.getVersion())) {
fhirContext = FhirContext.forDstu3();
packageSuffix = ".dstu3";
} else {
throw new FailureException("Unknown version configured: " + context.getVersion());
}
context.setPackageSuffix(packageSuffix);
/*
* Deal with which resources to process
*/
List<String> includeResources = context.getIncludeResources();
List<String> excludeResources = context.getExcludeResources();
if (includeResources == null || includeResources.isEmpty()) {
includeResources = new ArrayList<String>();
logInfo("No resource names supplied, going to use all resources from version: "+fhirContext.getVersion().getVersion());
Properties p = new Properties();
try {
p.load(fhirContext.getVersion().getFhirVersionPropertiesFile());
} catch (IOException e) {
throw new FailureException("Failed to load version property file", e);
}
logDebug("Property file contains: "+p);
TreeSet<String> keys = new TreeSet<String>();
for(Object next : p.keySet()) {
keys.add((String) next);
}
for (String next : keys) {
if (next.startsWith("resource.")) {
includeResources.add(next.substring("resource.".length()).toLowerCase());
}
}
/*
* No spreadsheet existed for Binary in DSTU1 so we don't generate it.. this
* is something we could work around, but at this point why bother since it's
* only an issue for DSTU1
*/
if (fhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
includeResources.remove("binary");
}
if (fhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
includeResources.remove("conformance");
}
}
for (int i = 0; i < includeResources.size(); i++) {
includeResources.set(i, includeResources.get(i).toLowerCase());
}
if (excludeResources != null) {
for (int i = 0; i < excludeResources.size(); i++) {
excludeResources.set(i, excludeResources.get(i).toLowerCase());
}
includeResources.removeAll(excludeResources);
}
context.setIncludeResources(includeResources);
logInfo("Including the following elements: "+includeResources);
/*
* Fill in ValueSet and DataTypes used by the resources
*/
ValueSetGenerator vsp = null;
DatatypeGeneratorUsingSpreadsheet dtp = null;
ProfileParser pp = null;
Map<String, String> datatypeLocalImports = new HashMap<String, String>();
vsp = new ValueSetGenerator(context.getVersion());
vsp.setResourceValueSetFiles(context.getValueSetFiles());
context.setValueSetGenerator(vsp);
try {
vsp.parse();
} catch (Exception e) {
throw new FailureException("Failed to load valuesets", e);
}
/*
* A few enums are not found by default because none of the generated classes
* refer to them, but we still want them.
*/
vsp.getClassForValueSetIdAndMarkAsNeeded("NarrativeStatus");
logInfo("Loading Datatypes...");
dtp = new DatatypeGeneratorUsingSpreadsheet(context.getVersion(), context.getBaseDir());
context.setDatatypeGenerator(dtp);
try {
dtp.parse();
dtp.markResourcesForImports();
} catch (Exception e) {
throw new FailureException("Failed to load datatypes", e);
}
dtp.bindValueSets(vsp);
datatypeLocalImports = dtp.getLocalImports();
/*
* Load the requested resources
*/
ResourceGeneratorUsingSpreadsheet rp = new ResourceGeneratorUsingSpreadsheet(context.getVersion(), context.getBaseDir());
context.setResourceGenerator(rp);
logInfo("Loading Resources...");
try {
rp.setBaseResourceNames(includeResources);
rp.parse();
rp.markResourcesForImports();
} catch (Exception e) {
throw new FailureException("Failed to load resources", e);
}
rp.bindValueSets(vsp);
rp.getLocalImports().putAll(datatypeLocalImports);
datatypeLocalImports.putAll(rp.getLocalImports());
rp.combineContentMaps(dtp);
dtp.combineContentMaps(rp);
if (context.getProfileFiles() != null) {
logInfo("Loading profiles...");
pp = new ProfileParser(context.getVersion(), context.getBaseDir());
context.setProfileParser(pp);
for (ProfileFileDefinition next : context.getProfileFiles()) {
logInfo("Parsing file: "+next.profileFile);
try {
pp.parseSingleProfile(new File(next.profileFile), next.profileSourceUrl);
} catch (MojoFailureException e) {
throw new FailureException(e);
}
}
pp.bindValueSets(vsp);
pp.markResourcesForImports();
pp.getLocalImports().putAll(datatypeLocalImports);
datatypeLocalImports.putAll(pp.getLocalImports());
pp.combineContentMaps(rp);
pp.combineContentMaps(dtp);
dtp.combineContentMaps(pp);
}
}
public class FailureException extends Exception {
public FailureException(String message, Throwable cause) {
super(message, cause);
}
public FailureException(Throwable cause) {
super(cause);
}
public FailureException(String message) {
super(message);
}
}
public class ExecutionException extends Exception {
public ExecutionException(String message, Throwable cause) {
super(message, cause);
}
public ExecutionException(String message) {
super(message);
}
}
}

View File

@ -0,0 +1,137 @@
package ca.uhn.fhir.tinder;
/*
* #%L
* HAPI FHIR Tinder Plug-In
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed 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.
* #L%
*/
import java.util.List;
import org.apache.maven.plugins.annotations.Parameter;
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;
/**
* @author Bill.Denton
*
*/
public class GeneratorContext {
private String version;
private String packageSuffix;
private String baseDir;
private List<String> includeResources;
private List<String> excludeResources;
private List<ValueSetFileDefinition> valueSetFiles;
private List<ProfileFileDefinition> profileFiles;
private ResourceGeneratorUsingSpreadsheet resourceGenerator = null;
private ValueSetGenerator valueSetGenerator = null;
private DatatypeGeneratorUsingSpreadsheet datatypeGenerator = null;
private ProfileParser profileParser = null;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getPackageSuffix() {
return packageSuffix;
}
public void setPackageSuffix(String packageSuffix) {
this.packageSuffix = packageSuffix;
}
public String getBaseDir() {
return baseDir;
}
public void setBaseDir(String baseDir) {
this.baseDir = baseDir;
}
public List<String> getIncludeResources() {
return includeResources;
}
public void setIncludeResources(List<String> includeResources) {
this.includeResources = includeResources;
}
public List<String> getExcludeResources() {
return excludeResources;
}
public void setExcludeResources(List<String> excludeResources) {
this.excludeResources = excludeResources;
}
public List<ValueSetFileDefinition> getValueSetFiles() {
return valueSetFiles;
}
public void setValueSetFiles(List<ValueSetFileDefinition> valueSetFiles) {
this.valueSetFiles = valueSetFiles;
}
public List<ProfileFileDefinition> getProfileFiles() {
return profileFiles;
}
public void setProfileFiles(List<ProfileFileDefinition> profileFiles) {
this.profileFiles = profileFiles;
}
public ResourceGeneratorUsingSpreadsheet getResourceGenerator() {
return resourceGenerator;
}
public void setResourceGenerator(ResourceGeneratorUsingSpreadsheet resourceGenerator) {
this.resourceGenerator = resourceGenerator;
}
public ValueSetGenerator getValueSetGenerator() {
return valueSetGenerator;
}
public void setValueSetGenerator(ValueSetGenerator valueSetGenerator) {
this.valueSetGenerator = valueSetGenerator;
}
public DatatypeGeneratorUsingSpreadsheet getDatatypeGenerator() {
return datatypeGenerator;
}
public void setDatatypeGenerator(DatatypeGeneratorUsingSpreadsheet datatypeGenerator) {
this.datatypeGenerator = datatypeGenerator;
}
public ProfileParser getProfileParser() {
return profileParser;
}
public void setProfileParser(ProfileParser profileParser) {
this.profileParser = profileParser;
}
public static class ProfileFileDefinition {
@Parameter(required = true)
String profileFile;
@Parameter(required = true)
String profileSourceUrl;
public String getProfileFile() {
return profileFile;
}
public void setProfileFile(String profileFile) {
this.profileFile = profileFile;
}
public String getProfileSourceUrl() {
return profileSourceUrl;
}
public void setProfileSourceUrl(String profileSourceUrl) {
this.profileSourceUrl = profileSourceUrl;
}
}
}

View File

@ -2,12 +2,10 @@ package ca.uhn.fhir.tinder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.TreeSet;
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;
@ -17,40 +15,241 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import ca.uhn.fhir.context.FhirContext;
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:&nbsp;<code><b>dstu</b></code>&nbsp;|&nbsp;<code><b>dstu2</b></code>&nbsp;|&nbsp;<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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<code><b>false</b></code></td>
* <td valign="top" align="center" rowspan="4">At least 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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetSourceDirectory&gt;/&lt;packageName&gt;/&lt;filenamePrefix&gt;<i>element-name</i>&lt;filenameSuffix&gt;<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 packageName 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">packageName</td>
* <td valign="top">The Java package that will contain the generated classes.
* This package is generated in the &lt;targetSourceDirectory&gt; 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>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetResourceDirectory&gt;/&lt;folderName&gt;/&lt;filenamePrefix&gt;<i>element-name</i>&lt;filenameSuffix&gt;<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">folderName</td>
* <td valign="top">The folder within the targetResourceDirectory where the generated files will be placed.
* This folder is generated in the &lt;targetResourceDirectory&gt; 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">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 packageName;
@Parameter(required = false)
private String filenamePrefix;
@Parameter(required = false)
private String filenameSuffix;
@Parameter(required = false)
private File targetResourceDirectory;
@Parameter(required = false)
private String folderName;
// one of these two is required
@Parameter(required = false)
private String template;
@Parameter(required = false)
private File templateFile;
@Parameter(required = true, defaultValue = "ResourceProvider")
private String filenameSuffix;
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-sources/tinder")
private File targetDirectory;
@Parameter(required = true)
private String packageBase;
@Parameter(required = false)
private String velocityPath;
@Parameter(required = false)
private List<String> baseResourceNames;
private List<String> includeResources;
@Parameter(required = false)
private List<String> excludeResourceNames;
private List<String> excludeResources;
@Parameter(required = true, defaultValue = "${project.build.directory}/..")
private String baseDir;
@Parameter(required = false)
private List<ValueSetFileDefinition> valueSetFiles;
@Parameter(required = true)
private String version;
@Parameter(required = false)
private List<ProfileFileDefinition> profileFiles;
@Component
private MavenProject myProject;
@ -58,74 +257,124 @@ public class TinderGenericMultiFileMojo extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
FhirContext fhirContext;
if ("dstu".equals(version)) {
fhirContext = FhirContext.forDstu1();
} else if ("dstu2".equals(version)) {
fhirContext = FhirContext.forDstu2();
} else if ("dstu3".equals(version)) {
fhirContext = FhirContext.forDstu3();
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 == packageName) {
throw new MojoFailureException("The [packageName] property must be specified when generating Java source code.");
}
targetDirectory = new File(targetSourceDirectory, packageName.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 (folderName != null) {
targetDirectory = new File(targetResourceDirectory, folderName);
} else {
throw new MojoFailureException("Unknown version configured: " + version);
targetDirectory = targetResourceDirectory;
}
} 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.writeAll(targetType, targetDirectory, null, packageName);
}
if (baseResourceNames == null || baseResourceNames.isEmpty()) {
baseResourceNames = new ArrayList<String>();
ourLog.info("No resource names supplied, going to use all resources from version: {}",fhirContext.getVersion().getVersion());
Properties p = new Properties();
try {
p.load(fhirContext.getVersion().getFhirVersionPropertiesFile());
} catch (IOException e) {
throw new MojoFailureException("Failed to load version property file", e);
/*
* 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.writeAll(targetType, targetDirectory, null, packageName);
}
ourLog.debug("Property file contains: {}",p);
TreeSet<String> keys = new TreeSet<String>();
for(Object next : p.keySet()) {
keys.add((String) next);
}
for (String next : keys) {
if (next.startsWith("resource.")) {
baseResourceNames.add(next.substring("resource.".length()).toLowerCase());
}
}
/*
* 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.writeMarkedValueSets(targetType, targetDirectory, packageName);
}
for (int i = 0; i < baseResourceNames.size(); i++) {
baseResourceNames.set(i, baseResourceNames.get(i).toLowerCase());
}
if (excludeResourceNames != null) {
for (int i = 0; i < excludeResourceNames.size(); i++) {
excludeResourceNames.set(i, excludeResourceNames.get(i).toLowerCase());
}
baseResourceNames.removeAll(excludeResourceNames);
}
ourLog.info("Including the following resources: {}", baseResourceNames);
File packageDirectoryBase = new File(targetDirectory, packageBase.replace(".", File.separatorChar + ""));
packageDirectoryBase.mkdirs();
ResourceGeneratorUsingSpreadsheet gen = new ResourceGeneratorUsingSpreadsheet(version, baseDir);
gen.setBaseResourceNames(baseResourceNames);
try {
gen.parse();
gen.setFilenameSuffix(filenameSuffix);
gen.setTemplate(template);
gen.setTemplateFile(templateFile);
gen.writeAll(packageDirectoryBase, null,packageBase);
} catch (Exception e) {
throw new MojoFailureException("Failed to generate files", e);
/*
* 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.writeAll(targetType, targetDirectory, null, packageName);
}
switch (targetType) {
case SOURCE: {
myProject.addCompileSourceRoot(targetDirectory.getAbsolutePath());
break;
}
case RESOURCE: {
Resource resource = new Resource();
resource.setDirectory(targetDirectory.getAbsolutePath());
resource.addInclude("*");
myProject.addResource(resource);
break;
}
default:
}
}
@ -149,10 +398,20 @@ public class TinderGenericMultiFileMojo extends AbstractMojo {
TinderGenericMultiFileMojo mojo = new TinderGenericMultiFileMojo();
mojo.myProject = new MavenProject();
mojo.version = "dstu2";
mojo.packageBase = "ca.uhn.test";
mojo.packageName = "ca.uhn.test";
mojo.template = "/vm/jpa_resource_provider.vm";
mojo.targetDirectory = new File("target/generated/valuesets");
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);
}
}
}

View File

@ -7,15 +7,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.TreeSet;
import org.apache.commons.lang.WordUtils;
import org.apache.http.ParseException;
import org.apache.maven.model.FileSet;
import org.apache.maven.model.PatternSet;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
@ -29,47 +24,212 @@ import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.tools.generic.EscapeTool;
import ca.uhn.fhir.context.FhirContext;
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.BaseStructureSpreadsheetParser;
import ca.uhn.fhir.tinder.parser.DatatypeGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.TargetType;
/**
* Generate a single file based on resource or composite type metadata.
* <p>
* Generates either a source or resource file containing all selected resources or
* composite data types. The file is
* 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:&nbsp;<code><b>dstu</b></code>&nbsp;|&nbsp;<code><b>dstu2</b></code>&nbsp;|&nbsp;<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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<code><b>false</b></code></td>
* <td valign="top" align="center" rowspan="2">At least one of these two 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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetSourceDirectory&gt;/&lt;packageName&gt;/&lt;targetFile&gt;<br>
* Note that all dots in the packageName 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 targetFile 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 file.</td>
* <td valign="top" align="center">Yes when a Java source file is to be generated</td>
* </tr>
* <tr>
* <td valign="top">packageName</td>
* <td valign="top">The Java package that will contain the generated classes.
* This package is generated in the &lt;targetSourceDirectory&gt; if needed.</td>
* <td valign="top" align="center">Yes when <i>targetSourceDirectory</i> is specified</td>
* </tr>
* <tr>
* <td valign="top">targetFile</td>
* <td valign="top">The name of the file to be generated</td>
* <td valign="top" align="center">Yes</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>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetResourceDirectory&gt;/&lt;folderName&gt;/&lt;targetFile&gt;<br>
* </td>
* </tr>
* <tr>
* <td valign="top">targetResourceDirectory</td>
* <td valign="top">The Maven resource directory to contain the generated file.</td>
* <td valign="top" align="center">Yes when a resource file is to be generated</td>
* </tr>
* <tr>
* <td valign="top">folderName</td>
* <td valign="top">The folder within the targetResourceDirectory where the generated file will be placed.
* This folder is generated in the &lt;targetResourceDirectory&gt; 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">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-single-file", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class TinderGenericSingleFileMojo extends AbstractMojo {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TinderGenericSingleFileMojo.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)
private File targetSourceDirectory;
@Parameter(required = false)
private String packageName;
@Parameter(required = false)
private File targetResourceDirectory;
@Parameter(required = false)
private String folderName;
@Parameter(required = false)
private String targetFile;
// one of these two is required
@Parameter(required = false)
private String template;
@Parameter(required = false)
private File templateFile;
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-sources/tinder")
private File targetDirectory;
@Parameter(required = false)
private String velocityPath;
@Parameter(required = false)
private String targetFolder;
private List<String> includeResources;
@Parameter(required = false)
private String targetPackage;
@Parameter(required = true)
private String targetFile;
@Parameter(required = true)
private String packageBase;
private List<String> excludeResources;
@Parameter(required = false)
private List<String> baseResourceNames;
private List<ValueSetFileDefinition> valueSetFiles;
@Parameter(required = false)
private List<String> excludeResourceNames;
@Parameter(required = true, defaultValue = "${project.build.directory}/..")
private String baseDir;
@Parameter(required = true)
private String version;
private List<ProfileFileDefinition> profileFiles;
@Component
private MavenProject myProject;
@ -77,66 +237,101 @@ public class TinderGenericSingleFileMojo extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
FhirContext fhirContext;
if ("dstu".equals(version)) {
fhirContext = FhirContext.forDstu1();
} else if ("dstu2".equals(version)) {
fhirContext = FhirContext.forDstu2();
} else if ("dstu3".equals(version)) {
fhirContext = FhirContext.forDstu3();
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());
}
try {
/*
* Deal with the generation target
*/
TargetType targetType = null;
File targetDirectory = null;
if (null == targetFile) {
throw new MojoFailureException("The [targetFile] parameter is required.");
}
if (targetSourceDirectory != null) {
if (targetResourceDirectory != null) {
throw new MojoFailureException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
targetType = TargetType.SOURCE;
if (null == packageName) {
throw new MojoFailureException("The [targetPackage] property must be specified when generating Java source code.");
}
targetDirectory = new File(targetSourceDirectory, packageName.replace('.', File.separatorChar));
if (!targetFile.endsWith(".java")) {
targetFile += ".java";
}
} else
if (targetResourceDirectory != null) {
if (targetSourceDirectory != null) {
throw new MojoFailureException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
targetType = TargetType.RESOURCE;
if (folderName != null) {
folderName = folderName.replace('\\', '/');
folderName = folderName.replace('/', File.separatorChar);
targetDirectory = new File(targetResourceDirectory, folderName);
} else {
throw new MojoFailureException("Unknown version configured: " + version);
targetDirectory = targetResourceDirectory;
}
if (baseResourceNames == null || baseResourceNames.isEmpty()) {
baseResourceNames = new ArrayList<String>();
ourLog.info("No resource names supplied, going to use all resources from version: {}",fhirContext.getVersion().getVersion());
Properties p = new Properties();
try {
p.load(fhirContext.getVersion().getFhirVersionPropertiesFile());
} catch (IOException e) {
throw new MojoFailureException("Failed to load version property file", e);
} else {
throw new MojoFailureException("Either [targetSourceDirectory] or [targetResourceDirectory] must be specified.");
}
ourLog.info(" * Output ["+targetType.toString()+"] file ["+targetFile+"] in directory: " + targetDirectory.getAbsolutePath());
targetDirectory.mkdirs();
File target = new File(targetDirectory, targetFile);
OutputStreamWriter targetWriter = new OutputStreamWriter(new FileOutputStream(target, false), "UTF-8");
ourLog.debug("Property file contains: {}",p);
TreeSet<String> keys = new TreeSet<String>();
for(Object next : p.keySet()) {
keys.add((String) next);
}
for (String next : keys) {
if (next.startsWith("resource.")) {
baseResourceNames.add(next.substring("resource.".length()).toLowerCase());
/*
* Next, deal with the template and initialize velocity
*/
VelocityEngine v = new VelocityEngine();
InputStream templateIs = null;
if (templateFile != null) {
templateIs = new FileInputStream(templateFile);
v.setProperty("resource.loader", "file");
v.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
if (velocityPath != null) {
v.setProperty("file.resource.loader.path", velocityPath);
} else {
String path = templateFile.getCanonicalFile().getParent();
if (null == path) {
path = ".";
}
v.setProperty("file.resource.loader.path", path);
}
} else {
templateIs = this.getClass().getResourceAsStream(template);
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
}
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStreamReader templateReader = new InputStreamReader(templateIs);
for (int i = 0; i < baseResourceNames.size(); i++) {
baseResourceNames.set(i, baseResourceNames.get(i).toLowerCase());
}
if (excludeResourceNames != null) {
for (int i = 0; i < excludeResourceNames.size(); i++) {
excludeResourceNames.set(i, excludeResourceNames.get(i).toLowerCase());
}
baseResourceNames.removeAll(excludeResourceNames);
}
ourLog.info("Including the following resources: {}", baseResourceNames);
ResourceGeneratorUsingSpreadsheet gen = new ResourceGeneratorUsingSpreadsheet(version, baseDir);
gen.setBaseResourceNames(baseResourceNames);
try {
gen.parse();
/*
* build new Velocity Context
*/
VelocityContext ctx = new VelocityContext();
ctx.put("resources", gen.getResources());
ctx.put("packageBase", packageBase);
ctx.put("targetPackage", targetPackage);
int ix = packageName.lastIndexOf('.');
ctx.put("packageBase", packageName.subSequence(0, ix));
ctx.put("targetPackage", packageName);
ctx.put("version", version);
ctx.put("isRi", BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi());
ctx.put("hash", "#");
ctx.put("esc", new EscapeTool());
if (BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi()) {
ctx.put("resourcePackage", "org.hl7.fhir." + version + ".model");
@ -150,45 +345,42 @@ public class TinderGenericSingleFileMojo extends AbstractMojo {
}
ctx.put("versionCapitalized", capitalize);
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStream templateIs = null;
if (templateFile != null) {
templateIs = new FileInputStream(templateFile);
/*
* Write resources if selected
*/
ResourceGeneratorUsingSpreadsheet rp = context.getResourceGenerator();
if (generateResources && rp != null) {
ourLog.info("Writing Resources...");
ctx.put("resources", rp.getResources());
v.evaluate(ctx, targetWriter, "", templateReader);
targetWriter.close();
} else {
templateIs = ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream(template);
DatatypeGeneratorUsingSpreadsheet dtp = context.getDatatypeGenerator();
if (generateDatatypes && dtp != null) {
ourLog.info("Writing DataTypes...");
ctx.put("datatypes", dtp.getResources());
v.evaluate(ctx, targetWriter, "", templateReader);
targetWriter.close();
}
InputStreamReader templateReader = new InputStreamReader(templateIs);
File target = targetDirectory;
if (targetFolder != null) {
targetFolder = targetFolder.replace('\\', '/');
targetFolder = targetFolder.replace('/', File.separatorChar);
target = new File(targetDirectory, targetFolder);
} else if (targetPackage != null) {
target = new File(targetDirectory, targetPackage.replace('.', File.separatorChar));
}
target.mkdirs();
File f = new File(target, targetFile);
OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(f, false), "UTF-8");
v.evaluate(ctx, w, "", templateReader);
w.close();
if (targetFile.endsWith(".java")) {
switch (targetType) {
case SOURCE: {
myProject.addCompileSourceRoot(targetDirectory.getAbsolutePath());
} else {
break;
}
case RESOURCE: {
Resource resource = new Resource();
resource.setDirectory(targetDirectory.getAbsolutePath());
String resName = targetFile;
if (targetFolder != null) {
resName = targetFolder+File.separator+targetFile;
if (folderName != null) {
resName = folderName+File.separator+targetFile;
}
resource.addInclude(resName);
myProject.addResource(resource);
break;
}
default:
}
} catch (Exception e) {
@ -217,10 +409,20 @@ public class TinderGenericSingleFileMojo extends AbstractMojo {
mojo.myProject = new MavenProject();
mojo.template = "/vm/jpa_spring_beans.vm";
mojo.version = "dstu2";
mojo.packageBase = "ca.uhn.test";
mojo.targetDirectory = new File("target/generated/valuesets");
mojo.packageName = "ca.uhn.test";
mojo.targetSourceDirectory = new File("target/generated/valuesets");
mojo.targetFile = "tmp_beans.xml";
mojo.execute();
}
class Generator extends AbstractGenerator {
@Override
protected void logInfo(String message) {
ourLog.info(message);
}
@Override
protected void logDebug(String message) {
ourLog.debug(message);
}
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.tinder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
@ -15,6 +16,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -39,8 +41,10 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.tinder.TinderStructuresMojo.ValueSetFileDefinition;
import ca.uhn.fhir.tinder.model.BaseRootType;
import ca.uhn.fhir.tinder.model.ValueSetTm;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
import ca.uhn.fhir.tinder.parser.TargetType;
public class ValueSetGenerator {
@ -51,6 +55,11 @@ public class ValueSetGenerator {
private int myValueSetCount;
private Map<String, ValueSetTm> myValueSets = new HashMap<String, ValueSetTm>();
private String myVersion;
private String myFilenamePrefix = "";
private String myFilenameSuffix = "";
private String myTemplate = null;
private File myTemplateFile = null;
private String myVelocityPath = null;
public ValueSetGenerator(String theVersion) {
myVersion = theVersion;
@ -293,9 +302,33 @@ public class ValueSetGenerator {
return b.toString();
}
public void setFilenamePrefix(String theFilenamePrefix) {
myFilenamePrefix = theFilenamePrefix;
}
public void setFilenameSuffix(String theFilenameSuffix) {
myFilenameSuffix = theFilenameSuffix;
}
public void setTemplate(String theTemplate) {
myTemplate = theTemplate;
}
public void setTemplateFile (File theTemplateFile) {
myTemplateFile = theTemplateFile;
}
public void setVelocityPath(String theVelocityPath) {
myVelocityPath = theVelocityPath;
}
public void write(Collection<ValueSetTm> theValueSets, File theOutputDirectory, String thePackageBase) throws IOException {
write(TargetType.SOURCE, theValueSets, theOutputDirectory, thePackageBase);
}
public void write(TargetType theTarget, Collection<ValueSetTm> theValueSets, File theOutputDirectory, String thePackageBase) throws IOException {
for (ValueSetTm nextValueSetTm : theValueSets) {
write(nextValueSetTm, theOutputDirectory, thePackageBase);
write(theTarget, nextValueSetTm, theOutputDirectory, thePackageBase);
}
}
@ -303,7 +336,7 @@ public class ValueSetGenerator {
// myValueSetName = theString;
// }
private void write(ValueSetTm theValueSetTm, File theOutputDirectory, String thePackageBase) throws IOException {
private void write(TargetType theTarget, ValueSetTm theValueSetTm, File theOutputDirectory, String thePackageBase) throws IOException {
if (!theOutputDirectory.exists()) {
theOutputDirectory.mkdirs();
}
@ -311,22 +344,51 @@ public class ValueSetGenerator {
throw new IOException(theOutputDirectory + " is not a directory");
}
File f = new File(theOutputDirectory, theValueSetTm.getClassName() + ".java");
String valueSetName = theValueSetTm.getClassName();
String prefix = myFilenamePrefix;
String suffix = myFilenameSuffix;
if (theTarget == TargetType.SOURCE) {
if (!suffix.endsWith(".java")) {
suffix += ".java";
}
}
String fileName = prefix + valueSetName + suffix;
File f = new File(theOutputDirectory, fileName);
OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(f, false), "UTF-8");
ourLog.debug("Writing file: {}", f.getAbsolutePath());
VelocityContext ctx = new VelocityContext();
InputStream templateIs = null;
ctx.put("valueSet", theValueSetTm);
ctx.put("packageBase", thePackageBase);
ctx.put("esc", new EscapeTool());
VelocityEngine v = new VelocityEngine();
if (myTemplateFile != null) {
templateIs = new FileInputStream(myTemplateFile);
v.setProperty("resource.loader", "file");
v.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
if (myVelocityPath != null) {
v.setProperty("file.resource.loader.path", myVelocityPath);
} else {
String path = myTemplateFile.getCanonicalFile().getParent();
if (null == path) {
path = ".";
}
v.setProperty("file.resource.loader.path", path);
}
} else {
String templateName = myTemplate;
if (null == templateName) {
templateName = "/vm/valueset.vm";
}
templateIs = this.getClass().getResourceAsStream(templateName);
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
}
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStream templateIs = ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream("/vm/valueset.vm");
InputStreamReader templateReader = new InputStreamReader(templateIs, "UTF-8");
v.evaluate(ctx, w, "", templateReader);
@ -334,8 +396,12 @@ public class ValueSetGenerator {
}
public void writeMarkedValueSets(File theOutputDirectory, String thePackageBase) throws MojoFailureException {
writeMarkedValueSets(TargetType.SOURCE, theOutputDirectory, thePackageBase);
}
public void writeMarkedValueSets(TargetType theTarget, File theOutputDirectory, String thePackageBase) throws MojoFailureException {
try {
write(myMarkedValueSets, theOutputDirectory, thePackageBase);
write(theTarget, myMarkedValueSets, theOutputDirectory, thePackageBase);
} catch (IOException e) {
throw new MojoFailureException("Failed to write valueset", e);
}

View File

@ -1,102 +1,280 @@
/**
* SOA Software, Inc. Copyright (C) 2000-2012, All rights reserved
package ca.uhn.fhir.tinder.ant;
/*
* #%L
* HAPI Tinder Plugin
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed 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
*
* This software is the confidential and proprietary information of SOA Software, Inc.
* and is subject to copyright protection under laws of the United States of America and
* other countries. The use of this software should be in accordance with the license
* agreement terms you entered into with SOA Software, Inc.
* http://www.apache.org/licenses/LICENSE-2.0
*
* $Id$
* 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.
* #L%
*/
package ca.uhn.fhir.tinder.ant;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import org.apache.commons.lang.WordUtils;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.util.FileUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.tools.generic.EscapeTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.tinder.AbstractGenerator;
import ca.uhn.fhir.tinder.AbstractGenerator.ExecutionException;
import ca.uhn.fhir.tinder.AbstractGenerator.FailureException;
import ca.uhn.fhir.tinder.GeneratorContext;
import ca.uhn.fhir.tinder.GeneratorContext.ProfileFileDefinition;
import ca.uhn.fhir.tinder.TinderStructuresMojo.ValueSetFileDefinition;
import ca.uhn.fhir.tinder.ValueSetGenerator;
import ca.uhn.fhir.tinder.parser.BaseStructureSpreadsheetParser;
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 Ant task properties are used
* <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:&nbsp;<code><b>dstu</b></code>&nbsp;|&nbsp;<code><b>dstu2</b></code>&nbsp;|&nbsp;<code><b>dstu3</b></code></td>
* <td valign="top" align="center">Yes</td>
* </tr>
* <tr>
* <td valign="top">projectHome</td>
* <td valign="top">The 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>${basedir}/..</code></td>
* </tr>
* <tr>
* <td valign="top">generateResources</td>
* <td valign="top">Should files be generated from FHIR resource metadata?<br>
* Valid values:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<code><b>false</b></code></td>
* <td valign="top" align="center" rowspan="4">At least 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:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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>
* This option can only be used if generating multiple files (one file per value-set.)<br>
* Valid values:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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>
* This option can only be used if generating multiple files (one file per profile.)<br>
* Valid values:&nbsp;<code><b>true</b></code>&nbsp;|&nbsp;<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. Source files can be
* generated for each selected entity or a single source file can
* be generated containing all of the selected entity. The following configuration
* properties control the naming of the generated source files:
* <p>The following properties will be used when generating multiple source files:<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetSourceDirectory&gt;/&lt;packageName&gt;/&lt;filenamePrefix&gt;<i>element-name</i>&lt;filenameSuffix&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;where: <i>element-name</i> is the "title-case" name of the selected resource or composite data type.
* <p>The following properties will be used when generating a single source file:<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetSourceDirectory&gt;/&lt;packageName&gt;/&lt;targetFile&gt;
* <p>
* Note that all dots in the packageName 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 or targetFile if it is not already included.
* </td>
* </tr>
* <tr>
* <td valign="top">targetSourceDirectory</td>
* <td valign="top">The source directory to contain the generated Java packages and classes.</td>
* <td valign="top" align="center">Yes when Java source files are to be generated</td>
* </tr>
* <tr>
* <td valign="top">packageName</td>
* <td valign="top">The Java package that will contain the generated classes.
* This package is generated in the &lt;targetSourceDirectory&gt; if needed.</td>
* <td valign="top" align="center">Yes when <i>targetSourceDirectory</i> is specified</td>
* </tr>
* <tr>
* <td valign="top">targetFile</td>
* <td valign="top">The name of the file to be generated</td>
* <td valign="top" align="center">Yes when generating a single file containing all selected elements</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">Resource (non-Java) files can also be generated
* for FHIR resources or composite data types. a file can be
* generated for each selected entity or a single file can
* be generated containing all of the selected entity. The following configuration
* properties control the naming of the generated files:
* <p>The following properties will be used when generating multiple files (one for each selected element):<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetResourceDirectory&gt;/&lt;folderName&gt;/&lt;filenamePrefix&gt;<i>element-name</i>&lt;filenameSuffix&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;where: <i>element-name</i> is the "title-case" name of the selected resource or composite data type.
* <p>The following properties will be used when generating a single file containing all selected elements:<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;targetResourceDirectory&gt;/&lt;folderName&gt;/&lt;targetFile&gt;
* </td>
* </tr>
* <tr>
* <td valign="top">targetResourceDirectory</td>
* <td valign="top">The 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">folderName</td>
* <td valign="top">The folder within the targetResourceDirectory where the generated files will be placed.
* This folder is generated in the &lt;targetResourceDirectory&gt; 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">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
*
* @author Copyright (c) 2012, SOA Software, Inc. All rights reserved.
* @since 6.0
*/
public class TinderGeneratorTask extends Task {
// Parameter(required = true)
private String version;
// Parameter(required = true)
private String template;
private String templateFile;
private File templateFileFile;
// Parameter(required = true, defaultValue = "${project.build.directory}/..")
private String projectHome;
// Parameter(required = true, defaultValue = "${project.build.directory}/generated-sources/tinder")
private String targetDir;
private File targetDirectoryFile;
private boolean generateResources;
private String targetPackage;
private boolean generateDatatypes;
private boolean generateValueSets;
private boolean generateProfiles;
private File targetSourceDirectory;
private String packageName;
// Parameter(required = true, defaultValue = "${project.build.directory}/generated-resources/tinder")
private String targetFile;
// Parameter(required = true)
private String packageBase;
private String filenamePrefix;
private String targetClassSuffix;
private String filenameSuffix;
// Parameter(required = false)
private List<String> baseResourceNames;
private File targetResourceDirectory;
// Parameter(required = false)
private List<String> excludeResourceNames;
private String folderName;
// one of these two is required
private String template;
private File templateFile;
private String velocityPath;
private List<String> includeResources;
private List<String> excludeResources;
private List<ValueSetFileDefinition> valueSetFiles;
private List<ProfileFileDefinition> profileFiles;
private boolean verbose;
@ -113,68 +291,110 @@ public class TinderGeneratorTask extends Task {
public void execute () throws BuildException {
validateAttributes();
GeneratorContext context = new GeneratorContext();
context.setVersion(version);
context.setBaseDir(projectHome);
context.setIncludeResources(includeResources);
context.setExcludeResources(excludeResources);
context.setValueSetFiles(valueSetFiles);
context.setProfileFiles(profileFiles);
Generator generator = new Generator();
try {
if (baseResourceNames == null || baseResourceNames.isEmpty()) {
baseResourceNames = new ArrayList<String>();
log("No resource names supplied, going to use all resources from version: "+fhirContext.getVersion().getVersion());
Properties p = new Properties();
try {
p.load(fhirContext.getVersion().getFhirVersionPropertiesFile());
} catch (IOException e) {
throw new BuildException("Failed to load version property file", e);
generator.prepare(context);
} catch (ExecutionException e) {
throw new BuildException(e.getMessage(), e.getCause());
} catch (FailureException e) {
throw new BuildException(e.getMessage(), e.getCause());
}
if (verbose) {
log("Property file contains: "+p);
/*
* Deal with the generation target
*/
TargetType targetType = null;
File targetDirectory = null;
if (targetSourceDirectory != null) {
if (targetResourceDirectory != null) {
throw new BuildException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
for(Object next : p.keySet()) {
if (((String)next).startsWith("resource.")) {
baseResourceNames.add(((String)next).substring("resource.".length()).toLowerCase());
targetType = TargetType.SOURCE;
if (null == packageName) {
throw new BuildException("The [packageName] property must be specified when generating Java source code.");
}
targetDirectory = new File(targetSourceDirectory, packageName.replace('.', File.separatorChar));
} else
if (targetResourceDirectory != null) {
if (targetSourceDirectory != null) {
throw new BuildException("Both [targetSourceDirectory] and [targetResourceDirectory] are specified. Please choose just one.");
}
targetType = TargetType.RESOURCE;
if (folderName != null) {
targetDirectory = new File(targetResourceDirectory, folderName);
} else {
targetDirectory = targetResourceDirectory;
}
} else {
for (int i = 0; i < baseResourceNames.size(); i++) {
baseResourceNames.set(i, baseResourceNames.get(i).toLowerCase());
throw new BuildException("Either [targetSourceDirectory] or [targetResourceDirectory] must be specified.");
}
}
if (excludeResourceNames != null) {
for (int i = 0; i < excludeResourceNames.size(); i++) {
baseResourceNames.remove(excludeResourceNames.get(i).toLowerCase());
}
}
log("Including the following resources: "+baseResourceNames);
ResourceGeneratorUsingSpreadsheet gen = new ResourceGeneratorUsingSpreadsheet(version, projectHome);
gen.setBaseResourceNames(baseResourceNames);
targetDirectory.mkdirs();
log(" * Output ["+targetType.toString()+"] Directory: " + targetDirectory.getAbsolutePath());
try {
gen.parse();
// gen.setFilenameSuffix("ResourceProvider");
// gen.setTemplate("/vm/jpa_daos.vm");
// gen.writeAll(packageDirectoryBase, null,packageBase);
// gen.setFilenameSuffix("ResourceTable");
// gen.setTemplate("/vm/jpa_resource_table.vm");
// gen.writeAll(directoryBase, packageBase);
} catch (Exception e) {
throw new BuildException("Failed to parse FHIR metadata", e);
/*
* Single file with all elements
*/
if (targetFile != null) {
if (targetType == TargetType.SOURCE) {
if (!targetFile.endsWith(".java")) {
targetFile += ".java";
}
}
File target = new File(targetDirectory, targetFile);
OutputStreamWriter targetWriter = new OutputStreamWriter(new FileOutputStream(target, false), "UTF-8");
try {
/*
* Next, deal with the template and initialize velocity
*/
VelocityEngine v = new VelocityEngine();
InputStream templateIs = null;
if (templateFile != null) {
templateIs = new FileInputStream(templateFile);
v.setProperty("resource.loader", "file");
v.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
if (velocityPath != null) {
v.setProperty("file.resource.loader.path", velocityPath);
} else {
String path = templateFile.getCanonicalFile().getParent();
if (null == path) {
path = ".";
}
v.setProperty("file.resource.loader.path", path);
}
} else {
templateIs = this.getClass().getResourceAsStream(template);
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
}
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStreamReader templateReader = new InputStreamReader(templateIs);
/*
* build new Velocity Context
*/
VelocityContext ctx = new VelocityContext();
ctx.put("resources", gen.getResources());
ctx.put("packageBase", packageBase);
ctx.put("targetPackage", targetPackage);
int ix = packageName.lastIndexOf('.');
ctx.put("packageBase", packageName.subSequence(0, ix));
ctx.put("targetPackage", packageName);
ctx.put("version", version);
ctx.put("isRi", BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi());
ctx.put("hash", "#");
ctx.put("esc", new EscapeTool());
if (BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi()) {
ctx.put("resourcePackage", "org.hl7.fhir." + version + ".model");
} else {
ctx.put("resourcePackage", "ca.uhn.fhir.model." + version + ".resource");
}
String capitalize = WordUtils.capitalize(version);
if ("Dstu".equals(capitalize)) {
@ -182,53 +402,85 @@ public class TinderGeneratorTask extends Task {
}
ctx.put("versionCapitalized", capitalize);
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
v.setProperty("runtime.references.strict", Boolean.TRUE);
targetDirectoryFile.mkdirs();
if (targetFile != null) {
InputStream templateIs = null;
if (templateFileFile != null) {
templateIs = new FileInputStream(templateFileFile);
/*
* Write resources if selected
*/
ResourceGeneratorUsingSpreadsheet rp = context.getResourceGenerator();
if (generateResources && rp != null) {
log("Writing Resources...");
ctx.put("resources", rp.getResources());
v.evaluate(ctx, targetWriter, "", templateReader);
targetWriter.close();
} else {
templateIs = ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream(template);
DatatypeGeneratorUsingSpreadsheet dtp = context.getDatatypeGenerator();
if (generateDatatypes && dtp != null) {
log("Writing DataTypes...");
ctx.put("datatypes", dtp.getResources());
v.evaluate(ctx, targetWriter, "", templateReader);
targetWriter.close();
}
}
InputStreamReader templateReader = new InputStreamReader(templateIs);
File target = null;
if (targetPackage != null) {
target = new File(targetDir, targetPackage.replace('.', File.separatorChar));
/*
* Multiple files.. one for each element
*/
} else {
target = new File(targetDir);
}
target.mkdirs();
File f = new File(target, targetFile);
OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(f, false), "UTF-8");
v.evaluate(ctx, w, "", templateReader);
w.close();
} else {
File packageDirectoryBase = new File(targetDir, packageBase.replace(".", File.separatorChar + ""));
packageDirectoryBase.mkdirs();
gen.setFilenameSuffix(targetClassSuffix);
gen.setTemplate(template);
gen.setTemplateFile(templateFileFile);
gen.writeAll(packageDirectoryBase, null,packageBase);
/*
* Write resources if selected
*/
ResourceGeneratorUsingSpreadsheet rp = context.getResourceGenerator();
if (generateResources && rp != null) {
log("Writing Resources...");
rp.setFilenamePrefix(filenamePrefix);
rp.setFilenameSuffix(filenameSuffix);
rp.setTemplate(template);
rp.setTemplateFile(templateFile);
rp.setVelocityPath(velocityPath);
rp.writeAll(targetType, targetDirectory, null, packageName);
}
} catch (Exception e) {
log("Caught exception: "+e.getClass().getName()+" ["+e.getMessage()+"]", 1);
e.printStackTrace();
throw new BuildException("Failed to generate file(s)", e);
/*
* Write composite datatypes
*/
DatatypeGeneratorUsingSpreadsheet dtp = context.getDatatypeGenerator();
if (generateDatatypes && dtp != null) {
log("Writing Composite Datatypes...");
dtp.setFilenamePrefix(filenamePrefix);
dtp.setFilenameSuffix(filenameSuffix);
dtp.setTemplate(template);
dtp.setTemplateFile(templateFile);
dtp.setVelocityPath(velocityPath);
dtp.writeAll(targetType, targetDirectory, null, packageName);
}
cleanup();
/*
* Write valuesets
*/
ValueSetGenerator vsp = context.getValueSetGenerator();
if (generateValueSets && vsp != null) {
log("Writing ValueSet Enums...");
vsp.setFilenamePrefix(filenamePrefix);
vsp.setFilenameSuffix(filenameSuffix);
vsp.setTemplate(template);
vsp.setTemplateFile(templateFile);
vsp.setVelocityPath(velocityPath);
vsp.writeMarkedValueSets(targetType, targetDirectory, packageName);
}
/*
* Write profiles
*/
ProfileParser pp = context.getProfileParser();
if (generateProfiles && pp != null) {
log("Writing Profiles...");
pp.setFilenamePrefix(filenamePrefix);
pp.setFilenameSuffix(filenameSuffix);
pp.setTemplate(template);
pp.setTemplateFile(templateFile);
pp.setVelocityPath(velocityPath);
pp.writeAll(targetType, targetDirectory, null, packageName);
}
}
} catch (Exception e) {
if (e instanceof BuildException) {
@ -237,6 +489,8 @@ public class TinderGeneratorTask extends Task {
log("Caught exception: "+e.getClass().getName()+" ["+e.getMessage()+"]", 1);
e.printStackTrace();
throw new BuildException("Error processing "+getTaskName()+" task.", e);
} finally {
cleanup();
}
}
@ -244,113 +498,172 @@ public class TinderGeneratorTask extends Task {
if (null == version) {
throw new BuildException("The "+this.getTaskName()+" task requires \"version\" attribute.");
}
if ("dstu".equals(version)) {
fhirContext = FhirContext.forDstu1();
} else if ("dstu2".equals(version)) {
fhirContext = FhirContext.forDstu2();
} else if ("dstu3".equals(version)) {
fhirContext = FhirContext.forDstu3();
} else {
throw new BuildException("Unknown version configured: " + version);
}
if (null == template) {
if (null == templateFile) {
throw new BuildException("The "+this.getTaskName()+" task requires \"template\" or \"templateFile\" attribute.");
}
templateFileFile = new File(templateFile);
if (!templateFileFile.exists()) {
throw new BuildException("The Velocity template file ["+templateFileFile.getAbsolutePath()+"] does not exist.");
if (!templateFile.exists()) {
throw new BuildException("The Velocity template file ["+templateFile.getAbsolutePath()+"] does not exist.");
}
if (!templateFileFile.canRead()) {
throw new BuildException("The Velocity template file ["+templateFileFile.getAbsolutePath()+"] cannot be read.");
if (!templateFile.canRead()) {
throw new BuildException("The Velocity template file ["+templateFile.getAbsolutePath()+"] cannot be read.");
}
if (!templateFileFile.isFile()) {
throw new BuildException("The Velocity template file ["+templateFileFile.getAbsolutePath()+"] is not a file.");
if (!templateFile.isFile()) {
throw new BuildException("The Velocity template file ["+templateFile.getAbsolutePath()+"] is not a file.");
}
}
if (null == projectHome) {
throw new BuildException("The "+this.getTaskName()+" task requires \"projectHome\" attribute.");
}
if (null == targetDir) {
throw new BuildException("The "+this.getTaskName()+" task requires \"targetDirectory\" attribute.");
}
targetDirectoryFile = new File(targetDir);
if (targetFile != null || (packageBase != null && targetClassSuffix != null)) {
// this is good
} else {
throw new BuildException("The "+this.getTaskName()+" task requires either the \"targetFile\" attribute or both the \"packageBase\" and \"targetClassSuffix\" attributes.");
}
}
protected void cleanup () {
}
public void setBaseResourceNames(String names) {
if (null == this.baseResourceNames) {
this.baseResourceNames = new ArrayList<String>();
}
if (names != null) {
List<String> work = new ArrayList<String>();
String[] tokens = names.split(",");
for (String token : tokens) {
work.add(token.trim());
}
this.baseResourceNames.addAll(work);
}
}
public void setExcludeResourceNames(String names) {
if (null == this.excludeResourceNames) {
this.excludeResourceNames = new ArrayList<String>();
}
if (names != null) {
List<String> work = new ArrayList<String>();
String[] tokens = names.split(",");
for (String token : tokens) {
work.add(token.trim());
}
this.excludeResourceNames.addAll(work);
}
}
public void setTemplate(String template) {
this.template = template;
}
public void setTemplateFile(String template) {
this.templateFile = template;
public void setVersion(String version) {
this.version = version;
}
public void setProjectHome(String projectHome) {
this.projectHome = projectHome;
}
public void setTargetDir(String targetDirectory) {
this.targetDir = targetDirectory;
public void setGenerateResources(boolean generateResources) {
this.generateResources = generateResources;
}
public void setGenerateDatatypes(boolean generateDatatypes) {
this.generateDatatypes = generateDatatypes;
}
public void setGenerateValueSets(boolean generateValueSets) {
this.generateValueSets = generateValueSets;
}
public void setGenerateProfiles(boolean generateProfiles) {
this.generateProfiles = generateProfiles;
}
public void setTargetSourceDirectory(File targetSourceDirectory) {
this.targetSourceDirectory = targetSourceDirectory;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public void setTargetFile(String targetFile) {
this.targetFile = targetFile;
}
public void setTargetPackage(String targetPackage) {
this.targetPackage = targetPackage;
public void setFilenamePrefix(String filenamePrefix) {
this.filenamePrefix = filenamePrefix;
}
public void setPackageBase(String packageBase) {
this.packageBase = packageBase;
public void setFilenameSuffix(String filenameSuffix) {
this.filenameSuffix = filenameSuffix;
}
public void setTargetClassSuffix(String targetClassSuffix) {
this.targetClassSuffix = targetClassSuffix;
public void setTargetResourceDirectory(File targetResourceDirectory) {
this.targetResourceDirectory = targetResourceDirectory;
}
public static class Version extends EnumeratedAttribute {
public String[] getValues() {
return new String[] {"dstu", "dstu2", "dstu3"};
public void setFolderName(String folderName) {
this.folderName = folderName;
}
public void setTemplate(String template) {
this.template = template;
}
public void setTemplateFile(File templateFile) {
this.templateFile = templateFile;
}
public void setVelocityPath(String velocityPath) {
this.velocityPath = velocityPath;
}
public void setIncludeResources(String names) {
if (null == this.includeResources) {
this.includeResources = new ArrayList<String>();
}
if (names != null) {
StringTokenizer tokens = new StringTokenizer(names, ", \t\r\n");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
this.includeResources.add(token.trim());
}
}
}
public void setExcludeResources(String names) {
if (null == this.excludeResources) {
this.excludeResources = new ArrayList<String>();
}
if (names != null) {
StringTokenizer tokens = new StringTokenizer(names, ", \t\r\n");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
this.excludeResources.add(token.trim());
}
}
}
public void setValueSetFiles(String names) {
if (null == this.valueSetFiles) {
this.valueSetFiles = new ArrayList<ValueSetFileDefinition>();
}
if (names != null) {
StringTokenizer tokens = new StringTokenizer(names, ", \t\r\n");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
ValueSetFileDefinition def = new ValueSetFileDefinition();
def.setValueSetFile(token.trim());
this.valueSetFiles.add(def);
}
}
}
public void setProfileFiles(String names) {
if (null == this.profileFiles) {
this.profileFiles = new ArrayList<ProfileFileDefinition>();
}
if (names != null) {
StringTokenizer tokens = new StringTokenizer(names, ", \t\r\n");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
ProfileFileDefinition def = new ProfileFileDefinition();
int ix = token.indexOf('{');
if (ix >= 0) {
int end = token.indexOf('}');
String url = token.substring(ix+1, end);
token = token.substring(end+1);
def.setProfileSourceUrl(url);
}
def.setProfileFile(token.trim());
this.profileFiles.add(def);
}
}
public void setVersion(Version version) {
this.version = version.getValue();
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
class Generator extends AbstractGenerator {
@Override
protected void logInfo(String message) {
log(message);
}
@Override
protected void logDebug(String message) {
if (verbose) {
log(message);
}
}
}
}

View File

@ -148,14 +148,21 @@ public abstract class Child extends BaseElement {
return false;
}
public boolean isPrimitive (String theType) {
return isPrimitiveInternal(theType);
}
public boolean isPrimitive() {
if (IDatatype.class.getSimpleName().equals(getReferenceType())) {
return false;
}
return isPrimitiveInternal(getSingleType());
}
protected boolean isPrimitiveInternal (String theType) {
try {
String name = "ca.uhn.fhir.model.primitive." + getSingleType();
String name = "ca.uhn.fhir.model.primitive." + theType;
Class.forName(name);
return true;
} catch (ClassNotFoundException e) {
@ -163,8 +170,16 @@ public abstract class Child extends BaseElement {
}
}
public String getPrimitiveType (String theType) throws ClassNotFoundException {
return getPrimitiveTypeInternal(theType);
}
public String getPrimitiveType() throws ClassNotFoundException {
String name = "ca.uhn.fhir.model.primitive." + getSingleType();
return getPrimitiveTypeInternal(getSingleType());
}
protected String getPrimitiveTypeInternal (String theType) throws ClassNotFoundException {
String name = "ca.uhn.fhir.model.primitive." + theType;
Class<?> clazz = Class.forName(name);
if (clazz.equals(IdDt.class)) {
return String.class.getSimpleName();

View File

@ -68,6 +68,11 @@ public abstract class BaseStructureParser {
private String myVersion;
private boolean myIsRi;
private FhirContext myCtx;
private String myFilenamePrefix = "";
private String myFilenameSuffix = "";
private String myTemplate = null;
private File myTemplateFile = null;
private String myVelocityPath = null;
public BaseStructureParser(String theVersion, String theBaseDir) {
myVersion = theVersion;
@ -174,7 +179,13 @@ public abstract class BaseStructureParser {
}
}
protected abstract String getFilenameSuffix();
protected String getFilenamePrefix() {
return myFilenamePrefix;
}
protected String getFilenameSuffix() {
return myFilenameSuffix;
}
public Map<String, String> getLocalImports() {
return myLocallyDefinedClassNames;
@ -188,9 +199,17 @@ public abstract class BaseStructureParser {
return myResources;
}
protected abstract String getTemplate();
protected String getTemplate() {
return myTemplate;
}
protected abstract File getTemplateFile();
protected File getTemplateFile() {
return myTemplateFile;
}
protected String getVelocityPath() {
return myVelocityPath;
}
protected boolean isSpreadsheet(String theFileName) {
return true;
@ -436,6 +455,26 @@ public abstract class BaseStructureParser {
myExtensions = theExts;
}
public void setFilenamePrefix(String theFilenamePrefix) {
myFilenamePrefix = theFilenamePrefix;
}
public void setFilenameSuffix(String theFilenameSuffix) {
myFilenameSuffix = theFilenameSuffix;
}
public void setTemplate(String theTemplate) {
myTemplate = theTemplate;
}
public void setTemplateFile (File theTemplateFile) {
myTemplateFile = theTemplateFile;
}
public void setVelocityPath(String theVelocityPath) {
myVelocityPath = theVelocityPath;
}
private void write(BaseRootType theResource, File theFile, String thePackageBase) throws IOException, MojoFailureException {
FileOutputStream fos = new FileOutputStream(theFile, false);
OutputStreamWriter w = new OutputStreamWriter(fos, "UTF-8");
@ -499,13 +538,27 @@ public abstract class BaseStructureParser {
ctx.put("versionCapitalized", capitalize);
VelocityEngine v = new VelocityEngine();
InputStream templateIs = null;
if (getTemplateFile() != null) {
templateIs = new FileInputStream(getTemplateFile());
v.setProperty("resource.loader", "file");
v.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
if (getVelocityPath() != null) {
v.setProperty("file.resource.loader.path", getVelocityPath());
} else {
String path = getTemplateFile().getCanonicalFile().getParent();
if (null == path) {
path = ".";
}
v.setProperty("file.resource.loader.path", path);
}
} else {
templateIs = this.getClass().getResourceAsStream(getTemplate());
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
}
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStream templateIs = getTemplateFile() != null
? new FileInputStream(getTemplateFile())
: ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream(getTemplate());
InputStreamReader templateReader = new InputStreamReader(templateIs);
v.evaluate(ctx, w, "", templateReader);
@ -514,6 +567,10 @@ public abstract class BaseStructureParser {
}
public void writeAll(File theOutputDirectory, File theResourceOutputDirectory, String thePackageBase) throws MojoFailureException {
writeAll(TargetType.SOURCE, theOutputDirectory, theResourceOutputDirectory, thePackageBase);
}
public void writeAll(TargetType theTarget, File theOutputDirectory, File theResourceOutputDirectory, String thePackageBase) throws MojoFailureException {
myPackageBase = thePackageBase;
if (!theOutputDirectory.exists()) {
@ -550,14 +607,17 @@ public abstract class BaseStructureParser {
// File f = new File(theOutputDirectory, (next.getDeclaringClassNameComplete()) /*+ getFilenameSuffix()*/ +
// ".java");
String elementName = Resource.correctName(next.getElementName());
String fwork = getFilenameSuffix();
// TODO -- how to generate multiple non-Java files??
if (fwork.endsWith(".java")) {
fwork = elementName + fwork;
} else {
fwork = elementName + fwork + ".java";
String prefix = getFilenamePrefix();
String suffix = getFilenameSuffix();
if (theTarget == TargetType.SOURCE) {
if (!suffix.endsWith(".java")) {
suffix += ".java";
}
File f = new File(theOutputDirectory, fwork);
}
String fileName = prefix + elementName + suffix;
int ix = fileName.lastIndexOf('.');
String className = ix < 0 ? fileName : fileName.substring(0, ix);
File f = new File(theOutputDirectory, fileName);
try {
write(next, f, thePackageBase);
} catch (IOException e) {
@ -565,9 +625,9 @@ public abstract class BaseStructureParser {
}
if (next instanceof Resource) {
myNameToResourceClass.put(next.getElementName(), thePackageBase + ".resource." + elementName);
myNameToResourceClass.put(next.getElementName(), thePackageBase + ".resource." + className);
} else if (next instanceof Composite) {
myNameToDatatypeClass.put(next.getElementName(), thePackageBase + ".composite." + elementName + "Dt");
myNameToDatatypeClass.put(next.getElementName(), thePackageBase + ".composite." + className);
} else {
throw new IllegalStateException(next.getClass().toString());
}

View File

@ -34,23 +34,18 @@ public class DatatypeGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP
public DatatypeGeneratorUsingSpreadsheet(String theVersion, String theBaseDir) {
super(theVersion, theBaseDir);
super.setFilenameSuffix("Dt");
}
@Override
protected String getTemplate() {
String template = super.getTemplate();
if (template != null) {
return template;
}
return "dstu".equals(getVersion()) ? "/vm/dt_composite_dstu.vm" : "/vm/dt_composite.vm";
}
@Override
protected File getTemplateFile() {
return null;
}
@Override
protected String getFilenameSuffix() {
return "Dt";
}
@Override
protected Collection<InputStream> getInputStreams() {
ArrayList<InputStream> retVal = new ArrayList<InputStream>();

View File

@ -40,6 +40,7 @@ public class ProfileParser extends BaseStructureParser {
public ProfileParser(String theVersion, String theBaseDir) {
super(theVersion, theBaseDir);
super.setFilenameSuffix("");
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ProfileParser.class);
@ -53,13 +54,12 @@ public class ProfileParser extends BaseStructureParser {
return null;
}
@Override
protected String getFilenameSuffix() {
return "";
}
@Override
protected String getTemplate() {
String template = super.getTemplate();
if (template != null) {
return template;
}
return "dstu".equals(getVersion()) ? "/vm/resource_dstu.vm" : "/vm/resource.vm";
}

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.tinder.parser;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
@ -13,14 +12,12 @@ import ca.uhn.fhir.tinder.model.BaseRootType;
import ca.uhn.fhir.tinder.model.Resource;
public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetParser {
private String myFilenameSuffix = "";
private List<String> myInputStreamNames;
private ArrayList<InputStream> myInputStreams;
private String myTemplate = null;
private File myTemplateFile = null;
public ResourceGeneratorUsingSpreadsheet(String theVersion, String theBaseDir) {
super(theVersion, theBaseDir);
super.setFilenameSuffix("");
}
public List<String> getInputStreamNames() {
@ -68,28 +65,11 @@ public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP
}
}
public void setFilenameSuffix(String theFilenameSuffix) {
myFilenameSuffix = theFilenameSuffix;
}
public void setTemplate(String theTemplate) {
myTemplate = theTemplate;
}
public void setTemplateFile (File theTemplateFile) {
myTemplateFile = theTemplateFile;
}
@Override
protected BaseRootType createRootType() {
return new Resource();
}
@Override
protected String getFilenameSuffix() {
return myFilenameSuffix;
}
@Override
protected Collection<InputStream> getInputStreams() {
return myInputStreams;
@ -97,8 +77,9 @@ public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP
@Override
protected String getTemplate() {
if (myTemplate != null) {
return myTemplate;
String template = super.getTemplate();
if (template != null) {
return template;
} else if ("dstu".equals(getVersion())) {
return "/vm/resource_dstu.vm";
} else {
@ -106,11 +87,6 @@ public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP
}
}
@Override
protected File getTemplateFile() {
return myTemplateFile;
}
@Override
protected boolean isSpreadsheet(String theFileName) {
return theFileName.endsWith("spreadsheet.xml");

View File

@ -0,0 +1,22 @@
/**
*
*/
package ca.uhn.fhir.tinder.parser;
/**
* @author Bill.Denton
*
*/
public enum TargetType {
/*
* The primary target of a generator is Java source code
* but others might also be generated.
*/
SOURCE,
/*
* The generator will primarilly produce non-source
* files that should be added to Maven Resources
*/
RESOURCE
}