package ca.uhn.fhir.tinder.parser;
import static org.apache.commons.lang.StringUtils.capitalize;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoFailureException;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.resource.Profile.ExtensionDefn;
import ca.uhn.fhir.model.dstu.resource.Profile.Structure;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElement;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinition;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureSearchParam;
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.tinder.model.AnyChild;
import ca.uhn.fhir.tinder.model.BaseElement;
import ca.uhn.fhir.tinder.model.BaseRootType;
import ca.uhn.fhir.tinder.model.Child;
import ca.uhn.fhir.tinder.model.Resource;
import ca.uhn.fhir.tinder.model.ResourceBlock;
import ca.uhn.fhir.tinder.model.SearchParameter;
import ca.uhn.fhir.tinder.model.SimpleChild;
import ca.uhn.fhir.tinder.model.Slicing;
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);
private ExtensionDefn findExtension(Profile theProfile, String theCode) {
for (ExtensionDefn next : theProfile.getExtensionDefn()) {
if (theCode.equals(next.getCode().getValue())) {
return next;
}
}
return null;
}
@Override
protected String getTemplate() {
String template = super.getTemplate();
if (template != null) {
return template;
}
return "dstu".equals(getVersion()) ? "/vm/resource_dstu.vm" : "/vm/resource.vm";
}
@Override
protected File getTemplateFile() {
return null;
}
public void parseSingleProfile(File theProfile, String theHttpUrl) throws MojoFailureException {
String profileString;
try {
profileString = IOUtils.toString(new FileReader(theProfile));
} catch (IOException e) {
throw new MojoFailureException("Failed to load: " + theProfile, e);
}
FhirContext ctx = new FhirContext(Profile.class);
Profile profile = ctx.newXmlParser().parseResource(Profile.class, profileString);
try {
parseSingleProfile(profile, theHttpUrl);
} catch (Exception e) {
throw new MojoFailureException("Failed to parse profile", e);
}
}
public void parseBaseResources(List<String> theBaseResourceNames, String theHttpUrl) throws MojoFailureException {
FhirContext fhirContext = new FhirContext(Profile.class);
for (String nextFileName : theBaseResourceNames) {
ourLog.info("Parsing file: {}", nextFileName);
Profile profile;
try {
profile = (Profile) fhirContext.newXmlParser().parseResource(IOUtils.toString(new FileReader(nextFileName)));
} catch (Exception e) {
throw new MojoFailureException("Failed to load or parse file: " + nextFileName, e);
}
try {
parseSingleProfile(profile, theHttpUrl);
} catch (Exception e) {
throw new MojoFailureException("Failed to process file: " + nextFileName, e);
}
}
// for (int i = 0; i < theBaseResourceNames.size(); i++) {
// theBaseResourceNames.set(i,
// theBaseResourceNames.get(i).toLowerCase());
// }
//
// try {
//
// Bundle bundle =
// fhirContext.newXmlParser().parseBundle(IOUtils.toString(getClass().getResourceAsStream("/prof/allprofiles.xml")));
// TreeSet<String> allProfiles = new TreeSet<String>();
// for (BundleEntry nextResource : bundle.getEntries() ) {
// Profile nextProfile = (Profile) nextResource.getResource();
// allProfiles.add(nextProfile.getName().getValue());
// if
// (theBaseResourceNames.contains(nextProfile.getName().getValue().toLowerCase())){
// parseSingleProfile(nextProfile,
// bundle.getLinkBase().getValueNotNull());
// }
// }
//
// ourLog.info("Base profiles found: {}", allProfiles);
//
// } catch (Exception e) {
// throw new MojoFailureException("Failed to load base resources", e);
// }
}
public BaseRootType parseSingleProfile(Profile theProfile, String theUrlTOThisProfile) throws Exception {
BaseRootType retVal = null;
for (Structure nextStructure : theProfile.getStructure()) {
int elemIdx = 0;
Map<String, BaseElement> elements = new HashMap<String, BaseElement>();
for (StructureElement next : nextStructure.getElement()) {
BaseElement elem;
if (elemIdx == 0) {
retVal = new Resource();
retVal.setProfile(theProfile.getIdentifier().getValue());
if (retVal.getProfile() == null) {
retVal.setProfile(theUrlTOThisProfile);
}
for (StructureSearchParam nextParam : nextStructure.getSearchParam()) {
SearchParameter param = new SearchParameter(getVersion(), retVal.getName());
param.setName(nextParam.getName().getValue());
String path = defaultString(nextParam.getXpath().getValue());
path=path.replace("/f:", ".").replace("f:", "");
param.setPath(path);
param.setType(nextParam.getType().getValue());
param.setDescription(nextParam.getDocumentation().getValue());
retVal.addSearchParameter(param);
}
addResource(retVal);
elem = retVal;
// below StringUtils.isBlank(type) || type.startsWith("=")
} else {
if (next.getDefinition().getType().isEmpty()) {
elem = new ResourceBlock();
// } else if (type.startsWith("@")) {
// elem = new ResourceBlockCopy();
} else if (next.getDefinition().getType().get(0).getCode().getValue().equals("*")) {
elem = new AnyChild();
// } else if (next.getDefinition().getType().get(0).getCode().getValue().equals("Extension")) {
// elem = new UndeclaredExtensionChild();
} else {
elem = new SimpleChild();
}
}
boolean allResourceReferences = next.getDefinition().getType().size() > 0;
for (StructureElementDefinitionType nextType : next.getDefinition().getType()) {
if (nextType.getCode().getValueAsEnum() != DataTypeEnum.RESOURCEREFERENCE) {
allResourceReferences = false;
}
}
populateNewElement(next, elem, allResourceReferences);
StructureElementDefinition definition = next.getDefinition();
BaseElement parentElement = elements.get(elem.getElementParentName());
if (next.getSlicing().getDiscriminator().getValue() != null) {
Slicing slicing = new Slicing();
slicing.setDiscriminator(next.getSlicing().getDiscriminator().getValue());
if (parentElement.getChildElementNameToSlicing().get(elem.getName()) != null) {
throw new ConfigurationException("Found multiple slicing definitions for path: " + next.getPath().getValue());
}
parentElement.getChildElementNameToSlicing().put(elem.getName(), slicing);
continue;
}
Slicing childIsSliced = parentElement != null ? parentElement.getChildElementNameToSlicing().get(elem.getName()) : null;
/*
* Profiles come with a number of standard elements which are generally ignored because they are boilerplate, unless the definition is somehow changing their behaviour (e.g. through
* slices)
*/
if (next.getPath().getValue().endsWith(".contained")) {
continue;
}
if (next.getPath().getValue().endsWith(".text")) {
continue;
}
if (next.getPath().getValue().endsWith(".extension")) {
if (childIsSliced != null) {
if (!"url".equals(childIsSliced.getDiscriminator())) {
throw new ConfigurationException("Extensions must be sliced on 'url' discriminator. Found: " + next.getSlicing().getDiscriminator().getValue());
}
if (next.getDefinition().getType().size() != 1 || next.getDefinition().getType().get(0).getCode().getValueAsEnum() != DataTypeEnum.EXTENSION) {
throw new ConfigurationException("Extension slices must have a single type with a code of 'Extension'");
}
String name = next.getName().getValue();
if (StringUtils.isBlank(name)) {
throw new ConfigurationException("Extension slices must have a 'name' defined, none found at path: " + next.getPath());
}
elem.setName(name);
elem.setElementName(name);
// elem = new Extension();
// populateNewElement(next, elem, allResourceReferences);
String profile = next.getDefinition().getType().get(0).getProfile().getValueAsString();
if (isBlank(profile)) {
throw new ConfigurationException("Extension slice for " + next.getPath().getValue() + " has no profile specified in its type");
}
if (profile.startsWith("#")) {
Profile.ExtensionDefn extension = findExtension(theProfile, profile.substring(1));
if (extension == null) {
throw new ConfigurationException("Unknown local extension reference: " + profile);
}
ourLog.info("Element at path {} is using extension {}", next.getPath(), profile);
definition = extension.getDefinition();
String extensionUrl = theUrlTOThisProfile + profile;
elem.setExtensionUrl(extensionUrl);
} else {
// TODO: implement this
throw new ConfigurationException("Extensions specified outside of the given profile are not yet supported");
}
} else {
continue;
}
}
if (next.getPath().getValue().endsWith(".modifierExtension")) {
continue;
}
for (StructureElementDefinitionType nextType : definition.getType()) {
if (nextType.getCode().getValueAsEnum() == DataTypeEnum.RESOURCEREFERENCE) {
if (nextType.getProfile().getValueAsString().startsWith("http://hl7.org/fhir/profiles/")) {
elem.getType().add(capitalize(nextType.getProfile().getValueAsString().substring("http://hl7.org/fhir/profiles/".length())));
} else {
// TODO: implement this.. we need to be able to
// reference other profiles
throw new ConfigurationException("Profile type not yet supported");
}
} else {
elem.getType().add(capitalize(nextType.getCode().getValue()) + "Dt");
}
}
elem.setBinding(definition.getBinding().getName().getValue());
elem.setShortName(definition.getShort().getValue());
elem.setDefinition(definition.getFormal().getValue());
elem.setRequirement(definition.getRequirements().getValue());
elem.setCardMin(definition.getMin().getValueAsString());
elem.setCardMax(definition.getMax().getValue());
if (elem instanceof Child) {
Child child = (Child) elem;
elements.put(elem.getName(), elem);
if (parentElement == null) {
throw new Exception("Can't find element " + elem.getElementParentName() + " - Valid values are: " + elements.keySet());
}
parentElement.addChild(child);
/*
* Find simple setters
*/
scanForSimpleSetters(child);
} else {
BaseRootType res = (BaseRootType) elem;
elements.put(res.getName(), res);
}
elemIdx++;
}
}
return retVal;
}
private void populateNewElement(StructureElement next, BaseElement elem, boolean allResourceReferences) {
elem.setName(next.getPath().getValue());
elem.setElementNameAndDeriveParentElementName(next.getPath().getValue());
elem.setResourceRef(allResourceReferences);
}
public static void main(String[] args) throws Exception {
IParser parser = new FhirContext(Profile.class).newXmlParser();
ProfileParser pp = new ProfileParser("dev",".");
String str = IOUtils.toString(new FileReader("../hapi-tinder-test/src/test/resources/profile/organization.xml"));
Profile prof = parser.parseResource(Profile.class, str);
pp.parseSingleProfile(prof, "http://foo");
str = IOUtils.toString(new FileReader("../hapi-tinder-test/src/test/resources/profile/patient.xml"));
prof = parser.parseResource(Profile.class, str);
pp.parseSingleProfile(prof, "http://foo");
pp.markResourcesForImports();
pp.writeAll(new File("target/gen/test/resource"), null,"test");
}
}