Merge remote-tracking branch 'upstream/master' into osgi-pull-request

This commit is contained in:
jamesagnew 2015-09-09 08:41:45 -04:00
commit aa8334a99a
2507 changed files with 1066582 additions and 1039195 deletions

2
.gitignore vendored
View File

@ -13,6 +13,7 @@ Servers/
nohup.out
.DS_Store
*.orig
tmp.txt
# Vagrant stuff.
.vagrant
@ -33,6 +34,7 @@ nohup.out
*.jar
*.war
*.ear
*.class
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

View File

@ -13,8 +13,12 @@ cache:
- '$HOME/.m2/repository'
install: /bin/true
# This seems to be required to get travis to set Xmx4g, per https://github.com/travis-ci/travis-ci/issues/3893
before_script:
- export MAVEN_SKIP_RC=true
script:
- mvn -B clean install && cd hapi-fhir-cobertura && mvn -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID -P COBERTURA clean cobertura:cobertura coveralls:report
# - mvn -B clean install -Dcobertura.skip=true && mvn -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID -P COBERTURA clean cobertura:cobertura coveralls:report
- mvn -B clean install && cd hapi-fhir-cobertura && mvn -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID -P COBERTURA clean test jacoco:report coveralls:report

View File

@ -6,6 +6,8 @@ HAPI FHIR - Java API for HL7 FHIR Clients and Servers
[![Build Status](https://travis-ci.org/jamesagnew/hapi-fhir.svg?branch=master)](https://travis-ci.org/jamesagnew/hapi-fhir)
[![Coverage Status](https://coveralls.io/repos/jamesagnew/hapi-fhir/badge.svg?branch=master&service=github)](https://coveralls.io/github/jamesagnew/hapi-fhir?branch=master)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg)](http://search.maven.org/#search|ga|1|ca.uhn.hapi.fhir)
[![Dependency Status](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e)
[![License](https://img.shields.io/badge/license-apache%202.0-ff69b4.svg)](http://jamesagnew.github.io/hapi-fhir/license.html)
Complete project documentation is available here:
http://jamesagnew.github.io/hapi-fhir/

View File

@ -31,15 +31,14 @@
<version>1.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit_version}</version>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.SimpleQuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
@ -128,8 +129,8 @@ public class FhirDataModel {
// If you need to set other fields (such as the display name) after
// using the Enum type, you may still do so.
patient.getMaritalStatus().getCodingFirstRep().setDisplay("Married");
patient.getMaritalStatus().getCodingFirstRep().setPrimary(true);
patient.getMaritalStatus().getCodingFirstRep().setVersion("1.0");
patient.getMaritalStatus().getCodingFirstRep().setUserSelected(true);
// You can use accessors to retrieve values from CodeableConcept fields
@ -167,8 +168,8 @@ public class FhirDataModel {
observation.setValue(q);
// Set the reference range
observation.getReferenceRangeFirstRep().setLow(new QuantityDt(100));
observation.getReferenceRangeFirstRep().setHigh(new QuantityDt(200));
observation.getReferenceRangeFirstRep().setLow(new SimpleQuantityDt(100));
observation.getReferenceRangeFirstRep().setHigh(new SimpleQuantityDt(200));
// END SNIPPET: observation

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
@ -228,7 +229,7 @@ public class GenericClientExample {
}
{
// START SNIPPET: search
Bundle response = client.search()
ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search()
.forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.CAREPROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
@ -259,10 +260,28 @@ public class GenericClientExample {
.forResource(Patient.class)
.withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(Bundle.class)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
// END SNIPPET: searchCompartment
// START SNIPPET: searchSubsetSummary
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.summaryMode(SummaryEnum.TRUE)
.execute();
// END SNIPPET: searchSubsetSummary
// START SNIPPET: searchSubsetElements
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.elementsSubset("identifier", "name") // only include the identifier and name
.execute();
// END SNIPPET: searchSubsetElements
// START SNIPPET: searchAdv
response = client.search()
.forResource(Patient.class)
@ -270,7 +289,8 @@ public class GenericClientExample {
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
.withTag("http://acme.org/codes", "needs-review")
.include(Patient.INCLUDE_ORGANIZATION)
.include(Patient.INCLUDE_ORGANIZATION.asRecursive())
.include(Patient.INCLUDE_CAREPROVIDER.asNonRecursive())
.revInclude(Provenance.INCLUDE_TARGET)
.lastUpdated(new DateRangeParam("2011-01-01", null))
.sort().ascending(Patient.BIRTHDATE)

View File

@ -2,14 +2,11 @@ package example;
import java.io.IOException;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.CustomThymeleafNarrativeGenerator;
public class NarrativeGenerator {
@Test
public void testGenerator() throws IOException {
//START SNIPPET: gen

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.DeleteTags;
import ca.uhn.fhir.rest.annotation.Elements;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
@ -59,6 +60,7 @@ import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
@ -102,6 +104,17 @@ public List<Organization> getAllOrganizations() {
}
//END SNIPPET: searchAll
//START SNIPPET: summaryAndElements
@Search
public List<Patient> search(
SummaryEnum theSummary, // will receive the summary (no annotation required)
@Elements Set<String> theElements // (requires the @Elements annotation)
) {
return null; // todo: populate
}
//END SNIPPET: summaryAndElements
//START SNIPPET: searchCompartment
public class PatientRp implements IResourceProvider {
@ -705,7 +718,7 @@ public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
// You can also add an OperationOutcome resource to return
// This part is optional though:
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue().setDetails("One minor issue detected");
outcome.addIssue().setDiagnostics("One minor issue detected");
retVal.setOperationOutcome(outcome);
return retVal;
@ -841,7 +854,7 @@ public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient t
// You can also add an OperationOutcome resource to return
// This part is optional though:
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue().setDetails("One minor issue detected");
outcome.addIssue().setDiagnostics("One minor issue detected");
retVal.setOperationOutcome(outcome);
// If your server supports creating resources during an update if they don't already exist
@ -881,7 +894,7 @@ public MethodOutcome validatePatient(@ResourceParam Patient thePatient,
// You may also add an OperationOutcome resource to return
// This part is optional though:
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("One minor issue detected");
outcome.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDiagnostics("One minor issue detected");
retVal.setOperationOutcome(outcome);
return retVal;
@ -1127,7 +1140,7 @@ public List<IResource> transaction(@TransactionParam List<IResource> theResource
// If wanted, you may optionally also return an OperationOutcome resource
// If present, the OperationOutcome must come first in the returned list.
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Completed successfully");
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Completed successfully");
retVal.add(0, oo);
return retVal;

View File

@ -8,17 +8,22 @@ import javax.servlet.ServletException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.validation.FhirInstanceValidator;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidationSupport;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
@ -114,10 +119,88 @@ public class ValidatorExamples {
}
public static void main(String[] args) throws Exception {
validateFiles();
instanceValidator();
}
private static void instanceValidator() throws Exception {
// START SNIPPET: instanceValidator
FhirContext ctx = FhirContext.forDstu2();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
/*
* Let's create a resource to validate. This Observation has some fields
* populated, but it is missing Observation.status, which is mandatory.
*/
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://loinc.org").setCode("12345-6");
obs.setValue(new StringDt("This is a value"));
// Validate
ValidationResult result = validator.validateWithResult(obs);
// Do we have any errors or fatal errors?
System.out.println(result.isSuccessful()); // false
// Show the issues
for (SingleValidationMessage next : result.getMessages()) {
System.out.println(" Next issue " + next.getSeverity() + " - " + next.getLocationString() + " - " + next.getMessage());
}
// Prints:
// Next issue ERROR - /f:Observation - Element '/f:Observation.status': minimum required = 1, but only found 0
// Next issue WARNING - /f:Observation/f:code - Unable to validate code "12345-6" in code system "http://loinc.org"
// You can also convert the result into an operation outcome if you
// need to return one from a server
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
// END SNIPPET: instanceValidator
}
private static void instanceValidatorCustom() throws Exception {
// START SNIPPET: instanceValidatorCustom
FhirContext ctx = FhirContext.forDstu2();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
IValidationSupport valSupport = new IValidationSupport() {
@Override
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
// TODO: Implement
return null;
}
@Override
public boolean isCodeSystemSupported(String theSystem) {
// TODO: Implement
return false;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
// TODO: Implement
return null;
}
@Override
public ValueSet fetchCodeSystem(String theSystem) {
// TODO: Implement
return null;
}
};
instanceValidator.setValidationSupport(valSupport);
// END SNIPPET: instanceValidatorCustom
}
@SuppressWarnings("unused")
private static void validateFiles() throws Exception {
// START SNIPPET: validateFiles
FhirContext ctx = FhirContext.forDstu2();

View File

@ -23,5 +23,6 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="target/hapi-fhir-android-1.2-SNAPSHOT.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -20,6 +20,7 @@
<artifactId>hapi-fhir-base</artifactId>
<version>1.2-SNAPSHOT</version>
<exclusions>
<!--
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
@ -28,6 +29,7 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
-->
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
@ -38,45 +40,38 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2-SNAPSHOT</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
<version>${phloc_schematron_version}</version>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-commons</artifactId>
<version>${phloc_commons_version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-android</artifactId>
<version>4.3.5.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-android</artifactId>
<version>${slf4j_version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons_io_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Android does not come with the Servlet API bundled, and MethodUtil
@ -84,19 +79,9 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet_api_version}</version>
<scope>compile</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit_version}</version>
<scope>test</scope>
</dependency>
<!-- <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId>
<version>${commons_io_version}</version> <scope>test</scope> </dependency> -->
</dependencies>
<build>
@ -111,12 +96,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven_failsafe_plugin_version}</version>
<configuration>
<classpathDependencyScopeExclude>compile+runtime</classpathDependencyScopeExclude>
<classpathDependencyScopeExclude>compile+runtime+test+provided</classpathDependencyScopeExclude>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.directory}/hapi-fhir-android-${project.version}.jar</additionalClasspathElement>
<additionalClasspathElement>${project.build.directory}/hapi-fhir-android-${project.version}-dstu2.jar</additionalClasspathElement>
</additionalClasspathElements>
<classpathDependencyExcludes>
<classpathDependencyExclude>ca.uhn.hapi.fhir:hapi-fhir-base</classpathDependencyExclude>
<classpathDependencyExclude>org.codehaus.woodstox:*</classpathDependencyExclude>
<classpathDependencyExclude>javax.json:*</classpathDependencyExclude>
<classpathDependencyExclude>org.glassfish:javax.json</classpathDependencyExclude>
</classpathDependencyExcludes>
</configuration>
<executions>
<execution>
@ -133,27 +123,73 @@
<version>2.3</version>
<executions>
<execution>
<id>normal</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<!-- <minimizeJar>true</minimizeJar> -->
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<artifactSet>
<includes>
<include>commons-codec:commons-codec</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<!--<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2</include>-->
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<include>javax.servlet:javax.servlet-api</include>
<include>org.codehaus.woodstox:stax2-api</include>
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-base</artifact>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
<execution>
<id>dstu</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>dstu</shadedClassifierName>
<artifactSet>
<includes>
<!--
<include>commons-codec:commons-codec</include>
-->
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<!-- <include>javax.servlet:javax.servlet-api</include>-->
<include>org.codehaus.woodstox:stax2-api</include>
<!-- <include>org.slf4j:slf4j*</include> -->
<!--
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
-->
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
@ -178,6 +214,48 @@
</filters>
</configuration>
</execution>
<execution>
<id>dstu2</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>dstu2</shadedClassifierName>
<artifactSet>
<includes>
<!--
<include>commons-codec:commons-codec</include>
-->
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<!-- <include>javax.servlet:javax.servlet-api</include>-->
<include>org.codehaus.woodstox:stax2-api</include>
<!--
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
-->
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

View File

@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Observable;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
@ -14,9 +15,15 @@ import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.junit.BeforeClass;
import org.junit.Test;
import com.ctc.wstx.stax.WstxInputFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
@ -24,8 +31,16 @@ import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
public class BuiltJarIT {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarIT.class);
@BeforeClass
public static void beforeClass() {
System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
}
@Test
public void testParser() {
public void testParserXml() throws Exception {
// fail("*******: " + WstxInputFactory.class.getProtectionDomain().getCodeSource().getLocation().toString());
FhirContext ctx = FhirContext.forDstu2();
Patient p = new Patient();
@ -37,6 +52,26 @@ public class BuiltJarIT {
assertEquals("system", p2.getIdentifierFirstRep().getSystemElement().getValueAsString());
}
@Test
public void testParserJson() {
FhirContext ctx = FhirContext.forDstu2();
Observation o = new Observation();
o.getCode().setText("TEXT");
o.setValue(new QuantityDt(123));
o.addIdentifier().setSystem("system");
String str = ctx.newJsonParser().encodeResourceToString(o);
Observation p2 = ctx.newJsonParser().parseResource(Observation.class, str);
assertEquals("TEXT", p2.getCode().getText());
QuantityDt dt = (QuantityDt) p2.getValue();
dt.getComparatorElement().getValueAsEnum();
QuantityCompararatorEnum.GREATERTHAN.name();
}
/**
* A simple client test - We try to connect to a server that doesn't exist, but
* if we at least get the right exception it means we made it up to the HTTP/network stack
@ -113,7 +148,7 @@ public class BuiltJarIT {
ourLog.info("File {} contains {} entries", file, names.size());
ourLog.info("Total classes {} - Total methods {}", totalClasses, totalMethods);
ourLog.info("Top classes {}", new ArrayList<ClassMethodCount>(topMethods).subList(topMethods.size() - 10, topMethods.size()));
ourLog.info("Top classes {}", new ArrayList<ClassMethodCount>(topMethods).subList(Math.max(0,topMethods.size() - 10), topMethods.size()));
} finally {
zip.close();

View File

@ -1,5 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@ -15,24 +14,23 @@
<name>HAPI FHIR - Minimal Dependency Test - Client</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit_version}</version>
</dependency>
<!-- Use an older version of SLF4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.0</version>
<version>1.6.0</version><!--$NO-MVN-MAN-VER$ -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.0</version>
</dependency>
<!--
Woodstox note: The hapi-fhir-base-testmindeps-client project includes no Woodstox at all, so that we use the
StAX version that's bundled with the JDK. The hapi-fhir-base-testmindeps-server
project uses a really old Woodstox version so that we test that.
-->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
@ -70,14 +68,7 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>${hamcrest_version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -15,15 +15,10 @@
<name>HAPI FHIR - Minimal Dependency Test - Server</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit_version}</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version> <!-- 6.1.26 -->
<version>6.1.26</version> <!-- Use an old version -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@ -31,6 +26,17 @@
<version>1.7.7</version>
</dependency>
<!--
Woodstox note: The hapi-fhir-base-testmindeps-client project includes no Woodstox at all, so that we use the
StAX version that's bundled with the JDK. The hapi-fhir-base-testmindeps-server
project uses a really old Woodstox version so that we test that.
-->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>wstx-asl</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
@ -69,13 +75,6 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>${hamcrest_version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -5,10 +5,9 @@ import org.junit.Test;
public class SetupTest {
/**
* Ensure that Woodstox is not on the classpath (we're testing that the library works ok without it
* elsewhere)
* Ensure that Woodstox is on the classpath
*/
@Test(expected=ClassNotFoundException.class)
@Test()
public void testValidateEnvironment() throws ClassNotFoundException {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
}

View File

@ -1,10 +1,14 @@
package ca.uhn.fhir.testmindeps;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.validation.FhirValidator;
public class ValidatorTest {
@ -15,6 +19,8 @@ public class ValidatorTest {
FhirContext ctx = new FhirContext();
FhirValidator val = ctx.newValidator();
val.validateWithResult(new Patient());
// Phloc is not onthe classpath
assertTrue(val.isValidateAgainstStandardSchema());
assertFalse(val.isValidateAgainstStandardSchematron());

View File

@ -22,33 +22,22 @@
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.4</version>
</dependency>
<!-- XML -->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>${woodstox_version}</version>
</dependency>
<!-- Only required for OpenID Connect Support -->
<!-- <dependency> <groupId>org.mitre</groupId> <artifactId>openid-connect-client</artifactId>
<version>${mitreid-connect-version}</version> <optional>true</optional> </dependency>
<dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId>
<version>2.0.2.RELEASE</version> <optional>true</optional> </dependency> -->
<!-- -->
<!-- Only required for narrative generator support -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>${thymeleaf-version}</version>
<optional>true</optional>
</dependency>
@ -56,7 +45,6 @@
<dependency>
<groupId>org.ebaysf.web</groupId>
<artifactId>cors-filter</artifactId>
<version>${ebay_cors_filter_version}</version>
<optional>true</optional>
<!-- <exclusions> <exclusion> <artifactId>servlet-api</artifactId> <groupId>javax.servlet</groupId>
</exclusion> </exclusions> -->
@ -66,13 +54,11 @@
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
<version>${phloc_schematron_version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-commons</artifactId>
<version>${phloc_commons_version}</version>
<optional>true</optional>
</dependency>
@ -83,34 +69,28 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons_lang_version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons_codec_version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons_io_version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j_version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j_version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback_version}</version>
<optional>true</optional>
</dependency>
@ -118,7 +98,6 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${apache_httpclient_version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
@ -129,13 +108,11 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${apache_httpcore_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring_version}</version>
<optional>true</optional>
<exclusions>
<exclusion>
@ -149,18 +126,9 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet_api_version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit_version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -30,39 +30,14 @@ public abstract class BaseRuntimeChildDefinition {
public abstract IAccessor getAccessor();
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getElementName() + "]";
}
public abstract BaseRuntimeElementDefinition<?> getChildByName(String theName);
public abstract BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theType);
public abstract String getChildNameByDatatype(Class<? extends IBase> theDatatype);
public abstract IMutator getMutator();
public abstract Set<String> getValidChildNames();
abstract void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions);
public interface IAccessor {
List<IBase> getValues(Object theTarget);
}
public abstract String getElementName();
public abstract int getMax();
public abstract int getMin();
public interface IMutator {
void setValue(Object theTarget, IBase theValue);
void addValue(Object theTarget, IBase theValue);
}
public String getExtensionUrl() {
return null;
}
@ -71,6 +46,33 @@ public abstract class BaseRuntimeChildDefinition {
return null;
}
public abstract int getMax();
public abstract int getMin();
public abstract IMutator getMutator();
public abstract Set<String> getValidChildNames();
public abstract boolean isSummary();
abstract void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions);
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getElementName() + "]";
}
public interface IAccessor {
List<IBase> getValues(Object theTarget);
}
public interface IMutator {
void addValue(Object theTarget, IBase theValue);
void setValue(Object theTarget, IBase theValue);
}
// public String getExtensionUrl() {
// return null;
// }

View File

@ -44,8 +44,10 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
private final String myFormalDefinition;
private final int myMax;
private final int myMin;
private boolean myModifier;
private final IMutator myMutator;
private final String myShortDefinition;
private boolean mySummary;
private Boolean ourUseMethodAccessors;
BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
@ -66,6 +68,8 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
myField = theField;
myMin = theChildAnnotation.min();
myMax = theChildAnnotation.max();
mySummary = theChildAnnotation.summary();
myModifier = theChildAnnotation.modifier();
myElementName = theElementName;
if (theDescriptionAnnotation != null) {
myShortDefinition = theDescriptionAnnotation.shortDefinition();
@ -186,6 +190,14 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
return getChildByName(getValidChildNames().iterator().next());
}
public boolean isModifier() {
return myModifier;
}
public boolean isSummary() {
return mySummary;
}
private static Method findMutator(Class<?> theDeclaringClass, Class<?> theTargetReturnType, String theElementName) {
String methodName = "set" + WordUtils.capitalize(theElementName);
try {

View File

@ -340,7 +340,7 @@ public class FhirContext {
* cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
* "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its
* sub-interface {@link IBasicClient}). See the <a
* href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more
* href="http://jamesagnew.github.io/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more
* information on how to define this interface.
*
* <p>

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.context;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
/*
* #%L
* HAPI FHIR - Core Library
@ -24,4 +27,10 @@ public interface IRuntimeDatatypeDefinition {
boolean isSpecialization();
public BaseRuntimeElementDefinition<?> getProfileOf();
boolean isProfileOf(Class<? extends IBaseDatatype> theType);
public Class<? extends IBase> getImplementingClass();
}

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.context;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.IOException;
import java.io.InputStream;
@ -53,7 +52,6 @@ import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
import org.hl7.fhir.instance.model.api.IBaseEnumFactory;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseReference;
@ -328,7 +326,8 @@ class ModelScanner {
Class<? extends IPrimitiveType<?>> resClass = (Class<? extends IPrimitiveType<?>>) theClass;
scanPrimitiveDatatype(resClass, datatypeDefinition);
} else {
throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName());
return;
// throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName());
}
}
@ -627,12 +626,6 @@ class ModelScanner {
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
Class<?> binderType = ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
} else if (childAnnotation.enumFactory().getSimpleName().equals("NoEnumFactory") == false) {
Class<? extends IBaseEnumFactory<?>> enumFactory = childAnnotation.enumFactory();
def = new RuntimeChildEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, enumFactory);
// } else if ("id".equals(elementName) && IIdType.class.isAssignableFrom(nextDatatype)) {
// def = new RuntimeChildIdDatatypeDefinition(next, elementName, descriptionAnnotation,
// childAnnotation, nextDatatype);
} else {
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
}
@ -813,6 +806,8 @@ class ModelScanner {
try {
// Datatypes
ourLog.warn("NEXT: {}", nextValue);
@SuppressWarnings("unchecked")
Class<? extends IBase> dtType = (Class<? extends IBase>) Class.forName(nextValue);
retVal.add(dtType);

View File

@ -86,27 +86,44 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
String elementName;
BaseRuntimeElementDefinition<?> nextDef;
boolean nonPreferred = false;
if (IBaseResource.class.isAssignableFrom(next)) {
elementName = getElementName() + StringUtils.capitalize(next.getSimpleName());
List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
types.add((Class<? extends IBaseResource>) next);
nextDef = new RuntimeResourceReferenceDefinition(elementName, types, false);
nextDef.sealAndInitialize(theContext, theClassToElementDefinitions);
myNameToChildDefinition.put(getElementName() + "Reference", nextDef);
myNameToChildDefinition.put(getElementName() + "Resource", nextDef);
} else {
nextDef = theClassToElementDefinitions.get(next);
elementName = getElementName() + StringUtils.capitalize(nextDef.getName());
BaseRuntimeElementDefinition<?> nextDefForChoice = nextDef;
if (nextDef instanceof IRuntimeDatatypeDefinition) {
IRuntimeDatatypeDefinition nextDefDatatype = (IRuntimeDatatypeDefinition) nextDef;
if (nextDefDatatype.getProfileOf() != null) {
/*
* Elements which are called foo[x] and have a choice which is a profiled datatype must use the
* unprofiled datatype as the element name. E.g. if foo[x] allows markdown as a datatype, it calls the
* element fooString when encoded, because markdown is a profile of string. This is according to the
* FHIR spec
*/
nextDefForChoice = nextDefDatatype.getProfileOf();
nonPreferred = true;
}
}
elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName());
}
if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) {
myNameToChildDefinition.put(elementName, nextDef);
}
myNameToChildDefinition.put(elementName, nextDef);
if (IBaseResource.class.isAssignableFrom(next)) {
Class<? extends IBase> refType = theContext.getVersion().getResourceReferenceType();
myDatatypeToElementDefinition.put(refType, nextDef);
String alternateElementName;
if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
alternateElementName = getElementName() + "Resource";
@ -115,7 +132,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
}
myDatatypeToElementName.put(refType, alternateElementName);
}
myDatatypeToElementDefinition.put(next, nextDef);
myDatatypeToElementName.put(next, elementName);
}

View File

@ -1,59 +0,0 @@
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.lang.reflect.Field;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumFactory;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
public class RuntimeChildEnumerationDatatypeDefinition extends RuntimeChildPrimitiveDatatypeDefinition {
private Class<? extends IBaseEnumFactory<?>> myBinderType;
private volatile IBaseEnumFactory<?> myBinder;
public RuntimeChildEnumerationDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype,
Class<? extends IBaseEnumFactory<?>> theBinderType) {
super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype);
myBinderType = theBinderType;
}
@Override
public IBaseEnumFactory<?> getInstanceConstructorArguments() {
IBaseEnumFactory<?> retVal = myBinder;
if (retVal == null) {
try {
retVal = myBinderType.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("Failed to instantiate " + myBinderType, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to instantiate " + myBinderType, e);
}
myBinder = retVal;
}
return retVal;
}
}

View File

@ -47,6 +47,25 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
// nothing
}
private void addReferenceBinding(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions, String value) {
List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
types.add(IBaseResource.class);
RuntimeResourceReferenceDefinition def = new RuntimeResourceReferenceDefinition(value, types, false);
def.sealAndInitialize(theContext, theClassToElementDefinitions);
myAttributeNameToDefinition.put(value, def);
/*
* Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
*/
boolean dstu1 = (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1));
if ((dstu1 && (value != VALUE_REFERENCE)) || (!dstu1 && (value != VALUE_RESOURCE))) {
myDatatypeToAttributeName.put(theContext.getVersion().getResourceReferenceType(), value);
myDatatypeToDefinition.put(BaseResourceReferenceDt.class, def);
myDatatypeToDefinition.put(theContext.getVersion().getResourceReferenceType(), def);
}
}
@Override
public IAccessor getAccessor() {
return new IAccessor() {
@ -123,6 +142,11 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
return myAttributeNameToDefinition.keySet();
}
@Override
public boolean isSummary() {
return false;
}
@Override
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
@ -187,25 +211,6 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_REFERENCE);
}
private void addReferenceBinding(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions, String value) {
List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
types.add(IBaseResource.class);
RuntimeResourceReferenceDefinition def = new RuntimeResourceReferenceDefinition(value, types, false);
def.sealAndInitialize(theContext, theClassToElementDefinitions);
myAttributeNameToDefinition.put(value, def);
/*
* Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
*/
boolean dstu1 = (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1));
if ((dstu1 && (value != VALUE_REFERENCE)) || (!dstu1 && (value != VALUE_RESOURCE))) {
myDatatypeToAttributeName.put(theContext.getVersion().getResourceReferenceType(), value);
myDatatypeToDefinition.put(BaseResourceReferenceDt.class, def);
myDatatypeToDefinition.put(theContext.getVersion().getResourceReferenceType(), def);
}
}
public static String createExtensionChildName(BaseRuntimeElementDefinition<?> next) {
String attrName = "value" + WordUtils.capitalize(next.getName());
return attrName;

View File

@ -19,9 +19,12 @@ package ca.uhn.fhir.context;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.ICompositeType;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -30,6 +33,8 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompositeDefinition<ICompositeType> implements IRuntimeDatatypeDefinition {
private boolean mySpecialization;
private Class<? extends IBaseDatatype> myProfileOfType;
private BaseRuntimeElementDefinition<?> myProfileOf;
public RuntimeCompositeDatatypeDefinition(DatatypeDef theDef, Class<? extends ICompositeType> theImplementingClass, boolean theStandardType) {
super(theDef.name(), theImplementingClass, theStandardType);
@ -40,9 +45,30 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
}
mySpecialization = theDef.isSpecialization();
myProfileOfType = theDef.profileOf();
if (myProfileOfType.equals(IBaseDatatype.class)) {
myProfileOfType = null;
}
}
@Override
public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
super.sealAndInitialize(theContext, theClassToElementDefinitions);
if (myProfileOfType != null) {
myProfileOf = theClassToElementDefinitions.get(myProfileOfType);
if (myProfileOf == null) {
throw new ConfigurationException("Unknown profileOf value: " + myProfileOfType);
}
}
}
@Override
public BaseRuntimeElementDefinition<?> getProfileOf() {
return myProfileOf;
}
@Override
public boolean isSpecialization() {
return mySpecialization;
@ -53,5 +79,17 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
return ChildTypeEnum.COMPOSITE_DATATYPE;
}
@Override
public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
if (myProfileOfType != null) {
if (myProfileOfType.equals(theType)) {
return true;
} else if (myProfileOf instanceof IRuntimeDatatypeDefinition) {
return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType);
}
}
return false;
}
}

View File

@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.*;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -32,6 +33,8 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition<IPrimitiveType<?>> implements IRuntimeDatatypeDefinition {
private BaseRuntimeElementDefinition<?> myProfileOf;
private Class<? extends IBaseDatatype> myProfileOfType;
private boolean mySpecialization;
public RuntimePrimitiveDatatypeDefinition(DatatypeDef theDef, Class<? extends IPrimitiveType<?>> theImplementingClass, boolean theStandardType) {
@ -43,6 +46,20 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
}
mySpecialization = theDef.isSpecialization();
myProfileOfType = theDef.profileOf();
if (myProfileOfType.equals(IBaseDatatype.class)) {
myProfileOfType = null;
}
}
@Override
public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
return ChildTypeEnum.PRIMITIVE_DATATYPE;
}
@Override
public BaseRuntimeElementDefinition<?> getProfileOf() {
return myProfileOf;
}
@Override
@ -52,12 +69,26 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
@Override
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
// nothing
super.sealAndInitialize(theContext, theClassToElementDefinitions);
if (myProfileOfType != null) {
myProfileOf = theClassToElementDefinitions.get(myProfileOfType);
if (myProfileOf == null) {
throw new ConfigurationException("Unknown profileOf value: " + myProfileOfType + " in type " + getImplementingClass().getName());
}
}
}
@Override
public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
return ChildTypeEnum.PRIMITIVE_DATATYPE;
public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
if (myProfileOfType != null) {
if (myProfileOfType.equals(theType)) {
return true;
} else if (myProfileOf instanceof IRuntimeDatatypeDefinition) {
return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType);
}
}
return false;
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.model.api;
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ -32,66 +33,56 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
/**
* This interface is the parent interface for all FHIR Resource definition
* classes. Classes implementing this interface should be annotated
* with the {@link ResourceDef @ResourceDef} annotation.
* This interface is the parent interface for all FHIR Resource definition classes. Classes implementing this interface should be annotated with the {@link ResourceDef @ResourceDef} annotation.
*
* <p>
* Note that this class is a part of HAPI's model API, used to define
* structure classes. Users will often interact with this interface, but
* should not need to implement it directly.
* Note that this class is a part of HAPI's model API, used to define structure classes. Users will often interact with this interface, but should not need to implement it directly.
* </p>
*/
public interface IResource extends ICompositeElement, org.hl7.fhir.instance.model.api.IBaseResource {
public static final Include INCLUDE_ALL = new Include("*");
public static final Set<Include> WILDCARD_ALL_SET = new HashSet<Include>(Arrays.asList(INCLUDE_ALL));
/**
* Include constant for <code>*</code> (return all includes)
*/
public static final Include INCLUDE_ALL = new Include("*", false).toLocked();
/**
/**
* Include set containing only {@link #INCLUDE_ALL}
*/
public static final Set<Include> WILDCARD_ALL_SET = Collections.unmodifiableSet(new HashSet<Include>(Arrays.asList(INCLUDE_ALL)));
/**
* Returns the contained resource list for this resource.
* <p>
* Usage note: HAPI will generally populate and use the resources from this
* list automatically (placing inline resources in the contained list when
* encoding, and copying contained resources from this list to their
* appropriate references when parsing) so it is generally not neccesary to
* interact with this list directly. Instead, in a server you can place
* resource instances in reference fields (such as <code>Patient#setManagingOrganization(ResourceReferenceDt)</code> )
* and the resource will be automatically contained. In a client, contained resources will
* be automatically populated into their appropriate fields by the HAPI parser.
* Usage note: HAPI will generally populate and use the resources from this list automatically (placing inline resources in the contained list when encoding, and copying contained resources from
* this list to their appropriate references when parsing) so it is generally not neccesary to interact with this list directly. Instead, in a server you can place resource instances in reference
* fields (such as <code>Patient#setManagingOrganization(ResourceReferenceDt)</code> ) and the resource will be automatically contained. In a client, contained resources will be automatically
* populated into their appropriate fields by the HAPI parser.
* </p>
* TODO: document contained resources and link there
*/
BaseContainedDt getContained();
/**
* Returns the ID of this resource. Note that this identifier is the URL (or a portion
* of the URL) used to access this resource, and is not the same thing as any business
* identifiers stored within the resource. For example, a Patient resource might
* have any number of medical record numbers but these are not stored here.
* Returns the ID of this resource. Note that this identifier is the URL (or a portion of the URL) used to access this resource, and is not the same thing as any business identifiers stored within
* the resource. For example, a Patient resource might have any number of medical record numbers but these are not stored here.
* <p>
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see
* <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
* </p>
*/
IdDt getId();
/**
* Gets the language of the resource itself - <b>NOTE that this language attribute
* applies to the resource itself, it is not (for example) the language spoken by
* a practitioner or patient</b>
* Gets the language of the resource itself - <b>NOTE that this language attribute applies to the resource itself, it is not (for example) the language spoken by a practitioner or patient</b>
*/
CodeDt getLanguage();
/**
* Returns the metadata map for this object, creating it if neccesary.
* Metadata entries are used to get/set feed bundle entries, such as the
* resource version, or the last updated timestamp.
* Returns the metadata map for this object, creating it if neccesary. Metadata entries are used to get/set feed bundle entries, such as the resource version, or the last updated timestamp.
* <p>
* Keys in this map are enumerated in the {@link ResourceMetadataKeyEnum},
* and each key has a specific value type that it must use.
* Keys in this map are enumerated in the {@link ResourceMetadataKeyEnum}, and each key has a specific value type that it must use.
* </p>
*
* @see ResourceMetadataKeyEnum for a list of allowable keys and the object
* types that values of a given key must use.
* @see ResourceMetadataKeyEnum for a list of allowable keys and the object types that values of a given key must use.
*/
ResourceMetadataMap getResourceMetadata();
@ -99,40 +90,31 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
* Returns the narrative block for this resource
*/
BaseNarrativeDt getText();
/**
* Sets the ID of this resource. Note that this identifier is the URL (or a portion
* of the URL) used to access this resource, and is not the same thing as any business
* identifiers stored within the resource. For example, a Patient resource might
* have any number of medical record numbers but these are not stored here.
* Sets the ID of this resource. Note that this identifier is the URL (or a portion of the URL) used to access this resource, and is not the same thing as any business identifiers stored within the
* resource. For example, a Patient resource might have any number of medical record numbers but these are not stored here.
* <p>
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see
* <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
* </p>
*/
void setId(IdDt theId);
/**
* Sets the language of the resource itself - <b>NOTE that this language attribute
* applies to the resource itself, it is not (for example) the language spoken by
* a practitioner or patient</b>
* Sets the language of the resource itself - <b>NOTE that this language attribute applies to the resource itself, it is not (for example) the language spoken by a practitioner or patient</b>
*/
void setLanguage(CodeDt theLanguage);
/**
* Sets the metadata map for this object. Metadata entries are used to
* get/set feed bundle entries, such as the resource version, or the last
* updated timestamp.
* Sets the metadata map for this object. Metadata entries are used to get/set feed bundle entries, such as the resource version, or the last updated timestamp.
*
* @throws NullPointerException
* The map must not be null
* The map must not be null
*/
void setResourceMetadata(ResourceMetadataMap theMap);
/**
* Returns a String representing the name of this Resource. This return
* value is not used for anything by HAPI itself, but is provided as a
* convenience to developers using the API.
* Returns a String representing the name of this Resource. This return value is not used for anything by HAPI itself, but is provided as a convenience to developers using the API.
*
* @return the name of this resource, e.g. "Patient", or "Observation"
*/
@ -141,7 +123,6 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
/**
* Returns the FHIR version represented by this structure
*/
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
}

View File

@ -23,59 +23,126 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
*/
/**
* Represents a FHIR resource path specification, e.g.
* <code>Patient.gender.coding</code>
* Represents a FHIR resource path specification, e.g. <code>Patient:name</code>
* <p>
* Note on equality: This class uses the {@link Include#setValue(String) value}
* as the single item used to provide {@link #hashCode()} and {@link #equals(Object)}.
* Note on equality: This class uses {@link #getValue() value} and the {@link #isRecurse() recurse} properties to test
* equality. Prior to HAPI 1.2 (and FHIR DSTU2) the recurse property did not exist, so this may merit consideration when
* upgrading servers.
* </p>
*/
public class Include {
private boolean myRecurse;
private String myValue;
private boolean myImmutable;
/**
* Constructor for <b>non-recursive</b> include
*
* @param theValue
* The <code>_include</code> value, e.g. "Patient:name"
*/
public Include(String theValue) {
myValue = theValue;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if ((obj instanceof Include)==false)
return false;
Include other = (Include) obj;
if (myValue == null) {
if (other.myValue != null)
return false;
} else if (!myValue.equals(other.myValue))
return false;
return true;
/**
* Constructor for <b>non-recursive</b> include
*
* @param theValue
* The <code>_include</code> value, e.g. "Patient:name"
* @param theRecurse
* Should the include recurse
*/
public Include(String theValue, boolean theRecurse) {
myValue = theValue;
myRecurse = theRecurse;
}
/**
* Creates a copy of this include with non-recurse behaviour
*/
public Include asNonRecursive() {
return new Include(myValue, false);
}
/**
* Creates a copy of this include with recurse behaviour
*/
public Include asRecursive() {
return new Include(myValue, true);
}
public String getValue() {
return myValue;
}
/**
* See the note on equality on the {@link Include class documentation}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (myRecurse ? 1231 : 1237);
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
return result;
}
/**
* See the note on equality on the {@link Include class documentation}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Include other = (Include) obj;
if (myRecurse != other.myRecurse) {
return false;
}
if (myValue == null) {
if (other.myValue != null) {
return false;
}
} else if (!myValue.equals(other.myValue)) {
return false;
}
return true;
}
public boolean isRecurse() {
return myRecurse;
}
public void setRecurse(boolean theRecurse) {
myRecurse = theRecurse;
}
public void setValue(String theValue) {
if (myImmutable) {
throw new IllegalStateException("Can not change the value of this include");
}
myValue = theValue;
}
public Include toLocked() {
Include retVal = new Include(myValue, myRecurse);
retVal.myImmutable = true;
return retVal;
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
builder.append("myValue", myValue);
builder.append("value", myValue);
builder.append("recurse", myRecurse);
return builder.toString();
}
}

View File

@ -37,6 +37,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
*/
public class Tag extends BaseElement implements IElement {
private static final long serialVersionUID = 1L;
public static final String ATTR_LABEL = "label";
public static final String ATTR_SCHEME = "scheme";
public static final String ATTR_TERM = "term";

View File

@ -31,7 +31,8 @@ import java.util.Set;
import org.hl7.fhir.instance.model.api.IBase;
/**
* A collection of tags present on a single resource. TagList is backed by a {@link LinkedHashSet}, so the order of added tags will be consistent, but duplicates will not be preserved.
* A collection of tags present on a single resource. TagList is backed by a {@link LinkedHashSet}, so the order of
* added tags will be consistent, but duplicates will not be preserved.
*
* <p>
* <b>Thread safety:</b> This class is not thread safe
@ -54,6 +55,17 @@ public class TagList implements Set<Tag>, Serializable, IBase {
super();
}
/**
* Copy constructor
*/
public TagList(TagList theTags) {
if (theTags != null) {
for (Tag next : theTags) {
add(next);
}
}
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
@ -77,7 +89,8 @@ public class TagList implements Set<Tag>, Serializable, IBase {
}
/**
* @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addTag(String, String, String)} should be used instead
* @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addTag(String, String, String)}
* should be used instead
*/
@Deprecated
public Tag addTag() {
@ -89,10 +102,11 @@ public class TagList implements Set<Tag>, Serializable, IBase {
* Add a new tag instance
*
* @param theScheme
* The tag scheme
* The tag scheme
* @param theTerm
* The tag term
* @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you generally do not need to interact directly with the added tag.
* The tag term
* @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you
* generally do not need to interact directly with the added tag.
*/
public Tag addTag(String theScheme, String theTerm) {
Tag retVal = new Tag(theScheme, theTerm);
@ -105,12 +119,13 @@ public class TagList implements Set<Tag>, Serializable, IBase {
* Add a new tag instance
*
* @param theScheme
* The tag scheme
* The tag scheme
* @param theTerm
* The tag term
* The tag term
* @param theLabel
* The tag label
* @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you generally do not need to interact directly with the added tag.
* The tag label
* @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you
* generally do not need to interact directly with the added tag.
*/
public Tag addTag(String theScheme, String theTerm, String theLabel) {
Tag retVal = new Tag(theScheme, theTerm, theLabel);
@ -153,7 +168,8 @@ public class TagList implements Set<Tag>, Serializable, IBase {
}
/**
* Returns the tag at a given index - Note that the TagList is backed by a {@link LinkedHashSet}, so the order of added tags will be consistent, but duplicates will not be preserved.
* Returns the tag at a given index - Note that the TagList is backed by a {@link LinkedHashSet}, so the order of
* added tags will be consistent, but duplicates will not be preserved.
*/
public Tag get(int theIndex) {
if (myOrderedTags == null) {

View File

@ -24,11 +24,6 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Enumeration;
import net.sourceforge.cobertura.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseEnumFactory;
import ca.uhn.fhir.model.api.IElement;
@ -103,31 +98,13 @@ public @interface Child {
// String replaces() default "";
/**
* For children which accept an {@link Enumeration} as the type, this
* field indicates the type to use for the enum factory
* Is this element a modifier?
*/
Class<? extends IBaseEnumFactory<?>> enumFactory() default NoEnumFactory.class;
@CoverageIgnore
public static class NoEnumFactory implements IBaseEnumFactory<Enum<?>> {
@CoverageIgnore
private NoEnumFactory() {
// non instantiable
}
@CoverageIgnore
@Override
public Enum<?> fromCode(String theCodeString) throws IllegalArgumentException {
return null;
}
@CoverageIgnore
@Override
public String toCode(Enum<?> theCode) {
return null;
}
}
boolean modifier() default false;
/**
* Should this element be included in the summary view
*/
boolean summary() default false;
}

View File

@ -25,6 +25,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
@ -49,4 +51,12 @@ public @interface DatatypeDef {
*/
boolean isSpecialization() default false;
/**
* Indicates that this datatype is a profile of the given datatype, which
* implies certain parsing/encoding rules (e.g. a choice element named
* foo[x] which allows a Markdown value will still be encoded as
* fooString because Markdown is a profile of string.
*/
Class<? extends IBaseDatatype> profileOf() default IBaseDatatype.class;
}

View File

@ -46,8 +46,14 @@ public interface BaseOperationOutcome extends IResource, IBaseOperationOutcome {
public abstract BaseIssue addLocation(String theString);
/**
* @deprecated Use {@link #setDiagnostics(String)} instead - Field was renamed in DSTU2
*/
@Deprecated
public abstract BaseIssue setDetails(String theString);
public abstract BaseIssue setDiagnostics(String theString);
public abstract StringDt getLocationFirstRep();
}

View File

@ -1,140 +0,0 @@
package ca.uhn.fhir.model.dstu.valueset;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.HashMap;
import java.util.Map;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
@CoverageIgnore
public enum RestfulOperationSystemEnum {
/**
* Code Value: <b>transaction</b>
*/
TRANSACTION("transaction", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>search-system</b>
*/
SEARCH_SYSTEM("search-system", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>history-system</b>
*/
HISTORY_SYSTEM("history-system", "http://hl7.org/fhir/restful-operation"),
;
/**
* Identifier for this Value Set:
* http://hl7.org/fhir/vs/system-restful-operation
*/
public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/vs/system-restful-operation";
/**
* Name for this Value Set:
* RestfulOperationSystem
*/
public static final String VALUESET_NAME = "RestfulOperationSystem";
private static Map<String, RestfulOperationSystemEnum> CODE_TO_ENUM = new HashMap<String, RestfulOperationSystemEnum>();
private static Map<String, Map<String, RestfulOperationSystemEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, RestfulOperationSystemEnum>>();
private final String myCode;
private final String mySystem;
static {
for (RestfulOperationSystemEnum next : RestfulOperationSystemEnum.values()) {
CODE_TO_ENUM.put(next.getCode(), next);
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap<String, RestfulOperationSystemEnum>());
}
SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next);
}
}
/**
* Returns the code associated with this enumerated value
*/
public String getCode() {
return myCode;
}
/**
* Returns the code system associated with this enumerated value
*/
public String getSystem() {
return mySystem;
}
/**
* Returns the enumerated value associated with this code
*/
public RestfulOperationSystemEnum forCode(String theCode) {
RestfulOperationSystemEnum retVal = CODE_TO_ENUM.get(theCode);
return retVal;
}
/**
* Converts codes to their respective enumerated values
*/
public static final IValueSetEnumBinder<RestfulOperationSystemEnum> VALUESET_BINDER = new IValueSetEnumBinder<RestfulOperationSystemEnum>() {
@Override
public String toCodeString(RestfulOperationSystemEnum theEnum) {
return theEnum.getCode();
}
@Override
public String toSystemString(RestfulOperationSystemEnum theEnum) {
return theEnum.getSystem();
}
@Override
public RestfulOperationSystemEnum fromCodeString(String theCodeString) {
return CODE_TO_ENUM.get(theCodeString);
}
@Override
public RestfulOperationSystemEnum fromCodeString(String theCodeString, String theSystemString) {
Map<String, RestfulOperationSystemEnum> map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString);
if (map == null) {
return null;
}
return map.get(theCodeString);
}
};
/**
* Constructor
*/
RestfulOperationSystemEnum(String theCode, String theSystem) {
myCode = theCode;
mySystem = theSystem;
}
}

View File

@ -1,170 +0,0 @@
package ca.uhn.fhir.model.dstu.valueset;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.HashMap;
import java.util.Map;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
@CoverageIgnore
public enum RestfulOperationTypeEnum {
/**
* Code Value: <b>read</b>
*/
READ("read", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>vread</b>
*/
VREAD("vread", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>update</b>
*/
UPDATE("update", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>delete</b>
*/
DELETE("delete", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>history-instance</b>
*/
HISTORY_INSTANCE("history-instance", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>validate</b>
*/
VALIDATE("validate", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>history-type</b>
*/
HISTORY_TYPE("history-type", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>create</b>
*/
CREATE("create", "http://hl7.org/fhir/restful-operation"),
/**
* Code Value: <b>search-type</b>
*/
SEARCH_TYPE("search-type", "http://hl7.org/fhir/restful-operation"),
;
/**
* Identifier for this Value Set:
* http://hl7.org/fhir/vs/type-restful-operation
*/
public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/vs/type-restful-operation";
/**
* Name for this Value Set:
* RestfulOperationType
*/
public static final String VALUESET_NAME = "RestfulOperationType";
private static Map<String, RestfulOperationTypeEnum> CODE_TO_ENUM = new HashMap<String, RestfulOperationTypeEnum>();
private static Map<String, Map<String, RestfulOperationTypeEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, RestfulOperationTypeEnum>>();
private final String myCode;
private final String mySystem;
static {
for (RestfulOperationTypeEnum next : RestfulOperationTypeEnum.values()) {
CODE_TO_ENUM.put(next.getCode(), next);
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap<String, RestfulOperationTypeEnum>());
}
SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next);
}
}
/**
* Returns the code associated with this enumerated value
*/
public String getCode() {
return myCode;
}
/**
* Returns the code system associated with this enumerated value
*/
public String getSystem() {
return mySystem;
}
/**
* Returns the enumerated value associated with this code
*/
public RestfulOperationTypeEnum forCode(String theCode) {
RestfulOperationTypeEnum retVal = CODE_TO_ENUM.get(theCode);
return retVal;
}
/**
* Converts codes to their respective enumerated values
*/
public static final IValueSetEnumBinder<RestfulOperationTypeEnum> VALUESET_BINDER = new IValueSetEnumBinder<RestfulOperationTypeEnum>() {
@Override
public String toCodeString(RestfulOperationTypeEnum theEnum) {
return theEnum.getCode();
}
@Override
public String toSystemString(RestfulOperationTypeEnum theEnum) {
return theEnum.getSystem();
}
@Override
public RestfulOperationTypeEnum fromCodeString(String theCodeString) {
return CODE_TO_ENUM.get(theCodeString);
}
@Override
public RestfulOperationTypeEnum fromCodeString(String theCodeString, String theSystemString) {
Map<String, RestfulOperationTypeEnum> map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString);
if (map == null) {
return null;
}
return map.get(theCodeString);
}
};
/**
* Constructor
*/
RestfulOperationTypeEnum(String theCode, String theSystem) {
myCode = theCode;
mySystem = theSystem;
}
}

View File

@ -386,7 +386,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
updateStringValue();
}
private void setTimeZone(String theValueString, boolean hasMillis) {
private BaseDateTimeDt setTimeZone(String theValueString, boolean hasMillis) {
clearTimeZone();
int timeZoneStart = 19;
if (hasMillis)
@ -398,16 +398,19 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
} else if (theValueString.indexOf('+', timeZoneStart) != -1 || theValueString.indexOf('-', timeZoneStart) != -1) {
setTimeZone(TimeZone.getTimeZone("GMT" + theValueString.substring(timeZoneStart)));
}
return this;
}
public void setTimeZone(TimeZone theTimeZone) {
public BaseDateTimeDt setTimeZone(TimeZone theTimeZone) {
myTimeZone = theTimeZone;
updateStringValue();
return this;
}
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
public BaseDateTimeDt setTimeZoneZulu(boolean theTimeZoneZulu) {
myTimeZoneZulu = theTimeZoneZulu;
updateStringValue();
return this;
}
/**

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
@DatatypeDef(name = "code")
@DatatypeDef(name = "code", profileOf=StringDt.class)
public class CodeDt extends BasePrimitive<String> implements ICodedDatatype, Comparable<CodeDt> {
/**

View File

@ -52,7 +52,7 @@ import ca.uhn.fhir.util.UrlUtil;
* regex: [a-z0-9\-\.]{1,36}
* </p>
*/
@DatatypeDef(name = "id")
@DatatypeDef(name = "id", profileOf=StringDt.class)
public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
private String myBaseUrl;
@ -199,6 +199,34 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return getIdPartAsBigDecimal();
}
private String determineLocalPrefix(String theValue) {
if (theValue == null || theValue.isEmpty()) {
return null;
}
if (theValue.startsWith("#")) {
return "#";
}
int lastPrefix = -1;
for (int i = 0; i < theValue.length(); i++) {
char nextChar = theValue.charAt(i);
if (nextChar == ':') {
lastPrefix = i;
} else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) {
break;
}
}
if (lastPrefix != -1) {
String candidate = theValue.substring(0, lastPrefix + 1);
if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) {
return candidate;
} else {
return null;
}
} else {
return null;
}
}
@Override
public boolean equals(Object theArg0) {
if (!(theArg0 instanceof IdDt)) {
@ -383,6 +411,34 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return isBlank(getValue());
}
@Override
public boolean isIdPartValid() {
String id = getIdPart();
if (StringUtils.isBlank(id)) {
return false;
}
if (id.length() > 64) {
return false;
}
for (int i = 0; i < id.length(); i++) {
char nextChar = id.charAt(i);
if (nextChar >= 'a' && nextChar <= 'z') {
continue;
}
if (nextChar >= 'A' && nextChar <= 'Z') {
continue;
}
if (nextChar >= '0' && nextChar <= '9') {
continue;
}
if (nextChar == '-' || nextChar == '.') {
continue;
}
return false;
}
return true;
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
*/
@ -407,7 +463,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
public boolean isLocal() {
return "#".equals(myBaseUrl);
}
/**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
*/
@ -416,34 +472,6 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
setValue(theId.getValue());
}
private String determineLocalPrefix(String theValue) {
if (theValue == null || theValue.isEmpty()) {
return null;
}
if (theValue.startsWith("#")) {
return "#";
}
int lastPrefix = -1;
for (int i = 0; i < theValue.length(); i++) {
char nextChar = theValue.charAt(i);
if (nextChar == ':') {
lastPrefix = i;
} else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) {
break;
}
}
if (lastPrefix != -1) {
String candidate = theValue.substring(0, lastPrefix + 1);
if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) {
return candidate;
} else {
return null;
}
} else {
return null;
}
}
/**
* Set the value
*
@ -602,6 +630,14 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
}
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
/**
* Retrieves the ID from the given resource instance
*/
@ -634,12 +670,4 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return theIdPart.toString();
}
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
}

View File

@ -34,7 +34,7 @@ public class IntegerDt extends BasePrimitive<Integer> implements IBaseIntegerDat
* Constructor
*/
public IntegerDt() {
// nothing
super();
}
/**

View File

@ -1,6 +1,4 @@
package org.hl7.fhir.instance.model;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
package ca.uhn.fhir.model.primitive;
/*
* #%L
@ -22,15 +20,11 @@ import org.hl7.fhir.instance.model.api.IBaseDatatype;
* #L%
*/
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import net.sourceforge.cobertura.CoverageIgnore;
public interface IPrimitiveType<T> extends IBaseDatatype {
@DatatypeDef(name = "markdown", profileOf=StringDt.class)
@CoverageIgnore
public class MarkdownDt extends StringDt {
void setValueAsString(String theValue) throws IllegalArgumentException;
String getValueAsString();
T getValue();
IPrimitiveType<T> setValue(T theValue) throws IllegalArgumentException;
}

View File

@ -22,7 +22,7 @@ package ca.uhn.fhir.model.primitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@DatatypeDef(name = "oid")
@DatatypeDef(name = "oid", profileOf=UriDt.class)
public class OidDt extends UriDt {
// TODO: implement restrictions

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "positiveInt")
@DatatypeDef(name = "positiveInt", profileOf=IntegerDt.class)
@CoverageIgnore
public class PositiveIntDt extends IntegerDt {

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "unsignedInt")
@DatatypeDef(name = "unsignedInt", profileOf=IntegerDt.class)
@CoverageIgnore
public class UnsignedIntDt extends IntegerDt {

View File

@ -205,6 +205,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
try {
Context context = new Context();
context.setVariable("resource", theResource);
context.setVariable("fhirVersion", myFhirContext.getVersion().getVersion().name());
String result = myTitleTemplateEngine.process(name, context);

View File

@ -19,8 +19,8 @@ package ca.uhn.fhir.parser;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.Reader;
@ -32,6 +32,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -40,7 +41,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
@ -56,24 +57,32 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildContainedResources;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public abstract class BaseParser implements IParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
private ContainedResources myContainedResources;
private FhirContext myContext;
private Set<String> myEncodeElements;
private Set<String> myEncodeElementsAppliesToResourceTypes;
private boolean myEncodeElementsIncludesStars;
private IParserErrorHandler myErrorHandler;
private boolean myOmitResourceId;
private String myServerBaseUrl;
private boolean myStripVersionsFromReferences = true;
private boolean mySummaryMode;
private boolean mySuppressNarratives;
/**
@ -86,6 +95,89 @@ public abstract class BaseParser implements IParser {
myErrorHandler = theParserErrorHandler;
}
protected Iterable<CompositeChildElement> compositeChildIterator(final List<? extends BaseRuntimeChildDefinition> theChildren, final boolean theContainedResource, final CompositeChildElement theParent) {
return new Iterable<BaseParser.CompositeChildElement>() {
@Override
public Iterator<CompositeChildElement> iterator() {
return new Iterator<CompositeChildElement>() {
private Iterator<? extends BaseRuntimeChildDefinition> myChildrenIter;
private Boolean myHasNext = null;
private CompositeChildElement myNext;
/**
* Constructor
*/
{
myChildrenIter = theChildren.iterator();
}
@Override
public boolean hasNext() {
if (myHasNext != null) {
return myHasNext;
}
myNext = null;
do {
if (myChildrenIter.hasNext() == false) {
myHasNext = Boolean.FALSE;
return false;
}
myNext = new CompositeChildElement(theParent, myChildrenIter.next());
/*
* There are lots of reasons we might skip encoding a particular child
*/
if (myNext.getDef().getElementName().equals("extension") || myNext.getDef().getElementName().equals("modifierExtension")) {
myNext = null;
} else if (myNext.getDef().getElementName().equals("id")) {
myNext = null;
} else if (!myNext.shouldBeEncoded()) {
myNext = null;
} else if (isSummaryMode() && !myNext.getDef().isSummary()) {
myNext = null;
} else if (myNext.getDef() instanceof RuntimeChildNarrativeDefinition) {
if (isSuppressNarratives() || isSummaryMode()) {
myNext = null;
} else if (theContainedResource) {
myNext = null;
}
} else if (myNext.getDef() instanceof RuntimeChildContainedResources) {
if (theContainedResource) {
myNext = null;
}
}
} while (myNext == null);
myHasNext = true;
return true;
}
@Override
public CompositeChildElement next() {
if (myHasNext == null) {
if (!hasNext()) {
throw new IllegalStateException();
}
}
CompositeChildElement retVal = myNext;
myNext = null;
myHasNext = null;
return retVal;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
Set<String> allIds = new HashSet<String>();
Map<String, IBaseResource> existingIdToContainedResource = null;
@ -172,16 +264,27 @@ public abstract class BaseParser implements IParser {
} else {
reference = "#" + containedId.getValue();
}
} else if (theRef.getResource().getIdElement() != null && theRef.getResource().getIdElement().hasIdPart()) {
if (isStripVersionsFromReferences()) {
reference = theRef.getResource().getIdElement().toVersionless().getValue();
} else {
reference = theRef.getResource().getIdElement().getValue();
} else {
IIdType refId = theRef.getResource().getIdElement();
if (refId != null) {
if (refId.hasIdPart()) {
if (!refId.hasResourceType()) {
refId = refId.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName());
}
if (isStripVersionsFromReferences()) {
reference = refId.toVersionless().getValue();
} else {
reference = refId.getValue();
}
}
}
}
}
return reference;
} else {
if (!ref.hasResourceType() && !ref.isLocal() && theRef.getResource() != null) {
ref = ref.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName());
}
if (isNotBlank(myServerBaseUrl) && StringUtils.equals(myServerBaseUrl, ref.getBaseUrl())) {
if (isStripVersionsFromReferences()) {
return ref.toUnqualifiedVersionless().getValue();
@ -257,10 +360,6 @@ public abstract class BaseParser implements IParser {
Validate.notNull(theResource, "theResource can not be null");
Validate.notNull(theWriter, "theWriter can not be null");
if (theResource instanceof IBaseBundle) {
fixBaseLinksForBundle((IBaseBundle) theResource);
}
doEncodeResourceToWriter(theResource, theWriter);
}
@ -275,50 +374,6 @@ public abstract class BaseParser implements IParser {
return stringWriter.toString();
}
/**
* If individual resources in the bundle have an ID that has the base set, we make sure that Bundle.entry.base gets set as needed.
*/
private void fixBaseLinksForBundle(IBaseBundle theBundle) {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
return;
}
/*
* ATTENTION IF YOU ARE EDITING THIS: There are two versions of this method, one for DSTU1/atom bundle and one for DSTU2/resource bundle. If you edit one, edit both and also update unit tests
* for both.
*/
FhirTerser t = myContext.newTerser();
IPrimitiveType<?> element = t.getSingleValueOrNull(theBundle, "base", IPrimitiveType.class);
String bundleBase = element != null ? element.getValueAsString() : null;
for (IBase nextEntry : t.getValues(theBundle, "Bundle.entry", IBase.class)) {
IBaseResource resource = t.getSingleValueOrNull(nextEntry, "resource", IBaseResource.class);
if (resource == null) {
continue;
}
IPrimitiveType<?> baseElement = t.getSingleValueOrNull(nextEntry, "base", IPrimitiveType.class);
String entryBase = baseElement != null ? baseElement.getValueAsString() : null;
if (isNotBlank(entryBase)) {
continue;
}
IIdType resourceId = resource.getIdElement();
String resourceIdBase = resourceId.getBaseUrl();
if (isNotBlank(resourceIdBase)) {
if (!ObjectUtil.equals(bundleBase, resourceIdBase)) {
if (baseElement == null) {
baseElement = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextEntry.getClass());
entryDef.getChildByNameOrThrowDataFormatException("base").getMutator().setValue(nextEntry, baseElement);
}
baseElement.setValueAsString(resourceIdBase);
}
}
}
}
protected String fixContainedResourceId(String theValue) {
if (StringUtils.isNotBlank(theValue) && theValue.charAt(0) == '#') {
return theValue.substring(1);
@ -330,20 +385,49 @@ public abstract class BaseParser implements IParser {
return myContainedResources;
}
/**
* See {@link #setEncodeElements(Set)}
*/
@Override
public Set<String> getEncodeElements() {
return myEncodeElements;
}
/**
* See {@link #setEncodeElementsAppliesToResourceTypes(Set)}
*/
@Override
public Set<String> getEncodeElementsAppliesToResourceTypes() {
return myEncodeElementsAppliesToResourceTypes;
}
protected IParserErrorHandler getErrorHandler() {
return myErrorHandler;
}
protected TagList getMetaTagsForEncoding(IResource theIResource) {
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(theIResource);
if (shouldAddSubsettedTag()) {
tags = new TagList(tags);
tags.add(new Tag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE, "Resource encoded in summary mode"));
}
return tags;
}
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded values.
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded
* values.
*
* @deprecated Use {@link #isSuppressNarratives()}
*/
@Deprecated
public boolean getSuppressNarratives() {
return mySuppressNarratives;
}
protected boolean isChildContained(BaseRuntimeElementDefinition<?> childDef, boolean theIncludedResource) {
return (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES || childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) && getContainedResources().isEmpty() == false
&& theIncludedResource == false;
return (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES || childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) && getContainedResources().isEmpty() == false && theIncludedResource == false;
}
@Override
@ -356,6 +440,21 @@ public abstract class BaseParser implements IParser {
return myStripVersionsFromReferences;
}
@Override
public boolean isSummaryMode() {
return mySummaryMode;
}
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded
* values.
*
* @since 1.2
*/
public boolean isSuppressNarratives() {
return mySuppressNarratives;
}
@Override
public Bundle parseBundle(Reader theReader) {
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU2_HL7ORG) {
@ -377,75 +476,31 @@ public abstract class BaseParser implements IParser {
RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal);
if ("Bundle".equals(def.getName())) {
List<IBase> base = null;
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
base = def.getChildByName("base").getAccessor().getValues(retVal);
if (base != null && base.size() > 0) {
IPrimitiveType<?> baseType = (IPrimitiveType<?>) base.get(0);
IBaseResource res = (retVal);
res.setId(new IdDt(baseType.getValueAsString(), def.getName(), res.getIdElement().getIdPart(), res.getIdElement().getVersionIdPart()));
}
}
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
List<IBase> entries = entryChild.getAccessor().getValues(retVal);
if (entries != null) {
for (IBase nextEntry : entries) {
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
List<IBase> entryBase = entryDef.getChildByName("base").getAccessor().getValues(nextEntry);
if (entryBase == null || entryBase.isEmpty()) {
entryBase = base;
}
if (entryBase != null && entryBase.size() > 0) {
IPrimitiveType<?> baseType = (IPrimitiveType<?>) entryBase.get(0);
/**
* If Bundle.entry.fullUrl is populated, set the resource ID to that
*/
// TODO: should emit a warning and maybe notify the error handler if the resource ID doesn't match the
// fullUrl idPart
BaseRuntimeChildDefinition fullUrlChild = entryDef.getChildByName("fullUrl");
if (fullUrlChild == null) {
continue; // TODO: remove this once the data model in tinder plugin catches up to 1.2
}
List<IBase> fullUrl = fullUrlChild.getAccessor().getValues(nextEntry);
if (fullUrl != null && !fullUrl.isEmpty()) {
IPrimitiveType<?> value = (IPrimitiveType<?>) fullUrl.get(0);
if (value.isEmpty() == false) {
List<IBase> entryResources = entryDef.getChildByName("resource").getAccessor().getValues(nextEntry);
if (entryResources != null && entryResources.size() > 0) {
IBaseResource res = (IBaseResource) entryResources.get(0);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(res);
String versionIdPart = res.getIdElement().getVersionIdPart();
if (isBlank(versionIdPart) && res instanceof IResource) {
versionIdPart = ResourceMetadataKeyEnum.VERSION.get((IResource) res);
}
String baseUrl = baseType.getValueAsString();
String idPart = res.getIdElement().getIdPart();
String resourceName = resDef.getName();
if (!baseUrl.startsWith("cid:") && !baseUrl.startsWith("urn:")) {
res.setId(new IdDt(baseUrl, resourceName, idPart, versionIdPart));
} else {
if (baseUrl.endsWith(":")) {
res.setId(new IdDt(baseUrl + idPart));
} else {
res.setId(new IdDt(baseUrl + ':' + idPart));
}
}
}
}
} else {
// DSTU2 after 0.5.0
/**
* If Bundle.entry.fullUrl is populated, set the resource ID to that
*/
List<IBase> fullUrl = entryDef.getChildByName("fullUrl").getAccessor().getValues(nextEntry);
if (fullUrl != null && !fullUrl.isEmpty()) {
IPrimitiveType<?> value = (IPrimitiveType<?>) fullUrl.get(0);
if (value.isEmpty() == false) {
List<IBase> entryResources = entryDef.getChildByName("resource").getAccessor().getValues(nextEntry);
if (entryResources != null && entryResources.size() > 0) {
IBaseResource res = (IBaseResource) entryResources.get(0);
res.setId(value.getValueAsString());
}
res.setId(value.getValueAsString());
}
}
}
}
@ -478,14 +533,92 @@ public abstract class BaseParser implements IParser {
return parseTagList(new StringReader(theString));
}
@SuppressWarnings("cast")
protected List<? extends IBase> preProcessValues(BaseRuntimeChildDefinition metaChildUncast, List<? extends IBase> theValues) {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
if (shouldAddSubsettedTag() && metaChildUncast.getValidChildNames().contains("meta")) {
BaseRuntimeElementDefinition<?> childByName = metaChildUncast.getChildByName("meta");
if (childByName instanceof BaseRuntimeElementCompositeDefinition<?>) {
BaseRuntimeElementCompositeDefinition<?> metaChildUncast1 = (BaseRuntimeElementCompositeDefinition<?>) childByName;
if (metaChildUncast1 != null) {
if (IBaseMetaType.class.isAssignableFrom(metaChildUncast1.getImplementingClass())) {
IBaseMetaType metaValue;
if (theValues != null && theValues.size() >= 1) {
metaValue = (IBaseMetaType) theValues.iterator().next();
try {
metaValue = (IBaseMetaType) metaValue.getClass().getMethod("copy").invoke(metaValue);
} catch (Exception e) {
throw new InternalErrorException("Failed to duplicate meta", e);
}
} else {
metaValue = (IBaseMetaType) metaChildUncast1.newInstance();
}
ArrayList<IBase> retVal = new ArrayList<IBase>();
retVal.add(metaValue);
BaseRuntimeChildDefinition tagChild = metaChildUncast1.getChildByName("tag");
BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) ((BaseRuntimeElementCompositeDefinition<?>) tagChild.getChildByName("tag"));
IBase coding = codingDef.newInstance();
tagChild.getMutator().addValue(metaValue, coding);
BaseRuntimeChildDefinition systemChild = codingDef.getChildByName("system");
IPrimitiveType<?> system = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
system.setValueAsString(Constants.TAG_SUBSETTED_SYSTEM);
systemChild.getMutator().addValue(coding, system);
BaseRuntimeChildDefinition codeChild = codingDef.getChildByName("code");
IPrimitiveType<?> code = (IPrimitiveType<?>) myContext.getElementDefinition("code").newInstance();
code.setValueAsString(Constants.TAG_SUBSETTED_CODE);
codeChild.getMutator().addValue(coding, code);
BaseRuntimeChildDefinition displayChild = codingDef.getChildByName("display");
IPrimitiveType<?> display = (IPrimitiveType<?>) myContext.getElementDefinition("string").newInstance();
display.setValueAsString("Resource encoded in summary mode");
displayChild.getMutator().addValue(coding, display);
return retVal;
}
}
}
}
}
return theValues;
}
@Override
public BaseParser setOmitResourceId(boolean theOmitResourceId) {
public void setEncodeElements(Set<String> theEncodeElements) {
myEncodeElementsIncludesStars = false;
if (theEncodeElements == null || theEncodeElements.isEmpty()) {
myEncodeElements = null;
} else {
myEncodeElements = theEncodeElements;
for (String next : theEncodeElements) {
if (next.startsWith("*.")) {
myEncodeElementsIncludesStars = true;
}
}
}
}
@Override
public void setEncodeElementsAppliesToResourceTypes(Set<String> theEncodeElementsAppliesToResourceTypes) {
if (theEncodeElementsAppliesToResourceTypes == null || theEncodeElementsAppliesToResourceTypes.isEmpty()) {
myEncodeElementsAppliesToResourceTypes = null;
} else {
myEncodeElementsAppliesToResourceTypes = theEncodeElementsAppliesToResourceTypes;
}
}
@Override
public IParser setOmitResourceId(boolean theOmitResourceId) {
myOmitResourceId = theOmitResourceId;
return this;
}
@Override
public BaseParser setParserErrorHandler(IParserErrorHandler theErrorHandler) {
public IParser setParserErrorHandler(IParserErrorHandler theErrorHandler) {
Validate.notNull(theErrorHandler, "theErrorHandler must not be null");
myErrorHandler = theErrorHandler;
return this;
@ -503,12 +636,22 @@ public abstract class BaseParser implements IParser {
return this;
}
@Override
public IParser setSummaryMode(boolean theSummaryMode) {
mySummaryMode = theSummaryMode;
return this;
}
@Override
public IParser setSuppressNarratives(boolean theSuppressNarratives) {
mySuppressNarratives = theSuppressNarratives;
return this;
}
protected boolean shouldAddSubsettedTag() {
return isSummaryMode() || isSuppressNarratives();
}
protected void throwExceptionForUnknownChildType(BaseRuntimeChildDefinition nextChild, Class<? extends IBase> theType) {
if (nextChild instanceof BaseRuntimeDeclaredChildDefinition) {
StringBuilder b = new StringBuilder();
@ -533,6 +676,107 @@ public abstract class BaseParser implements IParser {
return securityLabels;
}
protected class CompositeChildElement {
private final BaseRuntimeChildDefinition myDef;
private final CompositeChildElement myParent;
private final RuntimeResourceDefinition myResDef;
public CompositeChildElement(CompositeChildElement theParent, BaseRuntimeChildDefinition theDef) {
myDef = theDef;
myParent = theParent;
myResDef = null;
if (ourLog.isTraceEnabled()) {
if (theParent != null) {
StringBuilder path = theParent.buildPath();
if (path != null) {
path.append('.');
path.append(myDef.getElementName());
ourLog.trace(" * Next path: {}", path.toString());
}
}
}
}
public CompositeChildElement(RuntimeResourceDefinition theResDef) {
myResDef = theResDef;
myDef = null;
myParent = null;
}
private StringBuilder buildPath() {
if (myResDef != null) {
StringBuilder b = new StringBuilder();
b.append(myResDef.getName());
return b;
} else {
StringBuilder b = myParent.buildPath();
if (b != null && myDef != null) {
b.append('.');
b.append(myDef.getElementName());
}
return b;
}
}
private boolean checkIfParentShouldBeEncodedAndBuildPath(StringBuilder theB, boolean theStarPass) {
if (myResDef != null) {
if (myEncodeElementsAppliesToResourceTypes != null) {
if (!myEncodeElementsAppliesToResourceTypes.contains(myResDef.getName())) {
return true;
}
}
if (theStarPass) {
theB.append('*');
} else {
theB.append(myResDef.getName());
}
if (myEncodeElements.contains(theB.toString())) {
return true;
} else {
return false;
}
} else if (myParent != null) {
if (myParent.checkIfParentShouldBeEncodedAndBuildPath(theB, theStarPass)) {
return true;
}
if (myDef != null) {
theB.append('.');
theB.append(myDef.getElementName());
return myEncodeElements.contains(theB.toString());
}
}
return true;
}
public BaseRuntimeChildDefinition getDef() {
return myDef;
}
public CompositeChildElement getParent() {
return myParent;
}
public RuntimeResourceDefinition getResDef() {
return myResDef;
}
public boolean shouldBeEncoded() {
if (myEncodeElements == null) {
return true;
}
boolean retVal = checkIfParentShouldBeEncodedAndBuildPath(new StringBuilder(), false);
if (retVal == false && myEncodeElementsIncludesStars) {
retVal = checkIfParentShouldBeEncodedAndBuildPath(new StringBuilder(), true);
}
return retVal;
}
}
static class ContainedResources {
private long myNextContainedId = 1;

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.parser;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Set;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -31,22 +32,18 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* A parser, which can be used to convert between HAPI FHIR model/structure objects, and
* their respective String wire formats, in either XML or JSON.
* A parser, which can be used to convert between HAPI FHIR model/structure objects, and their respective String wire
* formats, in either XML or JSON.
* <p>
* Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread or every message being parsed/encoded.
* Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread or
* every message being parsed/encoded.
* </p>
*/
public interface IParser {
/**
* Registers an error handler which will be invoked when any parse errors are found
* @param theErrorHandler The error handler to set. Must not be null.
*/
IParser setParserErrorHandler(IParserErrorHandler theErrorHandler);
String encodeBundleToString(Bundle theBundle) throws DataFormatException;
void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException;
@ -56,41 +53,80 @@ public interface IParser {
void encodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException;
/**
* Encodes a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR Specification</a>.
* Encodes a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
* Specification</a>.
*
* @param theTagList
* The tag list to encode. Must not be null.
* The tag list to encode. Must not be null.
* @return An encoded tag list
*/
String encodeTagListToString(TagList theTagList);
/**
* Encodes a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR Specification</a>.
* Encodes a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
* Specification</a>.
*
* @param theTagList
* The tag list to encode. Must not be null.
* The tag list to encode. Must not be null.
* @param theWriter
* The writer to encode to
* The writer to encode to
*/
void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException;
/**
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you
* should use {@link #parseResource(Class, Reader)} with the Bundle class found in the
* See {@link #setEncodeElements(Set)}
*/
Set<String> getEncodeElements();
/**
* See {@link #setEncodeElementsAppliesToResourceTypes(Set)}
*/
Set<String> getEncodeElementsAppliesToResourceTypes();
/**
* Returns true if resource IDs should be omitted
*
* @see #setOmitResourceId(boolean)
* @since 1.1
*/
boolean isOmitResourceId();
/**
* If set to <code>true<code> (which is the default), resource references containing a version
* will have the version removed when the resource is encoded. This is generally good behaviour because
* in most situations, references from one resource to another should be to the resource by ID, not
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
*
* @return Returns the parser instance's configuration setting for stripping versions from resource references when
* encoding. Default is <code>true</code>.
*/
boolean isStripVersionsFromReferences();
/**
* Is the parser in "summary mode"? See {@link #setSummaryMode(boolean)} for information
*
* @see {@link #setSummaryMode(boolean)} for information
*/
boolean isSummaryMode();
/**
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you should use
* {@link #parseResource(Class, Reader)} with the Bundle class found in the
* <code>ca.uhn.hapi.fhir.model.[version].resource</code> package instead.
*/
<T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader);
/**
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you
* should use {@link #parseResource(Class, Reader)} with the Bundle class found in the
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you should use
* {@link #parseResource(Class, Reader)} with the Bundle class found in the
* <code>ca.uhn.hapi.fhir.model.[version].resource</code> package instead.
*/
Bundle parseBundle(Reader theReader);
/**
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you
* should use {@link #parseResource(Class, String)} with the Bundle class found in the
* Parse a DSTU1 style Atom Bundle. Note that as of DSTU2, Bundle is a resource so you should use
* {@link #parseResource(Class, String)} with the Bundle class found in the
* <code>ca.uhn.hapi.fhir.model.[version].resource</code> package instead.
*/
Bundle parseBundle(String theMessageString) throws ConfigurationException, DataFormatException;
@ -99,12 +135,13 @@ public interface IParser {
* Parses a resource
*
* @param theResourceType
* The resource type to use. This can be used to explicitly specify a class which extends a built-in type (e.g. a custom type extending the default Patient class)
* The resource type to use. This can be used to explicitly specify a class which extends a built-in type
* (e.g. a custom type extending the default Patient class)
* @param theReader
* The reader to parse input from. Note that the Reader will not be closed by the parser upon completion.
* The reader to parse input from. Note that the Reader will not be closed by the parser upon completion.
* @return A parsed resource
* @throws DataFormatException
* If the resource can not be parsed because the data is not recognized or invalid for any reason
* If the resource can not be parsed because the data is not recognized or invalid for any reason
*/
<T extends IBaseResource> T parseResource(Class<T> theResourceType, Reader theReader) throws DataFormatException;
@ -112,24 +149,25 @@ public interface IParser {
* Parses a resource
*
* @param theResourceType
* The resource type to use. This can be used to explicitly specify a class which extends a built-in type (e.g. a custom type extending the default Patient class)
* The resource type to use. This can be used to explicitly specify a class which extends a built-in type
* (e.g. a custom type extending the default Patient class)
* @param theString
* The string to parse
* The string to parse
* @return A parsed resource
* @throws DataFormatException
* If the resource can not be parsed because the data is not recognized or invalid for any reason
* If the resource can not be parsed because the data is not recognized or invalid for any reason
*/
<T extends IBaseResource> T parseResource(Class<T> theResourceType, String theString) throws DataFormatException;
/**
* Parses a resource
* Parses a resource
*
* @param theReader
* The reader to parse input from. Note that the Reader will not be closed by the parser upon completion.
* @return A parsed resource. Note that the returned object will be an instance of {@link IResource} or {@link IAnyResource}
* depending on the specific FhirContext which created this parser.
* The reader to parse input from. Note that the Reader will not be closed by the parser upon completion.
* @return A parsed resource. Note that the returned object will be an instance of {@link IResource} or
* {@link IAnyResource} depending on the specific FhirContext which created this parser.
* @throws DataFormatException
* If the resource can not be parsed because the data is not recognized or invalid for any reason
* If the resource can not be parsed because the data is not recognized or invalid for any reason
*/
IBaseResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException;
@ -137,52 +175,94 @@ public interface IParser {
* Parses a resource
*
* @param theMessageString
* The string to parse
* @return A parsed resource. Note that the returned object will be an instance of {@link IResource} or {@link IAnyResource}
* depending on the specific FhirContext which created this parser.
* The string to parse
* @return A parsed resource. Note that the returned object will be an instance of {@link IResource} or
* {@link IAnyResource} depending on the specific FhirContext which created this parser.
* @throws DataFormatException
* If the resource can not be parsed because the data is not recognized or invalid for any reason
* If the resource can not be parsed because the data is not recognized or invalid for any reason
*/
IBaseResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
/**
* Parses a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR Specification</a>.
* Parses a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
* Specification</a>.
*
* @param theReader
* A reader which will supply a tag list
* A reader which will supply a tag list
* @return A parsed tag list
*/
TagList parseTagList(Reader theReader);
/**
* Parses a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR Specification</a>.
* Parses a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
* Specification</a>.
*
* @param theString
* A string containing a tag list
* A string containing a tag list
* @return A parsed tag list
*/
TagList parseTagList(String theString);
/**
* Sets the "pretty print" flag, meaning that the parser will encode resources with human-readable spacing and newlines between elements instead of condensing output as much as possible.
* If provided, specifies the elements which should be encoded, to the exclusion of all others. Valid values for this
* field would include:
* <ul>
* <li><b>Patient</b> - Encode patient and all its children</li>
* <li><b>Patient.name</b> - Encoding only the patient's name</li>
* <li><b>Patient.name.family</b> - Encode only the patient's family name</li>
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a wildcard)</li>
* </ul>
*
* @param theEncodeElements
* The elements to encode
*/
void setEncodeElements(Set<String> theEncodeElements);
/**
* If provided, tells the parse which resource types to apply {@link #setEncodeElements(Set) encode elements} to. Any
* resource types not specified here will be encoded completely, with no elements excluded.
*
* @param theEncodeElementsAppliesToResourceTypes
*/
void setEncodeElementsAppliesToResourceTypes(Set<String> theEncodeElementsAppliesToResourceTypes);
/**
* If set to <code>true</code> (default is <code>false</code>) the ID of any resources being encoded will not be
* included in the output. Note that this does not apply to contained resources, only to root resources. In other
* words, if this is set to <code>true</code>, contained resources will still have local IDs but the outer/containing
* ID will not have an ID.
*
* @param theOmitResourceId
* Should resource IDs be omitted
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
* @since 1.1
*/
IParser setOmitResourceId(boolean theOmitResourceId);
/**
* Registers an error handler which will be invoked when any parse errors are found
*
* @param theErrorHandler
* The error handler to set. Must not be null.
*/
IParser setParserErrorHandler(IParserErrorHandler theErrorHandler);
/**
* Sets the "pretty print" flag, meaning that the parser will encode resources with human-readable spacing and
* newlines between elements instead of condensing output as much as possible.
*
* @param thePrettyPrint
* The flag
* The flag
* @return Returns an instance of <code>this</code> parser so that method calls can be chained together
*/
IParser setPrettyPrint(boolean thePrettyPrint);
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded values.
*/
IParser setSuppressNarratives(boolean theSuppressNarratives);
/**
* Sets the server's base URL used by this parser. If a value is set, resource references
* will be turned into relative references if they are provided as absolute URLs but
* have a base matching the given base.
* Sets the server's base URL used by this parser. If a value is set, resource references will be turned into
* relative references if they are provided as absolute URLs but have a base matching the given base.
*
* @param theUrl The base URL, e.g. "http://example.com/base"
* @param theUrl
* The base URL, e.g. "http://example.com/base"
* @return Returns an instance of <code>this</code> parser so that method calls can be chained together
*/
IParser setServerBaseUrl(String theUrl);
@ -194,40 +274,30 @@ public interface IParser {
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
*
* @param theStripVersionsFromReferences Set this to <code>false<code> to prevent the parser from removing
* @param theStripVersionsFromReferences
* Set this to <code>false<code> to prevent the parser from removing
* resource versions from references.
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
IParser setStripVersionsFromReferences(boolean theStripVersionsFromReferences);
/**
* If set to <code>true<code> (which is the default), resource references containing a version
* will have the version removed when the resource is encoded. This is generally good behaviour because
* in most situations, references from one resource to another should be to the resource by ID, not
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
*
* @return Returns the parser instance's configuration setting for stripping versions from resource references when encoding. Default is <code>true</code>.
*/
boolean isStripVersionsFromReferences();
/**
* If set to <code>true</code> (default is <code>false</code>) the ID of any resources being encoded
* will not be included in the output. Note that this does not apply to contained resources, only to root resources.
* In other words, if this is set to <code>true</code>, contained resources will still have local IDs but the
* outer/containing ID will not have an ID.
* If set to <code>true</code> (default is <code>false</code>) only elements marked by the FHIR specification as
* being "summary elements" will be included.
*
* @param theOmitResourceId Should resource IDs be omitted
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
* @since 1.1
*/
IParser setOmitResourceId(boolean theOmitResourceId);
IParser setSummaryMode(boolean theSummaryMode);
/**
* Returns true if resource IDs should be omitted
* @see #setOmitResourceId(boolean)
* @since 1.1
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded
* values.
*/
boolean isOmitResourceId();
IParser setSuppressNarratives(boolean theSuppressNarratives);
/**
* Which encoding does this parser instance produce?
*/
EncodingEnum getEncoding();
}

View File

@ -98,10 +98,12 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.ElementUtil;
/**
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use {@link FhirContext#newJsonParser()} to get an instance.
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
* {@link FhirContext#newJsonParser()} to get an instance.
*/
public class JsonParser extends BaseParser implements IParser {
@ -115,6 +117,7 @@ public class JsonParser extends BaseParser implements IParser {
hashSetDstu1.add("id");
hashSetDstu1.add("updated");
hashSetDstu1.add("published");
hashSetDstu1.add("totalResults");
BUNDLE_TEXTNODE_CHILDREN_DSTU1 = Collections.unmodifiableSet(hashSetDstu1);
HashSet<String> hashSetDstu2 = new HashSet<String>();
@ -128,7 +131,8 @@ public class JsonParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke {@link FhirContext#newJsonParser()}.
* Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke
* {@link FhirContext#newJsonParser()}.
*
* @param theParserErrorHandler
*/
@ -183,6 +187,41 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.flush();
}
@Override
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false);
eventWriter.flush();
}
@Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
try {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(theResourceType, myContext, true, getErrorHandler());
state.enteringNewElement(null, resourceType);
parseChildren(object, state);
state.endingElement();
@SuppressWarnings("unchecked")
T retVal = (T) state.getObject();
return retVal;
} catch (JsonParsingException e) {
throw new DataFormatException("Failed to parse JSON: " + e.getMessage(), e);
}
}
private void encodeBundleToWriterInDstu1Format(Bundle theBundle, JsonGenerator eventWriter) throws IOException {
eventWriter.writeStartObject();
@ -268,10 +307,6 @@ public class JsonParser extends BaseParser implements IParser {
writeOptionalTagWithTextNode(theEventWriter, "type", theBundle.getType());
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
writeOptionalTagWithTextNode(theEventWriter, "base", theBundle.getLinkBase());
}
writeOptionalTagWithNumberNode(theEventWriter, "total", theBundle.getTotalResults());
boolean linkStarted = false;
@ -288,14 +323,10 @@ public class JsonParser extends BaseParser implements IParser {
for (BundleEntry nextEntry : theBundle.getEntries()) {
theEventWriter.writeStartObject();
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
writeOptionalTagWithTextNode(theEventWriter, "base", determineResourceBaseUrl(theBundle.getLinkBase().getValue(), nextEntry));
} else {
if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) {
writeOptionalTagWithTextNode(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue());
}
if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) {
writeOptionalTagWithTextNode(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue());
}
boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false;
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
@ -312,7 +343,7 @@ public class JsonParser extends BaseParser implements IParser {
}
if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) {
theEventWriter.writeStartObject("transaction");
theEventWriter.writeStartObject("request");
writeOptionalTagWithTextNode(theEventWriter, "method", nextEntry.getTransactionMethod().getValue());
writeOptionalTagWithTextNode(theEventWriter, "url", nextEntry.getLinkSearch().getValue());
theEventWriter.writeEnd();
@ -357,8 +388,8 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue,
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource) throws IOException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, CompositeChildElement theChildElem)
throws IOException {
switch (theChildDef.getChildType()) {
case ID_DATATYPE: {
@ -419,7 +450,7 @@ public class JsonParser extends BaseParser implements IParser {
if (theNextValue instanceof IBaseExtension) {
theWriter.write("url", ((IBaseExtension<?, ?>) theNextValue).getUrl());
}
encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theWriter, childCompositeDef, theContainedResource);
encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theWriter, childCompositeDef, theContainedResource, theChildElem);
theWriter.writeEnd();
break;
}
@ -445,8 +476,10 @@ public class JsonParser extends BaseParser implements IParser {
case CONTAINED_RESOURCE_LIST:
case CONTAINED_RESOURCES: {
/*
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) {
* continue; } encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue())); }
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
* fixContainedResourceId(next.getId().getValue())); }
*/
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
if (containedResources.size() > 0) {
@ -491,18 +524,12 @@ public class JsonParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren, boolean theContainedResource) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) {
continue;
}
if (nextChild.getElementName().equals("id")) {
continue;
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theContainedResource, CompositeChildElement theParent)
throws IOException {
for (CompositeChildElement nextChildElem : super.compositeChildIterator(theChildren, theContainedResource, theParent)) {
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (gen != null) {
BaseNarrativeDt<?> narr = ((IResource) theResource).getText();
@ -511,20 +538,20 @@ public class JsonParser extends BaseParser implements IParser {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theContainedResource);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theContainedResource, nextChildElem);
continue;
}
}
} else if (nextChild instanceof RuntimeChildContainedResources) {
if (theContainedResource == false) {
String childName = nextChild.getValidChildNames().iterator().next();
BaseRuntimeElementDefinition<?> child = nextChild.getChildByName(childName);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theContainedResource);
}
String childName = nextChild.getValidChildNames().iterator().next();
BaseRuntimeElementDefinition<?> child = nextChild.getChildByName(childName);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theContainedResource, nextChildElem);
continue;
}
List<? extends IBase> values = nextChild.getAccessor().getValues(theNextValue);
values = super.preProcessValues(nextChild, values);
if (values == null || values.isEmpty()) {
continue;
}
@ -537,6 +564,7 @@ public class JsonParser extends BaseParser implements IParser {
int valueIdx = 0;
for (IBase nextValue : values) {
if (nextValue == null || nextValue.isEmpty()) {
if (nextValue instanceof BaseContainedDt) {
if (theContainedResource || getContainedResources().isEmpty()) {
@ -577,15 +605,15 @@ public class JsonParser extends BaseParser implements IParser {
if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
theEventWriter.writeStartArray(childName);
inArray = true;
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem);
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
// suppress narratives from contained resources
} else {
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, nextChildElem);
}
currentChildName = childName;
} else {
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem);
}
if (primitive) {
@ -659,15 +687,14 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theContainedResource) throws IOException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theContainedResource, CompositeChildElement theParent)
throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource, null);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theContainedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theContainedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theContainedResource, theParent);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theContainedResource, theParent);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource)
throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource) throws IOException {
String resourceId = null;
if (theResource instanceof IResource) {
IResource res = (IResource) theResource;
@ -692,8 +719,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theContainedResource, resourceId);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theContainedResource, String theResourceId) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource, String theResourceId) throws IOException {
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
}
@ -717,7 +743,7 @@ public class JsonParser extends BaseParser implements IParser {
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource);
TagList tags = getMetaTagsForEncoding(resource);
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId();
String versionIdPart = resourceId.getVersionIdPart();
@ -745,7 +771,7 @@ public class JsonParser extends BaseParser implements IParser {
for (BaseCodingDt securityLabel : securityLabels) {
theEventWriter.writeStartObject();
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource);
encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource, null);
theEventWriter.writeEnd();
}
theEventWriter.writeEnd();
@ -775,21 +801,12 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.write("contentType", bin.getContentType());
theEventWriter.write("content", bin.getContentAsBase64());
} else {
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theContainedResource);
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theContainedResource, new CompositeChildElement(resDef));
}
theEventWriter.writeEnd();
}
@Override
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false);
eventWriter.flush();
}
@Override
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
@ -821,10 +838,10 @@ public class JsonParser extends BaseParser implements IParser {
}
/**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
*/
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IBaseResource theResource, String theParentExtensionUrl) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, String theParentExtensionUrl) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -905,6 +922,11 @@ public class JsonParser extends BaseParser implements IParser {
}
}
@Override
public EncodingEnum getEncoding() {
return EncodingEnum.JSON;
}
private void parseAlternates(JsonValue theAlternateVal, ParserState<?> theState, String theElementName) {
if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
return;
@ -958,8 +980,7 @@ public class JsonParser extends BaseParser implements IParser {
object = reader.readObject();
} catch (JsonParsingException e) {
if (e.getMessage().startsWith("Unexpected char 39")) {
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage()
+ " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
}
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
}
@ -1131,9 +1152,9 @@ public class JsonParser extends BaseParser implements IParser {
JsonObject nextObject = (JsonObject) theJsonVal;
boolean preResource = false;
if (theState.isPreResource()) {
String resType = nextObject.getString("resourceType");
String resType = nextObject.getString("resourceType", null);
if (isBlank(resType)) {
throw new DataFormatException("Missing 'resourceType' from resource");
throw new DataFormatException("Missing required element 'resourceType' from JSON resource object, unable to parse");
}
theState.enteringNewElement(null, resType);
preResource = true;
@ -1190,7 +1211,8 @@ public class JsonParser extends BaseParser implements IParser {
}
}
// private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) {
// private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String
// theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) {
// String extUrl = UrlUtil.constructAbsoluteUrl(theParentExtensionUrl, theExtensionUrl);
// theState.enteringNewElementExtension(null, extUrl, theModifier);
//
@ -1215,31 +1237,6 @@ public class JsonParser extends BaseParser implements IParser {
// theState.endingElement();
// }
@Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
try {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(theResourceType, myContext, true, getErrorHandler());
state.enteringNewElement(null, resourceType);
parseChildren(object, state);
state.endingElement();
@SuppressWarnings("unchecked")
T retVal = (T) state.getObject();
return retVal;
} catch (JsonParsingException e) {
throw new DataFormatException("Failed to parse JSON: " + e.getMessage(), e);
}
}
@Override
public TagList parseTagList(Reader theReader) {
@ -1323,8 +1320,7 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
List<HeldExtension> modifierExtensions, String theParentExtensionUrl) throws IOException {
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, String theParentExtensionUrl) throws IOException {
if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) {
@ -1341,18 +1337,18 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) {
if (theValue != null && theValue.isEmpty() == false) {
theEventWriter.write(theElementName, theValue.getValue().intValue());
}
}
private void writeOptionalTagWithDecimalNode(JsonGenerator theEventWriter, String theElementName, DecimalDt theValue) {
if (theValue != null && theValue.isEmpty() == false) {
theEventWriter.write(theElementName, theValue.getValue());
}
}
private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) {
if (theValue != null && theValue.isEmpty() == false) {
theEventWriter.write(theElementName, theValue.getValue().intValue());
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> thePrimitive) {
if (thePrimitive == null) {
return;
@ -1387,9 +1383,9 @@ public class JsonParser extends BaseParser implements IParser {
private class HeldExtension implements Comparable<HeldExtension> {
private RuntimeChildDeclaredExtensionDefinition myDef;
private boolean myModifier;
private IBaseExtension<?, ?> myUndeclaredExtension;
private IBase myValue;
private boolean myModifier;
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier) {
assert theUndeclaredExtension != null;
@ -1404,6 +1400,15 @@ public class JsonParser extends BaseParser implements IParser {
myValue = theValue;
}
@Override
public int compareTo(HeldExtension theArg0) {
String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrl();
String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrl();
url1 = defaultString(url1);
url2 = defaultString(url2);
return url1.compareTo(url2);
}
public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter) throws IOException {
if (myUndeclaredExtension != null) {
writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, myUndeclaredExtension);
@ -1416,7 +1421,7 @@ public class JsonParser extends BaseParser implements IParser {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myDef.getExtensionUrl());
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, null);
}
theEventWriter.writeEnd();
@ -1455,7 +1460,7 @@ public class JsonParser extends BaseParser implements IParser {
if (childDef == null) {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
}
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, null);
}
// theEventWriter.name(myUndeclaredExtension.get);
@ -1463,14 +1468,5 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
@Override
public int compareTo(HeldExtension theArg0) {
String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrl();
String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrl();
url1 = defaultString(url1);
url2 = defaultString(url2);
return url1.compareTo(url2);
}
}
}

View File

@ -227,7 +227,7 @@ class ParserState<T> {
/**
* @param theResourceType
* May be null
* May be null
*/
static <T extends IBaseResource> ParserState<T> getPreResourceInstance(Class<T> theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler)
throws DataFormatException {
@ -633,8 +633,9 @@ class ParserState<T> {
public void attributeValue(String theName, String theValue) throws DataFormatException {
if (myJsonMode) {
string(theValue);
} else {
super.attributeValue(theName, theValue);
}
super.attributeValue(theName, theValue);
}
@Override
@ -796,6 +797,10 @@ class ParserState<T> {
myPreResourceState = thePreResourceState;
}
/**
* @param theValue
* The attribute value
*/
public void attributeValue(String theName, String theValue) throws DataFormatException {
myErrorHandler.unknownAttribute(null, theName);
}
@ -804,6 +809,15 @@ class ParserState<T> {
// ignore by default
}
protected void logAndSwallowUnexpectedElement(String theLocalPart) {
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
}
/**
* @param theNamespaceUri
* The XML namespace (if XML) or null
*/
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
myErrorHandler.unknownElement(null, theLocalPart);
}
@ -860,7 +874,7 @@ class ParserState<T> {
/**
* @param theData
* The string value
* The string value
*/
public void string(String theData) {
// ignore by default
@ -872,7 +886,7 @@ class ParserState<T> {
/**
* @param theNextEvent
* The XML event
* The XML event
*/
public void xmlEvent(XMLEvent theNextEvent) {
// ignore
@ -1028,6 +1042,7 @@ class ParserState<T> {
private BundleEntry myEntry;
private Class<? extends IBaseResource> myResourceType;
private IdDt myFullUrl;
public BundleEntryState(Bundle theInstance, Class<? extends IBaseResource> theResourceType) {
super(null);
@ -1046,7 +1061,7 @@ class ParserState<T> {
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
if ("base".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getLinkBase()));
} else if ("transaction".equals(theLocalPart)) {
} else if ("request".equals(theLocalPart)) {
push(new BundleEntryTransactionState(myEntry));
} else if ("search".equals(theLocalPart)) {
push(new BundleEntrySearchState(myEntry));
@ -1058,6 +1073,9 @@ class ParserState<T> {
push(new BundleEntryDeletedState(getPreResourceState(), myEntry));
} else if ("link".equals(theLocalPart)) {
push(new BundleLinkState(myEntry));
} else if ("fullUrl".equals(theLocalPart)) {
myFullUrl = new IdDt();
push(new PrimitiveState(getPreResourceState(), myFullUrl));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
}
@ -1077,6 +1095,10 @@ class ParserState<T> {
if (id != null && id.isEmpty() == false) {
myEntry.getResource().setId(id);
}
if (myFullUrl != null && !myFullUrl.isEmpty()) {
myEntry.getResource().setId(myFullUrl);
}
Map<ResourceMetadataKeyEnum<?>, Object> metadata = myEntry.getResource().getResourceMetadata();
if (myEntry.getPublished().isEmpty() == false) {
@ -1143,13 +1165,13 @@ class ParserState<T> {
} else if ("url".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getLinkSearch()));
} else {
ourLog.warn("Unexpected element in Bundle.entry.search: " + theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
logAndSwallowUnexpectedElement(theLocalPart);
}
}
}
private class BundleLinkState extends BaseState {
private BundleEntry myEntry;
@ -1226,7 +1248,7 @@ class ParserState<T> {
}
}
private class BundleState extends BaseState {
private Bundle myInstance;
@ -1323,7 +1345,9 @@ class ParserState<T> {
} else {
baseUrl = bundleBaseUrl;
}
if (!baseUrl.startsWith("cid:") && !baseUrl.startsWith("urn:")) {
if (baseUrl == null) {
// nothing
} else if (!baseUrl.startsWith("cid:") && !baseUrl.startsWith("urn:")) {
nextResource.setId(new IdDt(baseUrl, resourceName, bundleIdPart, version));
} else {
if (baseUrl.endsWith(":")) {
@ -1528,6 +1552,8 @@ class ParserState<T> {
}
} else if ("url".equals(theName) && myInstance instanceof ExtensionDt) {
((ExtensionDt) myInstance).setUrl(theValue);
} else {
super.attributeValue(theName, theValue);
}
}
@ -1553,13 +1579,13 @@ class ParserState<T> {
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
}
if ((child.getMax() == 0 || child.getMax() == 1) && !myParsedNonRepeatableNames.add(theChildName)) {
myErrorHandler.unexpectedRepeatingElement(null, theChildName);
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
}
BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
if (target == null) {
// This is a bug with the structures and shouldn't happen..
@ -2328,11 +2354,14 @@ class ParserState<T> {
} else if ("resource".equals(theLocalPart)) {
mySubState = ResourceReferenceSubState.REFERENCE;
break;
} else {
logAndSwallowUnexpectedElement(theLocalPart);
break;
}
//$FALL-THROUGH$
case DISPLAY:
case REFERENCE:
throw new DataFormatException("Unexpected element: " + theLocalPart);
logAndSwallowUnexpectedElement(theLocalPart);
break;
}
}

View File

@ -19,8 +19,9 @@ package ca.uhn.fhir.parser;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.Reader;
@ -84,6 +85,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
@ -91,8 +93,7 @@ import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
/**
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use
* {@link FhirContext#newXmlParser()} to get an instance.
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use {@link FhirContext#newXmlParser()} to get an instance.
*/
public class XmlParser extends BaseParser implements IParser {
@ -111,8 +112,7 @@ public class XmlParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke
* {@link FhirContext#newXmlParser()}.
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke {@link FhirContext#newXmlParser()}.
*
* @param theParserErrorHandler
*/
@ -158,6 +158,39 @@ public class XmlParser extends BaseParser implements IParser {
}
}
@Override
public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
try {
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
encodeBundleToWriterDstu2(theBundle, eventWriter);
} else {
encodeBundleToWriterDstu1(theBundle, eventWriter);
}
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
}
@Override
public void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws DataFormatException {
XMLStreamWriter eventWriter;
try {
eventWriter = createXmlWriter(theWriter);
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
eventWriter.flush();
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
}
@Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
XMLEventReader streamReader = createStreamReader(theReader);
return parseResource(theResourceType, streamReader);
}
private <T> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
ourLog.trace("Entering XML parsing loop with state: {}", parserState);
@ -233,20 +266,6 @@ public class XmlParser extends BaseParser implements IParser {
return stringWriter.toString();
}
@Override
public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
try {
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
encodeBundleToWriterDstu2(theBundle, eventWriter);
} else {
encodeBundleToWriterDstu1(theBundle, eventWriter);
}
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
}
private void encodeBundleToWriterDstu1(Bundle theBundle, XMLStreamWriter eventWriter) throws XMLStreamException {
eventWriter.writeStartElement("feed");
eventWriter.writeDefaultNamespace(ATOM_NS);
@ -385,9 +404,6 @@ public class XmlParser extends BaseParser implements IParser {
String bundleBaseUrl = theBundle.getLinkBase().getValue();
writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue());
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
writeOptionalTagWithValue(theEventWriter, "base", bundleBaseUrl);
}
writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString());
writeBundleResourceLink(theEventWriter, "first", theBundle.getLinkFirst());
@ -404,18 +420,12 @@ public class XmlParser extends BaseParser implements IParser {
deleted = true;
}
if (!myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
writeOptionalTagWithValue(theEventWriter, "base", determineResourceBaseUrl(bundleBaseUrl, nextEntry));
}
writeBundleResourceLink(theEventWriter, "alternate", nextEntry.getLinkAlternate());
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) {
writeOptionalTagWithValue(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue());
}
if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) {
writeOptionalTagWithValue(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue());
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
theEventWriter.writeStartElement("resource");
@ -434,7 +444,7 @@ public class XmlParser extends BaseParser implements IParser {
}
if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) {
theEventWriter.writeStartElement("transaction");
theEventWriter.writeStartElement("request");
writeOptionalTagWithValue(theEventWriter, "method", nextEntry.getTransactionMethod().getValue());
writeOptionalTagWithValue(theEventWriter, "url", nextEntry.getLinkSearch().getValue());
theEventWriter.writeEndElement();
@ -456,7 +466,8 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.close();
}
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef,
String theExtensionUrl, boolean theIncludedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException {
if (nextValue == null || nextValue.isEmpty()) {
if (isChildContained(childDef, theIncludedResource)) {
// We still want to go in..
@ -495,7 +506,7 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeAttribute("url", theExtensionUrl);
}
BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) childDef;
encodeCompositeElementToStreamWriter(theResource, nextValue, theEventWriter, childCompositeDef, theIncludedResource);
encodeCompositeElementToStreamWriter(theResource, nextValue, theEventWriter, childCompositeDef, theIncludedResource, theParent);
theEventWriter.writeEndElement();
break;
}
@ -511,10 +522,9 @@ public class XmlParser extends BaseParser implements IParser {
case CONTAINED_RESOURCE_LIST:
case CONTAINED_RESOURCES: {
/*
* Disable per #103 for (IResource next : value.getContainedResources()) { if
* (getContainedResources().getResourceId(next) != null) { continue; }
* theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter, true,
* fixContainedResourceId(next.getId().getValue())); theEventWriter.writeEndElement(); }
* Disable per #103 for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
* theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
* theEventWriter.writeEndElement(); }
*/
for (IBaseResource next : getContainedResources().getContainedResources()) {
IIdType resourceId = getContainedResources().getResourceId(next);
@ -558,16 +568,13 @@ public class XmlParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) {
continue;
}
if (nextChild.getElementName().equals("id")) {
continue;
}
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren,
boolean theContainedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException {
for (CompositeChildElement nextChildElem : super.compositeChildIterator(theChildren, theContainedResource, theParent)) {
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (theResource instanceof IResource) {
BaseNarrativeDt<?> narr = ((IResource) theResource).getText();
@ -579,36 +586,22 @@ public class XmlParser extends BaseParser implements IParser {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theContainedResource, nextChildElem);
continue;
}
} else {
// Narrative generation not currently supported for HL7org structures
// INarrative narr1 = ((IDomainResource) theResource).getText();
// BaseNarrativeDt<?> narr2 = null;
// if (gen != null && narr1.isEmpty()) {
// // TODO: need to implement this
// String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile();
// gen.generateNarrative(resourceProfile, theResource, null);
// }
// if (narr2 != null) {
// RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
// String childName = nextChild.getChildNameByDatatype(child.getDatatype());
// BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
// encodeChildElementToStreamWriter(theResource, theEventWriter, narr2, childName, type, null,
// theIncludedResource);
// continue;
// }
}
}
if (nextChild instanceof RuntimeChildContainedResources) {
if (!theIncludedResource) {
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theIncludedResource);
}
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource,
nextChildElem);
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
values = super.preProcessValues(nextChild, values);
if (values == null || values.isEmpty()) {
continue;
}
@ -627,7 +620,7 @@ public class XmlParser extends BaseParser implements IParser {
if (nextValue instanceof IBaseExtension && myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
// This is called for the Query resource in DSTU1 only
extensionUrl = ((IBaseExtension<?, ?>) nextValue).getUrl();
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem);
} else if (extensionUrl != null && childName.equals("extension") == false) {
RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
@ -638,22 +631,23 @@ public class XmlParser extends BaseParser implements IParser {
}
theEventWriter.writeAttribute("url", extensionUrl);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, null, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, null, theContainedResource, nextChildElem);
theEventWriter.writeEndElement();
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theIncludedResource) {
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
// suppress narratives from contained resources
} else {
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem);
}
}
}
}
}
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition,
boolean theIncludedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource, theParent);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource, theParent);
}
private void encodeExtensionsIfPresent(IBaseResource theResource, XMLStreamWriter theWriter, IBase theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
@ -672,17 +666,6 @@ public class XmlParser extends BaseParser implements IParser {
}
}
/**
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to
* java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be rejected by the
* compiler some of the time.
*/
private <Q extends IBaseExtension<?, ?>> List<IBaseExtension<?, ?>> toBaseExtensionList(final List<Q> theList) {
List<IBaseExtension<?, ?>> retVal = new ArrayList<IBaseExtension<?, ?>>(theList.size());
retVal.addAll(theList);
return retVal;
}
private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, IBaseReference theRef, IBaseResource theResource, boolean theIncludedResource) throws XMLStreamException {
String reference = determineReferenceText(theRef);
@ -700,10 +683,11 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
/*
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way
* to make that happen, but hopefully this won't matter as much once we use the HL7 structures
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way to make that happen, but hopefully this won't matter as much once we use the HL7
* structures
*/
List<BaseRuntimeChildDefinition> preExtensionChildren = new ArrayList<BaseRuntimeChildDefinition>();
@ -718,28 +702,18 @@ public class XmlParser extends BaseParser implements IParser {
postExtensionChildren.add(next);
}
}
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, preExtensionChildren, theIncludedResource);
CompositeChildElement parent = new CompositeChildElement(theResDef);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, preExtensionChildren, theIncludedResource, parent);
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource, parent);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, postExtensionChildren, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, postExtensionChildren, theIncludedResource, parent);
}
@Override
public void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws DataFormatException {
XMLStreamWriter eventWriter;
try {
eventWriter = createXmlWriter(theWriter);
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
eventWriter.flush();
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
}
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
String resourceId = null;
if (theResource instanceof IResource) {
@ -780,7 +754,7 @@ public class XmlParser extends BaseParser implements IParser {
// HL7.org Structures
writeOptionalTagWithValue(theEventWriter, "id", theResourceId);
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource);
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource, new CompositeChildElement(resDef));
} else {
@ -799,7 +773,8 @@ public class XmlParser extends BaseParser implements IParser {
}
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource);
TagList tags = getMetaTagsForEncoding((resource));
if (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) {
theEventWriter.writeStartElement("meta");
writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart);
@ -815,7 +790,7 @@ public class XmlParser extends BaseParser implements IParser {
for (BaseCodingDt securityLabel : securityLabels) {
theEventWriter.writeStartElement("security");
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
encodeCompositeElementChildrenToStreamWriter(resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource);
encodeCompositeElementChildrenToStreamWriter(resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource, null);
theEventWriter.writeEndElement();
}
if (tags != null) {
@ -855,7 +830,7 @@ public class XmlParser extends BaseParser implements IParser {
}
theEventWriter.writeCharacters(bin.getContentAsBase64());
} else {
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource);
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource, new CompositeChildElement(resDef));
}
}
@ -896,7 +871,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource)
throws XMLStreamException, DataFormatException {
for (IBaseExtension<?, ?> next : theExtensions) {
if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) {
continue;
@ -925,7 +901,7 @@ public class XmlParser extends BaseParser implements IParser {
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
}
}
encodeChildElementToStreamWriter(theResource, theWriter, value, childName, childDef, null, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theWriter, value, childName, childDef, null, theIncludedResource, null);
}
// child extensions
@ -936,7 +912,7 @@ public class XmlParser extends BaseParser implements IParser {
}
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
if (theDt == null || theDt.getValue() == null || getSuppressNarratives()) {
if (theDt == null || theDt.getValue() == null) {
return;
}
@ -1028,6 +1004,11 @@ public class XmlParser extends BaseParser implements IParser {
}
}
@Override
public EncodingEnum getEncoding() {
return EncodingEnum.XML;
}
@Override
public <T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
XMLEventReader streamReader = createStreamReader(theReader);
@ -1040,12 +1021,6 @@ public class XmlParser extends BaseParser implements IParser {
return doXmlLoop(theStreamReader, parserState);
}
@Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
XMLEventReader streamReader = createStreamReader(theReader);
return parseResource(theResourceType, streamReader);
}
private <T extends IBaseResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
ParserState<T> parserState = ParserState.getPreResourceInstance(theResourceType, myContext, false, getErrorHandler());
return doXmlLoop(theStreamReader, parserState);
@ -1065,6 +1040,16 @@ public class XmlParser extends BaseParser implements IParser {
return this;
}
/**
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be
* rejected by the compiler some of the time.
*/
private <Q extends IBaseExtension<?, ?>> List<IBaseExtension<?, ?>> toBaseExtensionList(final List<Q> theList) {
List<IBaseExtension<?, ?>> retVal = new ArrayList<IBaseExtension<?, ?>>(theList.size());
retVal.addAll(theList);
return retVal;
}
private void writeAtomLink(XMLStreamWriter theEventWriter, String theRel, StringDt theStringDt) throws XMLStreamException {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.writeStartElement("link");
@ -1144,4 +1129,5 @@ public class XmlParser extends BaseParser implements IParser {
}
theEventWriter.writeEndElement();
}
}

View File

@ -46,7 +46,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* Note that for a
* server implementation, the {@link #type()} annotation is optional if the
* method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* <li>
* To add tag(s) on the server <b>to the given version of the
@ -61,7 +61,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* operation.
* Note that for a server implementation, the
* {@link #type()} annotation is optional if the method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* </ul>
*/

View File

@ -35,5 +35,16 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ConditionalUrlParam {
// just a marker
/**
* Does this param support operating over multiple objects without throwing an error? This
* should be set to <code>true</code> only for conditional delete operations if the server
* supports multiple deletes via a conditional URL.
* <p>
* Note that this flag is only a hint to the Conformance statement generator,
* it does not actually affect how the server itself works.
* </p>
*/
boolean supportsMultiple() default false;
}

View File

@ -44,7 +44,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* Note that for a
* server implementation, the {@link #type()} annotation is optional if the
* method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* <li>
* To delete tag(s) on the server <b>to the given version of the
@ -57,7 +57,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* to be deleted.
* Note that for a server implementation, the
* {@link #type()} annotation is optional if the method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* </ul>
*/

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.gclient;
package ca.uhn.fhir.rest.annotation;
/*
* #%L
@ -20,17 +20,18 @@ package ca.uhn.fhir.rest.gclient;
* #L%
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @deprecated Use {@link QuantityClientParam} instead. That class is identical to this one but was renamed to reduct
* confusing duplicate names in the API. This class will be removed in a future release.
* On a method which returns resource(s), a parameter of type
* <code>Set&lt;String&gt;</code> with this annotation will be passed the
* contents of the <code>_elements</code> parameter
*/
@Deprecated
public class QuantityParam extends QuantityClientParam {
public QuantityParam(String theParamName) {
super(theParamName);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Elements {
// just a marker
}

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* contain a {@link #type()} attribute, and the method should not have an ID or
* Version ID parameter. On server implementations, the method must be defined
* in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#plain_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#plain_providers"
* >plain provider</a>.</li>
* <li>
* To return a list of all tags on the server <b>for the given resource
@ -46,7 +46,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* specifying the resource type, and the method should not have an ID or Version
* ID parameter. Note that for a server implementation, the {@link #type()}
* annotation is optional if the method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* <li>
* To return a list of all tags on the server <b>for the given resource
@ -55,7 +55,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* {@link IdDt} annotated with the {@link IdParam} annotation. Note that for a
* server implementation, the {@link #type()} annotation is optional if the
* method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* <li>
* To return a list of all tags on the server <b>for the given version of the
@ -65,7 +65,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
* annotation, <b>and</b> a parameter of type {@link IdDt} annotated with the
* {@link IdParam} annotation. Note that for a server implementation, the
* {@link #type()} annotation is optional if the method is defined in a <a href=
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
* "http://jamesagnew.github.io/hapi-fhir/doc_rest_server.html#resource_providers"
* >resource provider</a>, since the type is implied.</li>
* </ul>
*/

View File

@ -59,16 +59,16 @@ public @interface OperationParam {
Class<? extends IBase> type() default IBase.class;
/**
* The minimum number of repetitions allowed for this child
* The minimum number of repetitions allowed for this child (default is 0)
*/
int min() default 0;
/**
* The maximum number of repetitions allowed for this child. Should be
* set to {@link #MAX_UNLIMITED} if there is no limit to the number of
* repetitions.
* repetitions (default is 1)
*/
int max() default MAX_UNLIMITED;
int max() default 1;
}

View File

@ -0,0 +1,179 @@
package ca.uhn.fhir.rest.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.HashMap;
import java.util.Map;
import net.sourceforge.cobertura.CoverageIgnore;
@CoverageIgnore
public enum RestOperationTypeEnum {
ADD_TAGS("add-tags"),
DELETE_TAGS("delete-tags"),
GET_TAGS("get-tags"),
GET_PAGE("get-page"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_SERVER("extended-operation-server"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_TYPE("extended-operation-type"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_INSTANCE("extended-operation-instance"),
/**
* Code Value: <b>create</b>
*/
CREATE("create"),
/**
* Code Value: <b>delete</b>
*/
DELETE("delete"),
/**
* Code Value: <b>history-instance</b>
*/
HISTORY_INSTANCE("history-instance"),
/**
* Code Value: <b>history-system</b>
*/
HISTORY_SYSTEM("history-system"),
/**
* Code Value: <b>history-type</b>
*/
HISTORY_TYPE("history-type"),
/**
* Code Value: <b>read</b>
*/
READ("read"),
/**
* Code Value: <b>search-system</b>
*/
SEARCH_SYSTEM("search-system"),
/**
* Code Value: <b>search-type</b>
*/
SEARCH_TYPE("search-type"),
/**
* Code Value: <b>transaction</b>
*/
TRANSACTION("transaction"),
/**
* Code Value: <b>update</b>
*/
UPDATE("update"),
/**
* Code Value: <b>validate</b>
*/
VALIDATE("validate"),
/**
* Code Value: <b>vread</b>
*/
VREAD("vread"),
/**
* Load the server's metadata
*/
METADATA("metadata"),
/**
* $meta-add extended operation
*/
META_ADD("$meta-add"),
/**
* $meta-add extended operation
*/
META("$meta"),
/**
* $meta-delete extended operation
*/
META_DELETE("$meta-delete"),
;
private static Map<String, RestOperationTypeEnum> CODE_TO_ENUM = new HashMap<String, RestOperationTypeEnum>();
/**
* Identifier for this Value Set: http://hl7.org/fhir/vs/type-restful-operation
*/
public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/vs/type-restful-operation";
/**
* Name for this Value Set: RestfulOperationType
*/
public static final String VALUESET_NAME = "RestfulOperationType";
static {
for (RestOperationTypeEnum next : RestOperationTypeEnum.values()) {
CODE_TO_ENUM.put(next.getCode(), next);
}
}
private final String myCode;
/**
* Constructor
*/
RestOperationTypeEnum(String theCode) {
myCode = theCode;
}
/**
* Returns the enumerated value associated with this code
*/
public RestOperationTypeEnum forCode(String theCode) {
RestOperationTypeEnum retVal = CODE_TO_ENUM.get(theCode);
return retVal;
}
/**
* Returns the code associated with this enumerated value
*/
public String getCode() {
return myCode;
}
}

View File

@ -0,0 +1,79 @@
package ca.uhn.fhir.rest.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.HashMap;
import java.util.Map;
/**
* Enum representing the values for the <code>_summary</code> search parameter
*/
public enum SummaryEnum {
/**
* Return only those elements marked as 'summary' in the base definition of the resource(s)
*/
TRUE("true"),
/**
* Return only the 'text' element, and any mandatory elements
*/
TEXT("text"),
/**
* Remove the text element
*/
DATA("data"),
/**
* Search only: just return a count of the matching resources, without returning the actual matches
*/
COUNT("count"),
/**
* Return all parts of the resource(s)
*/
FALSE("false");
private String myCode;
private static Map<String, SummaryEnum> ourCodeToSummary = null;
SummaryEnum(String theCode) {
myCode = theCode;
}
public String getCode() {
return myCode;
}
public static SummaryEnum fromCode(String theCode) {
Map<String, SummaryEnum> c2s = ourCodeToSummary;
if (c2s == null) {
c2s = new HashMap<String, SummaryEnum>();
for (SummaryEnum next : values()) {
c2s.put(next.getCode(), next);
}
ourCodeToSummary = c2s;
}
return c2s.get(theCode);
}
}

View File

@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -50,6 +51,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
@ -70,8 +72,9 @@ public abstract class BaseClient implements IRestfulClient {
private HttpResponse myLastResponse;
private String myLastResponseBody;
private Boolean myPrettyPrint = false;
private SummaryEnum mySummary;
private final String myUrlBase;
BaseClient(HttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
super();
myClient = theClient;
@ -149,6 +152,10 @@ public abstract class BaseClient implements IRestfulClient {
return myUrlBase;
}
public SummaryEnum getSummary() {
return mySummary;
}
public String getUrlBase() {
return myUrlBase;
}
@ -158,11 +165,11 @@ public abstract class BaseClient implements IRestfulClient {
}
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, boolean theLogRequestAndResponse) {
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse);
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse, null, null);
}
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint,
boolean theLogRequestAndResponse) {
boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements) {
if (!myDontValidateConformance) {
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
@ -180,10 +187,20 @@ public abstract class BaseClient implements IRestfulClient {
} else if (theEncoding == EncodingEnum.JSON) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
}
if (theSummaryMode != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(theSummaryMode.getCode()));
} else if (mySummary != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(mySummary.getCode()));
}
if (thePrettyPrint == Boolean.TRUE) {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
if (theSubsetElements != null && theSubsetElements.isEmpty()== false) {
params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
}
EncodingEnum encoding = getEncoding();
if (theEncoding != null) {
@ -373,6 +390,7 @@ public abstract class BaseClient implements IRestfulClient {
}
}
@Override
public void registerInterceptor(IClientInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null");
myInterceptors.add(theInterceptor);
@ -390,6 +408,7 @@ public abstract class BaseClient implements IRestfulClient {
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is perfectly acceptable behaviour according
* to the FHIR specification. In this case, the server will choose which encoding to return, and the client can handle either XML or JSON)
*/
@Override
public void setEncoding(EncodingEnum theEncoding) {
myEncoding = theEncoding;
// return this;
@ -420,11 +439,18 @@ public abstract class BaseClient implements IRestfulClient {
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* HAPI based servers (and any other servers which might implement it).
*/
@Override
public void setPrettyPrint(Boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
// return this;
}
@Override
public void setSummary(SummaryEnum theSummary) {
mySummary = theSummary;
}
@Override
public void unregisterInterceptor(IClientInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null");
myInterceptors.remove(theInterceptor);

View File

@ -28,6 +28,7 @@ import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -35,10 +36,10 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
class ClientInvocationHandlerFactory {
private final Map<Method, BaseMethodBinding<?>> myBindings = new HashMap<Method, BaseMethodBinding<?>>();
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
private final FhirContext myContext;
private final HttpClient myClient;
private final FhirContext myContext;
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
private final String myUrlBase;
public ClientInvocationHandlerFactory(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
@ -55,6 +56,7 @@ class ClientInvocationHandlerFactory {
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", Boolean.class), new SetPrettyPrintLambda());
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("setSummary", SummaryEnum.class), new SetSummaryLambda());
} catch (NoSuchMethodException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
@ -75,6 +77,15 @@ class ClientInvocationHandlerFactory {
Object handle(ClientInvocationHandler theTarget, Object[] theArgs);
}
class RegisterInterceptorLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
theTarget.registerInterceptor(interceptor);
return null;
}
}
class SetEncodingLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
@ -93,6 +104,15 @@ class ClientInvocationHandlerFactory {
}
}
class SetSummaryLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
SummaryEnum encoding = (SummaryEnum) theArgs[0];
theTarget.setSummary(encoding);
return null;
}
}
class UnregisterInterceptorLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
@ -102,13 +122,4 @@ class ClientInvocationHandlerFactory {
}
}
class RegisterInterceptorLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
theTarget.registerInterceptor(interceptor);
return null;
}
}
}

View File

@ -19,19 +19,20 @@ package ca.uhn.fhir.rest.client;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -49,6 +50,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -67,6 +69,8 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
@ -225,7 +229,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
return delete(theType, new IdDt(theId));
}
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches) {
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) {
String resName = toResourceName(theType);
IIdType id = theId;
if (!id.hasBaseUrl()) {
@ -254,13 +258,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
invocation.addHeader(Constants.HEADER_IF_NONE_MATCH, '"' + theIfVersionMatches + '"');
}
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, id);
boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT);
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, id, allowHtmlResponse);
if (theNotModifiedHandler == null) {
return invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements);
} else {
try {
return invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements);
} catch (NotModifiedException e) {
return theNotModifiedHandler.call();
}
@ -382,6 +387,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new LoadPageInternal();
}
@Override
public IMeta meta() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
throw new IllegalStateException("Can not call $meta operations on a DSTU1 client");
}
return new MetaInternal();
}
@Override
public IOperation operation() {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) == false) {
@ -403,7 +416,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public <T extends IBaseResource> T read(final Class<T> theType, UriDt theUrl) {
IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl);
return doReadOrVRead(theType, id, false, null, null);
return doReadOrVRead(theType, id, false, null, null, false, null, null, null);
}
@Override
@ -549,7 +562,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
if (theId.hasVersionIdPart() == false) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue()));
}
return doReadOrVRead(theType, theId, true, null, null);
return doReadOrVRead(theType, theId, true, null, null, false, null, null, null);
}
/* also deprecated in interface */
@ -579,9 +592,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
private EncodingEnum myParamEncoding;
private Boolean myPrettyPrint;
protected EncodingEnum myParamEncoding;
protected Boolean myPrettyPrint;
private boolean myQueryLogRequestAndResponse;
private HashSet<String> mySubsetElements;
protected SummaryEnum mySummaryMode;
@SuppressWarnings("unchecked")
@Override
@ -608,6 +623,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
return myParamEncoding;
}
protected HashSet<String> getSubsetElements() {
return mySubsetElements;
}
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
// if (myParamEncoding != null) {
// theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
@ -621,7 +640,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint);
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements);
return resp;
}
@ -640,6 +659,24 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T elementsSubset(String... theElements) {
if (theElements != null && theElements.length > 0) {
mySubsetElements = new HashSet<String>(Arrays.asList(theElements));
} else {
mySubsetElements = null;
}
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T summaryMode(SummaryEnum theSummary) {
mySummaryMode = theSummary;
return ((T) this);
}
}
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
@ -661,7 +698,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome>implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
private CriterionList myCriterionList;
private String myId;
@ -781,7 +818,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome> implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped {
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome>implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped {
private CriterionList myCriterionList;
private IIdType myId;
@ -895,7 +932,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object> implements IGetPageTyped<Object> {
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object>implements IGetPageTyped<Object> {
private Class<? extends IBaseBundle> myBundleType;
private String myUrl;
@ -925,7 +962,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList> implements IGetTags {
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList>implements IGetTags {
private String myId;
private String myResourceName;
@ -1167,6 +1204,154 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@SuppressWarnings("rawtypes")
private class MetaInternal extends BaseClientExecutable implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced {
private IIdType myId;
private IBaseMetaType myMeta;
private Class<? extends IBaseMetaType> myMetaType;
private String myOnType;
private MetaOperation myOperation;
@Override
public IMetaAddOrDeleteUnsourced add() {
myOperation = MetaOperation.ADD;
return this;
}
@Override
public IMetaAddOrDeleteUnsourced delete() {
myOperation = MetaOperation.DELETE;
return this;
}
@SuppressWarnings("unchecked")
@Override
public Object execute() {
BaseHttpClientInvocation invocation = null;
IBaseParameters parameters = ParametersUtil.newInstance(myContext);
switch (myOperation) {
case ADD:
ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta");
invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-add", parameters, false);
break;
case DELETE:
ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta");
invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-delete", parameters, false);
break;
case GET:
if (myId != null) {
invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, myId.getIdPart(), "$meta", parameters, true);
} else if (myOnType != null) {
invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, null, "$meta", parameters, true);
} else {
invocation = OperationMethodBinding.createOperationInvocation(myContext, null, null, "$meta", parameters, true);
}
break;
}
// Should not happen
if (invocation == null) {
throw new IllegalStateException();
}
IClientResponseHandler handler;
handler = new MetaParametersResponseHandler(myMetaType);
return invoke(null, handler, invocation);
}
@Override
public IClientExecutable fromResource(IIdType theId) {
setIdInternal(theId);
return this;
}
@Override
public IClientExecutable fromServer() {
return this;
}
@Override
public IClientExecutable fromType(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be blank");
myOnType = theResourceName;
return this;
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseMetaType> IMetaGetUnsourced<T> get(Class<T> theType) {
myMetaType = theType;
myOperation = MetaOperation.GET;
return this;
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseMetaType> IClientExecutable<IClientExecutable<?, ?>, T> meta(T theMeta) {
Validate.notNull(theMeta, "theMeta must not be null");
myMeta = theMeta;
myMetaType = myMeta.getClass();
return this;
}
@Override
public IMetaAddOrDeleteSourced onResource(IIdType theId) {
setIdInternal(theId);
return this;
}
private void setIdInternal(IIdType theId) {
Validate.notBlank(theId.getResourceType(), "theId must contain a resource type");
Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
myOnType = theId.getResourceType();
myId = theId;
}
}
private enum MetaOperation {
ADD, DELETE, GET
}
private final class MetaParametersResponseHandler<T extends IBaseMetaType> implements IClientResponseHandler<T> {
private Class<T> myType;
public MetaParametersResponseHandler(Class<T> theMetaType) {
myType = theMetaType;
}
@SuppressWarnings("unchecked")
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
IParser parser = respType.newParser(myContext);
RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters");
IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseReader);
BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter");
BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
List<IBase> parameter = paramChild.getAccessor().getValues(retVal);
if (parameter == null || parameter.isEmpty()) {
return (T) myContext.getElementDefinition(myType).newInstance();
}
IBase param = parameter.get(0);
List<IBase> meta = paramChildElem.getChildByName("value[x]").getAccessor().getValues(param);
if (meta.isEmpty()) {
return (T) myContext.getElementDefinition(myType).newInstance();
}
return (T) meta.get(0);
}
}
@SuppressWarnings("rawtypes")
private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput {
@ -1321,11 +1506,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
private RuntimeResourceDefinition myType;
@Override
public Object execute() {
public Object execute() {//AAA
if (myId.hasVersionIdPart()) {
return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches);
return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements());
} else {
return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches);
return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements());
}
}
@ -1403,6 +1588,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IReadExecutable withId(Long theId) {
Validate.notNull(theId, "The ID can not be null");
myId = new IdDt(myType.getName(), theId);
return this;
}
@Override
public IReadExecutable withId(String theId) {
Validate.notBlank(theId, "The ID can not be blank");
@ -1444,7 +1636,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings("unchecked")
@Override
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
throws BaseServerResponseException {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType, null);
@ -1460,6 +1653,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class ResourceResponseHandler<T extends IBaseResource> implements IClientResponseHandler<T> {
private boolean myAllowHtmlResponse;
private IIdType myId;
private Class<T> myType;
@ -1468,10 +1662,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
myId = theId;
}
public ResourceResponseHandler(Class<T> theType, IIdType theId, boolean theAllowHtmlResponse) {
myType = theType;
myId = theId;
myAllowHtmlResponse = theAllowHtmlResponse;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
if (myAllowHtmlResponse && theResponseMimeType.toLowerCase().contains(Constants.CT_HTML) && myType != null) {
return readHtmlResponse(theResponseReader);
}
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
IParser parser = respType.newParser(myContext);
@ -1481,59 +1684,49 @@ public class GenericClient extends BaseClient implements IGenericClient {
return retVal;
}
}
private final class MetaParametersResponseHandler<T extends IBaseMetaType> implements IClientResponseHandler<T> {
private Class<T> myType;
public MetaParametersResponseHandler(Class<T> theMetaType) {
myType = theMetaType;
}
@SuppressWarnings("unchecked")
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
private T readHtmlResponse(Reader theResponseReader) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(myType);
IBaseResource instance = resDef.newInstance();
BaseRuntimeChildDefinition textChild = resDef.getChildByName("text");
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
IBase textInstance = textElement.newInstance();
textChild.getMutator().addValue(instance, textInstance);
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();
try {
divInstance.setValueAsString(IOUtils.toString(theResponseReader));
} catch (IllegalArgumentException e) {
throw new InvalidResponseException(400, "Failed to process HTML response from server", e);
} catch (IOException e) {
throw new InvalidResponseException(400, "Failed to process HTML response from server", e);
}
IParser parser = respType.newParser(myContext);
RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters");
IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseReader);
BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter");
BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
List<IBase> parameter = paramChild.getAccessor().getValues(retVal);
if (parameter == null || parameter.isEmpty()) {
return (T) myContext.getElementDefinition(myType).newInstance();
}
IBase param = parameter.get(0);
List<IBase> meta = paramChildElem.getChildByName("value[x]").getAccessor().getValues(param);
if (meta.isEmpty()) {
return (T) myContext.getElementDefinition(myType).newInstance();
}
return (T) meta.get(0);
divChild.getMutator().addValue(textInstance, divInstance);
return (T) instance;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object> implements IQuery<Object>, IUntypedQuery {
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object>implements IQuery<Object>, IUntypedQuery {
private String myCompartmentName;
private CriterionList myCriterion = new CriterionList();
private List<Include> myInclude = new ArrayList<Include>();
private DateRangeParam myLastUpdated;
private Integer myParamLimit;
private List<String> myProfile = new ArrayList<String>();
private String myResourceId;
private String myResourceName;
private Class<? extends IBaseResource> myResourceType;
private Class<? extends IBaseBundle> myReturnBundleType;
private List<Include> myRevInclude = new ArrayList<Include>();
private SearchStyleEnum mySearchStyle;
private List<TokenParam> mySecurity = new ArrayList<TokenParam>();
private List<SortInternal> mySort = new ArrayList<SortInternal>();
private List<TokenParam> myTags = new ArrayList<TokenParam>();
public SearchInternal() {
myResourceType = null;
@ -1561,8 +1754,20 @@ public class GenericClient extends BaseClient implements IGenericClient {
addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken());
}
for (TokenParam next : mySecurity) {
addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken());
}
for (String next : myProfile) {
addParam(params, Constants.PARAM_PROFILE, next);
}
for (Include next : myInclude) {
addParam(params, Constants.PARAM_INCLUDE, next.getValue());
if (next.isRecurse()) {
addParam(params, Constants.PARAM_INCLUDE_RECURSE, next.getValue());
} else {
addParam(params, Constants.PARAM_INCLUDE, next.getValue());
}
}
for (Include next : myRevInclude) {
@ -1643,7 +1848,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public IClientExecutable returnBundle(Class theClass) {
public IQuery returnBundle(Class theClass) {
if (theClass == null) {
throw new NullPointerException("theClass must not be null");
}
@ -1694,8 +1899,20 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
private List<TokenParam> myTags = new ArrayList<TokenParam>();
@Override
public IQuery<Object> withProfile(String theProfileUri) {
Validate.notBlank(theProfileUri, "theProfileUri must not be null or empty");
myProfile.add(theProfileUri);
return this;
}
@Override
public IQuery<Object> withSecurity(String theSystem, String theCode) {
Validate.notBlank(theCode, "theCode must not be null or empty");
mySecurity.add(new TokenParam(theSystem, theCode));
return this;
}
@Override
public IQuery<Object> withTag(String theSystem, String theCode) {
Validate.notBlank(theCode, "theCode must not be null or empty");
@ -1750,7 +1967,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class StringResponseHandler implements IClientResponseHandler<String> {
@Override
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
throws IOException, BaseServerResponseException {
return IOUtils.toString(theResponseReader);
}
}
@ -1768,7 +1986,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> implements ITransactionTyped<T> {
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T>implements ITransactionTyped<T> {
private IBaseBundle myBaseBundle;
private Bundle myBundle;
@ -1858,7 +2076,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome>implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
private CriterionList myCriterionList;
private IIdType myId;
@ -1975,7 +2193,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome> implements IValidate, IValidateUntyped {
private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome>implements IValidate, IValidateUntyped {
private IBaseResource myResource;
@Override
@ -2017,124 +2235,4 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@SuppressWarnings("rawtypes")
private class MetaInternal extends BaseClientExecutable implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced {
private MetaOperation myOperation;
private String myOnType;
private IIdType myId;
private Class<? extends IBaseMetaType> myMetaType;
private IBaseMetaType myMeta;
@SuppressWarnings("unchecked")
@Override
public Object execute() {
BaseHttpClientInvocation invocation = null;
IBaseParameters parameters = ParametersUtil.newInstance(myContext);
switch (myOperation) {
case ADD:
ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta");
invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-add", parameters, false);
break;
case DELETE:
ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta");
invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-delete", parameters, false);
break;
case GET:
if (myId != null) {
invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, myId.getIdPart(), "$meta", parameters, true);
} else if (myOnType != null) {
invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, null, "$meta", parameters, true);
} else {
invocation = OperationMethodBinding.createOperationInvocation(myContext, null, null, "$meta", parameters, true);
}
break;
}
// Should not happen
if (invocation == null) {
throw new IllegalStateException();
}
IClientResponseHandler handler;
handler = new MetaParametersResponseHandler(myMetaType);
return invoke(null, handler, invocation);
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseMetaType> IMetaGetUnsourced<T> get(Class<T> theType) {
myMetaType = theType;
myOperation = MetaOperation.GET;
return this;
}
@Override
public IMetaAddOrDeleteUnsourced add() {
myOperation = MetaOperation.ADD;
return this;
}
@Override
public IMetaAddOrDeleteUnsourced delete() {
myOperation = MetaOperation.DELETE;
return this;
}
@Override
public IClientExecutable fromServer() {
return this;
}
@Override
public IClientExecutable fromType(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be blank");
myOnType = theResourceName;
return this;
}
@Override
public IClientExecutable fromResource(IIdType theId) {
setIdInternal(theId);
return this;
}
@Override
public IMetaAddOrDeleteSourced onResource(IIdType theId) {
setIdInternal(theId);
return this;
}
private void setIdInternal(IIdType theId) {
Validate.notBlank(theId.getResourceType(), "theId must contain a resource type");
Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
myOnType = theId.getResourceType();
myId = theId;
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseMetaType> IClientExecutable<IClientExecutable<?, ?>, T> meta(T theMeta) {
Validate.notNull(theMeta, "theMeta must not be null");
myMeta = theMeta;
myMetaType = myMeta.getClass();
return this;
}
}
private enum MetaOperation {
DELETE, ADD, GET
}
@Override
public IMeta meta() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
throw new IllegalStateException("Can not call $meta operations on a DSTU1 client");
}
return new MetaInternal();
}
}

View File

@ -24,6 +24,7 @@ package ca.uhn.fhir.rest.client.api;
import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -40,6 +41,17 @@ public interface IRestfulClient {
*/
HttpClient getHttpClient();
/**
* Base URL for the server, with no trailing "/"
*/
String getServerBase();
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
/**
* Specifies that the client should use the given encoding to do its
* queries. This means that the client will append the "_format" param
@ -50,7 +62,7 @@ public interface IRestfulClient {
* an encoding (which generally implies the use of XML). The default is <code>null</code>.
*/
void setEncoding(EncodingEnum theEncoding);
/**
* Specifies that the client should request that the server respond with "pretty printing"
* enabled. Note that this is a non-standard parameter, not all servers will
@ -61,15 +73,10 @@ public interface IRestfulClient {
void setPrettyPrint(Boolean thePrettyPrint);
/**
* Base URL for the server, with no trailing "/"
* If not set to <code>null</code>, specifies a value for the <code>_summary</code> parameter
* to be applied globally on this client.
*/
String getServerBase();
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
void setSummary(SummaryEnum theSummary);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}

View File

@ -36,7 +36,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
* HTTP interceptor to be used for adding HTTP basic auth username/password tokens
* to requests
* <p>
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a>
* See the <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_client_interceptor.html#Security_HTTP_Basic_Authorization">HAPI Documentation</a>
* for information on how to use this class.
* </p>
*/
@ -44,7 +44,7 @@ public class BasicAuthInterceptor implements IClientInterceptor {
private String myUsername;
private String myPassword;
public BasicAuthInterceptor(String theUsername, String thePassword) {
super();
myUsername = theUsername;

View File

@ -20,44 +20,71 @@ package ca.uhn.fhir.rest.client.interceptor;
* #L%
*/
import java.io.IOException;
import org.apache.commons.lang3.Validate;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.server.Constants;
import net.sourceforge.cobertura.CoverageIgnore;
/**
* HTTP interceptor to be used for adding HTTP Authorization using "bearer tokens" to requests. Bearer tokens are used for protocols such as OAUTH2 (see the <a
* href="http://tools.ietf.org/html/rfc6750">RFC 6750</a> specification on bearer token usage for more information).
* HTTP interceptor to be used for adding HTTP Authorization using "bearer tokens" to requests. Bearer tokens are used for protocols such as OAUTH2 (see the
* <a href="http://tools.ietf.org/html/rfc6750">RFC 6750</a> specification on bearer token usage for more information).
* <p>
* This interceptor adds a header resembling the following:<br>
* &nbsp;&nbsp;&nbsp;<code>Authorization: Bearer dsfu9sd90fwp34.erw0-reu</code><br>
* where the token portion (at the end of the header) is supplied by the invoking code.
* </p>
* <p>
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a> for information on how to use this class.
* See the <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_client_interceptor.html#Security_HTTP_Bearer_Token_Authorization">HAPI Documentation</a> for information on how to use this class.
* </p>
*/
public class BearerTokenAuthInterceptor implements IClientInterceptor {
private String myToken;
/**
* Constructor. If this constructor is used, a token must be supplied later
*/
@CoverageIgnore
public BearerTokenAuthInterceptor() {
// nothing
}
/**
* Constructor
*
* @param theToken
* The bearer token to use (must not be null)
*/
public BearerTokenAuthInterceptor(String theToken) {
Validate.notNull("theToken must not be null");
myToken = theToken;
}
/**
* Returns the bearer token to use
*/
public String getToken() {
return myToken;
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
theRequest.addHeader(Constants.HEADER_AUTHORIZATION, (Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER + myToken));
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(HttpResponse theResponse) {
// nothing
}
/**
* Sets the bearer token to use
*/
public void setToken(String theToken) {
myToken = theToken;
}
}

View File

@ -152,7 +152,7 @@ public class LoggingInterceptor implements IClientInterceptor {
throw new InternalErrorException(e);
}
myLog.info("Client response body:\n{}", new String(bytes));
myLog.info("Client response body:\n{}", new String(bytes, "UTF-8"));
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
} else {
myLog.info("Client response body: (none)");

View File

@ -29,10 +29,6 @@ import ca.uhn.fhir.rest.client.IClientInterceptor;
/**
* HTTP interceptor to be used for adding HTTP headers containing user identifying info for auditing purposes to the request
* <p>
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#User_Info">HAPI Documentation</a>
* for information on how to use this class.
* </p>
*/
public class UserInfoInterceptor implements IClientInterceptor {

View File

@ -1,36 +0,0 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* @deprecated Use {@link CompositeClientParam} instead. That class is identical to this one but was renamed to reduce
* confusing duplicate names in the API. This class will be removed in a future release.
*/
@Deprecated
public class CompositeParam<A extends IParam, B extends IParam> extends CompositeClientParam<A,B> {
public CompositeParam(String theParamName) {
super(theParamName);
}
}

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.gclient;
* #L%
*/
public interface IBaseQuery<T> {
public interface IBaseQuery<T extends IBaseQuery<?>> {
T where(ICriterion<?> theCriterion);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.api.SummaryEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -23,12 +25,6 @@ package ca.uhn.fhir.rest.gclient;
public interface IClientExecutable<T extends IClientExecutable<?,?>, Y> {
Y execute();
T encodedJson();
T encodedXml();
/**
* If set to true, the client will log the request and response to the SLF4J logger. This can be useful for
* debugging, but is generally not desirable in a production situation.
@ -38,6 +34,24 @@ public interface IClientExecutable<T extends IClientExecutable<?,?>, Y> {
@Deprecated
T andLogRequestAndResponse(boolean theLogRequestAndResponse);
/**
* Request that the server return subsetted resources, containing only the elements specified in the given parameters.
* For example: <code>subsetElements("name", "identifier")</code> requests that the server only return
* the "name" and "identifier" fields in the returned resource, and omit any others.
*/
T elementsSubset(String... theElements);
T encodedJson();
T encodedXml();
Y execute();
T prettyPrint();
/**
* Request that the server modify the response using the <code>_summary</code> param
*/
T summaryMode(SummaryEnum theSummary);
}

View File

@ -29,7 +29,13 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<IQuery<T>> {
/**
* Add an "_include" specification
* Add an "_include" specification or an "_include:recurse" specification. If you are using
* a constant from one of the built-in structures you can select whether you want recursive
* behaviour by using the following syntax:
* <ul>
* <li><b>Recurse:</b> <code>.include(Patient.INCLUDE_ORGANIZATION.asRecursive())</code>
* <li><b>No Recurse:</b> <code>.include(Patient.INCLUDE_ORGANIZATION.asNonRecursive())</code>
* </ul>
*/
IQuery<T> include(Include theInclude);
@ -45,6 +51,22 @@ public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<I
*/
IQuery<T> withTag(String theSystem, String theCode);
/**
* Match only resources where the resource has the given security tag. This parameter corresponds to
* the <code>_security</code> URL parameter.
* @param theSystem The tag code system, or <code>null</code> to match any code system (this may not be supported on all servers)
* @param theCode The tag code. Must not be <code>null</code> or empty.
*/
IQuery<T> withSecurity(String theSystem, String theCode);
/**
* Match only resources where the resource has the given profile declaration. This parameter corresponds to
* the <code>_profile</code> URL parameter.
* @param theSystem The tag code system, or <code>null</code> to match any code system (this may not be supported on all servers)
* @param theCode The tag code. Must not be <code>null</code> or empty.
*/
IQuery<T> withProfile(String theProfileUri);
/**
* Forces the query to perform the search using the given method (allowable methods are described in the
* <a href="http://www.hl7.org/implement/standards/fhir/http.html#search">FHIR Specification Section 2.1.11</a>)
@ -74,6 +96,20 @@ public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<I
* Request that the client return the specified bundle type, e.g. <code>org.hl7.fhir.instance.model.Bundle.class</code>
* or <code>ca.uhn.fhir.model.dstu2.resource.Bundle.class</code>
*/
<B extends IBaseBundle> IClientExecutable<IQuery<B>, B> returnBundle(Class<B> theClass);
<B extends IBaseBundle> IQuery<B> returnBundle(Class<B> theClass);
/**
* {@inheritDoc}
*/
// This is here as an overridden method to allow mocking clients with Mockito to work
@Override
IQuery<T> where(ICriterion<?> theCriterion);
/**
* {@inheritDoc}
*/
// This is here as an overridden method to allow mocking clients with Mockito to work
@Override
IQuery<T> and(ICriterion<?> theCriterion);
}

View File

@ -29,6 +29,8 @@ public interface IReadTyped<T extends IBaseResource> {
IReadExecutable<T> withIdAndVersion(String theId, String theVersion);
IReadExecutable<T> withId(Long theId);
/**
* Search using an ID. Note that even if theId contains a base URL it will be
* ignored in favour of the base url for the given client. If you want to specify
@ -40,4 +42,5 @@ public interface IReadTyped<T extends IBaseResource> {
IReadExecutable<T> withUrl(String theUrl);
IReadExecutable<T> withUrl(IIdType theUrl);
}

View File

@ -1,36 +0,0 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* @deprecated Use {@link NumberClientParam} instead. That class is identical to this one but was renamed to reduct
* confusing duplicate names in the API. This class will be removed in a future release.
*/
@Deprecated
public class NumberParam extends NumberClientParam {
public NumberParam(String theParamName) {
super(theParamName);
}
}

View File

@ -1,36 +0,0 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* @deprecated Use {@link ReferenceClientParam} instead. That class is identical to this one but was renamed to reduct
* confusing duplicate names in the API. This class will be removed in a future release.
*/
@Deprecated
public class ReferenceParam extends ReferenceClientParam {
public ReferenceParam(String theParamName) {
super(theParamName);
}
}

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.rest.server.Constants;
* @author james
*
*/
public class StringClientParam extends BaseClientParam implements IParam {
public class StringClientParam extends BaseClientParam implements IParam {
private final String myParamName;
@ -45,7 +45,8 @@ public class StringClientParam extends BaseClientParam implements IParam {
}
/**
* The string matches the given value (servers will often, but are not required to) implement this as a left match, meaning that a value of "smi" would match "smi" and "smith".
* The string matches the given value (servers will often, but are not required to) implement this as a left match,
* meaning that a value of "smi" would match "smi" and "smith".
*/
public IStringMatch matches() {
return new StringMatches();
@ -66,8 +67,9 @@ public class StringClientParam extends BaseClientParam implements IParam {
ICriterion<StringClientParam> value(String theValue);
/**
* Requests that resources be returned which match ANY of the given values (this is an OR search). Note that to specify an AND search, simply add a subsequent {@link IQuery#where(ICriterion)
* where} criteria with the same parameter.
* Requests that resources be returned which match ANY of the given values (this is an OR search, not an AND search). Note that to
* specify an AND search, simply add a subsequent {@link IQuery#where(ICriterion) where} criteria with the same
* parameter.
*/
ICriterion<StringClientParam> values(List<String> theValues);
@ -77,8 +79,9 @@ public class StringClientParam extends BaseClientParam implements IParam {
ICriterion<StringClientParam> value(StringDt theValue);
/**
* Requests that resources be returned which match ANY of the given values (this is an OR search). Note that to specify an AND search, simply add a subsequent {@link IQuery#where(ICriterion)
* where} criteria with the same parameter.
* Requests that resources be returned which match ANY of the given values (this is an OR search, not an AND search). Note that to
* specify an AND search, simply add a subsequent {@link IQuery#where(ICriterion) where} criteria with the same
* parameter.
*/
ICriterion<?> values(String... theValues);

View File

@ -1,36 +0,0 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* @deprecated Use {@link StringClientParam} instead. That class is identical to this one but was renamed to reduct
* confusing duplicate names in the API. This class will be removed in a future release.
*/
@Deprecated
public class StringParam extends StringClientParam {
public StringParam(String theParamName) {
super(theParamName);
}
}

View File

@ -24,6 +24,7 @@ import java.lang.reflect.Method;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.AddTags;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
@ -37,8 +38,8 @@ class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
}
@Override
public OtherOperationTypeEnum getOtherOperationType() {
return OtherOperationTypeEnum.ADD_TAGS;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.ADD_TAGS;
}
}

View File

@ -36,13 +36,12 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.TagListParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
@ -90,7 +89,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
}
@Override
public Void invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public Void invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED:
@ -108,12 +107,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return null;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
public RestOperationTypeEnum getRestOperationType() {
return null;
}
@ -177,7 +171,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
} finally {
reader.close();
}
invokeServerMethod(params);
invokeServerMethod(theServer, theRequest, params);
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
@ -190,7 +184,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
HttpServletResponse response = theRequest.getServletResponse();
response.setContentType(Constants.CT_TEXT);
response.setStatus(Constants.STATUS_HTTP_200_OK);
response.setCharacterEncoding(Constants.CHARSETNAME_UTF_8);
response.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
theServer.addHeadersToResponse(response);

View File

@ -19,7 +19,6 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
@ -46,8 +45,6 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.AddTags;
import ca.uhn.fhir.rest.annotation.Create;
@ -63,6 +60,7 @@ import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.server.BundleProviders;
@ -81,6 +79,8 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
@ -95,6 +95,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
private List<IParameter> myParameters;
private Object myProvider;
private boolean mySupportsConditional;
private boolean mySupportsConditionalMultiple;
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
assert theMethod != null;
@ -103,11 +104,14 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
myMethod = theMethod;
myContext = theContext;
myProvider = theProvider;
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getResourceOperationType());
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getRestOperationType());
for (IParameter next : myParameters) {
if (next instanceof ConditionalParamBinder) {
mySupportsConditional = true;
if (((ConditionalParamBinder) next).isSupportsMultiple()) {
mySupportsConditionalMultiple = true;
}
break;
}
}
@ -181,10 +185,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return myMethod;
}
public OtherOperationTypeEnum getOtherOperationType() {
return null;
}
public List<IParameter> getParameters() {
return myParameters;
}
@ -207,8 +207,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
index++;
}
if (!match)
if (!match) {
return null;
}
if (index >= params.length) {
ourLog.warn("index out of parameter range (should never happen");
return null;
@ -234,37 +235,36 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
*/
public abstract String getResourceName();
public abstract RestfulOperationTypeEnum getResourceOperationType();
public abstract RestOperationTypeEnum getRestOperationType();
/**
* Returns the value of {@link #getResourceOperationType()} or {@link #getSystemOperationType()} or
* {@link #getOtherOperationType()}
* Determine which operation is being fired for a specific request
*
* @param theRequestDetails
* The request
*/
public String getResourceOrSystemOperationType() {
Enum<?> retVal = getResourceOperationType();
if (retVal != null) {
return retVal.name();
}
retVal = getSystemOperationType();
if (retVal != null) {
return retVal.name();
}
retVal = getOtherOperationType();
if (retVal != null) {
return retVal.name();
}
return null;
public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
return getRestOperationType();
}
public abstract RestfulOperationSystemEnum getSystemOperationType();
public abstract boolean incomingServerRequestMatchesMethod(RequestDetails theRequest);
public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
public abstract void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
protected Object invokeServerMethod(Object[] theMethodParams) {
protected final Object invokeServerMethod(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) {
// Handle server action interceptors
RestOperationTypeEnum operationType = getRestOperationType(theRequest);
if (operationType != null) {
for (IServerInterceptor next : theServer.getInterceptors()) {
ActionRequestDetails details = new ActionRequestDetails(theRequest);
populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams);
next.incomingRequestPreHandled(operationType, details);
}
}
// Actually invoke the method
try {
Method method = getMethod();
return method.invoke(getProvider(), theMethodParams);
@ -280,19 +280,24 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
/**
* Does this method have a parameter annotated with {@link ConditionalParamBinder}. Note that many operations don't
* actually support this paramter, so this will only return true occasionally.
* Does this method have a parameter annotated with {@link ConditionalParamBinder}. Note that many operations don't actually support this paramter, so this will only return true occasionally.
*/
public boolean isSupportsConditional() {
return mySupportsConditional;
}
/**
* Does this method support conditional operations over multiple objects (basically for conditional delete)
*/
public boolean isSupportsConditionalMultiple() {
return mySupportsConditionalMultiple;
}
protected byte[] loadRequestContents(RequestDetails theRequest) throws IOException {
/*
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
* servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
* in servers. Down the road it may make sense to just split the method binding classes into server and client
* versions, but this isn't actually a huge deal I don't think.
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on servlet-api in clients since there is no point. So we dynamically load a class
* that does the servlet processing in servers. Down the road it may make sense to just split the method binding classes into server and client versions, but this isn't actually a huge deal I
* don't think.
*/
IRequestReader reader = ourRequestReader;
if (reader == null) {
@ -317,9 +322,28 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
InputStream inputStream = reader.getInputStream(theRequest);
byte[] requestContents = IOUtils.toByteArray(inputStream);
theRequest.setRawRequest(requestContents);
return requestContents;
}
/**
* Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(RequestDetails, ActionRequestDetails, Object[])} to provide method specifics to the
* interceptors.
*
* @param theRequestDetails
* The server request details
* @param theDetails
* The details object to populate
* @param theMethodParams
* The method params as generated by the specific method binding
*/
protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) {
// TODO Auto-generated method stub
}
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
@ -342,7 +366,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
// TODO: handle if something other than OO comes back
BaseOperationOutcome operationOutcome = (BaseOperationOutcome) parser.parseResource(theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
ex = new UnprocessableEntityException(myContext, operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
@ -409,14 +433,16 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
if (theProvider instanceof IResourceProvider) {
returnTypeFromRp = ((IResourceProvider) theProvider).getResourceType();
if (!verifyIsValidResourceReturnType(returnTypeFromRp)) {
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned " + toLogString(returnTypeFromRp) + " - Must return a resource type");
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned "
+ toLogString(returnTypeFromRp) + " - Must return a resource type");
}
}
Class<?> returnTypeFromMethod = theMethod.getReturnType();
if (getTags != null) {
if (!TagList.class.equals(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @" + GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @"
+ GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
}
} else if (MethodOutcome.class.equals(returnTypeFromMethod)) {
// returns a method outcome
@ -429,13 +455,15 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
} else if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
if (!verifyIsValidResourceReturnType(returnTypeFromMethod) && !isResourceInterface(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns a collection with generic type " + toLogString(returnTypeFromMethod)
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns a collection with generic type " + toLogString(returnTypeFromMethod)
+ " - Must return a resource type or a collection (List, Set) with a resource type parameter (e.g. List<Patient> or List<IBaseResource> )");
}
} else {
if (!isResourceInterface(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type (eg Patient, "
+ Bundle.class.getSimpleName() + ", " + IBundleProvider.class.getSimpleName() + ", etc., see the documentation for more details)");
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type (eg Patient, " + Bundle.class.getSimpleName() + ", " + IBundleProvider.class.getSimpleName()
+ ", etc., see the documentation for more details)");
}
}
@ -465,12 +493,13 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
if (returnTypeFromRp != null) {
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName()
+ " (or a subclass of it) per IResourceProvider contract");
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
+ returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
}
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName() + " per method annotation - Must return "
+ returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
throw new ConfigurationException(
"Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName()
+ " per method annotation - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
}
returnType = returnTypeFromAnnotation;
} else {
@ -479,8 +508,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
} else {
if (!isResourceInterface(returnTypeFromAnnotation)) {
if (!verifyIsValidResourceReturnType(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromAnnotation)
+ " according to annotation - Must return a resource type");
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns " + toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
}
returnType = returnTypeFromAnnotation;
} else {
@ -595,7 +624,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
if (obj1 == null) {
obj1 = object;
} else {
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @"
+ obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
}
}

View File

@ -83,6 +83,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
b.append(response.getVersionId().getValue());
}
theResponse.addHeader(headerLocation, b.toString());
}
protected abstract void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams);
@ -140,6 +141,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
@Override
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
byte[] requestContents = loadRequestContents(theRequest);
// if (requestContainsResource()) {
// requestContents = parseIncomingServerResource(theRequest);
// } else {
@ -152,7 +154,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
HttpServletResponse servletResponse = theRequest.getServletResponse();
MethodOutcome response;
try {
response = (MethodOutcome) invokeServerMethod(params);
response = (MethodOutcome) invokeServerMethod(theServer, theRequest, params);
} catch (InternalErrorException e) {
ourLog.error("Internal error during method invocation", e);
EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
@ -182,7 +184,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
boolean allowPrefer = false;
switch (getResourceOperationType()) {
switch (getRestOperationType()) {
case CREATE:
if (response == null) {
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()

View File

@ -35,11 +35,12 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
private Integer myIdParamIndex;
private String myResourceName;
private int myResourceParameterIndex;
private int myResourceParameterIndex = -1;
private Class<? extends IBaseResource> myResourceType;
private Class<? extends IIdType> myIdParamType;
@ -86,6 +87,22 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
}
@Override
protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) {
super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams);
/*
* If the method has no parsed resource parameter, we parse here in order to have something for the interceptor.
*/
if (myResourceParameterIndex != -1) {
theDetails.setResource((IBaseResource) theMethodParams[myResourceParameterIndex]);
} else {
theDetails.setResource(ResourceParameter.parseResourceFromRequest(theRequestDetails, this, myResourceType));
}
}
@Override
protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
if (myIdParamIndex != null) {

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.Reader;
@ -46,21 +45,18 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -83,6 +79,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
set.add(Constants.PARAM_SORT_ASC);
set.add(Constants.PARAM_SORT_DESC);
set.add(Constants.PARAM_COUNT);
set.add(Constants.PARAM_SUMMARY);
set.add(Constants.PARAM_ELEMENTS);
ALLOWED_PARAMS = Collections.unmodifiableSet(set);
}
@ -233,7 +231,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
throw new IllegalStateException("Should not get here!");
}
public abstract Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
public abstract Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
@Override
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
@ -242,7 +240,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
// Narrative mode
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequest);
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
// Determine response encoding
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest());
@ -265,7 +263,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
Object resultObj = invokeServer(theRequest, params);
Object resultObj = invokeServer(theServer, theRequest, params);
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
boolean respondGzip = theRequest.isRespondGzip();
@ -331,8 +329,9 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
return;
}
}
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK, respondGzip,
theRequest.getFhirServerBase(), isAddContentLocationHeader());
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
isAddContentLocationHeader(), theRequest);
break;
} else {
Set<Include> includes = getRequestIncludesFromParams(params);
@ -355,7 +354,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
return;
}
}
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, theRequest.getFhirServerBase(), summaryMode, respondGzip, requestIsBrowser, theRequest);
} else {
IBaseResource resBundle = bundleFactory.getResourceBundle();
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
@ -366,8 +365,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
return;
}
}
RestfulServerUtils.streamResponseAsResource(theServer, response, resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode,
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), theRequest.getFhirServerBase(), isAddContentLocationHeader());
RestfulServerUtils.streamResponseAsResource(theServer, response, resBundle, prettyPrint, summaryMode,
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), isAddContentLocationHeader(), theRequest);
}
break;
@ -391,8 +390,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK, respondGzip,
theRequest.getFhirServerBase(), isAddContentLocationHeader());
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
isAddContentLocationHeader(), theRequest);
break;
}
}

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Method;
import java.util.Collection;
@ -31,18 +30,29 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class ConditionalParamBinder implements IParameter {
private RestfulOperationTypeEnum myOperationType;
private RestOperationTypeEnum myOperationType;
private boolean mySupportsMultiple;
ConditionalParamBinder(RestfulOperationTypeEnum theOperationType) {
ConditionalParamBinder(RestOperationTypeEnum theOperationType, boolean theSupportsMultiple) {
Validate.notNull(theOperationType, "theOperationType can not be null");
myOperationType = theOperationType;
mySupportsMultiple = theSupportsMultiple;
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// nothing
}
public boolean isSupportsMultiple() {
return mySupportsMultiple;
}
@Override
@ -53,7 +63,7 @@ class ConditionalParamBinder implements IParameter {
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
if (myOperationType == RestfulOperationTypeEnum.CREATE) {
if (myOperationType == RestOperationTypeEnum.CREATE) {
String retVal = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_EXIST);
if (isBlank(retVal)) {
return null;
@ -62,7 +72,7 @@ class ConditionalParamBinder implements IParameter {
retVal = retVal.substring(theRequest.getFhirServerBase().length());
}
return retVal;
} else if (myOperationType != RestfulOperationTypeEnum.DELETE && myOperationType != RestfulOperationTypeEnum.UPDATE) {
} else if (myOperationType != RestOperationTypeEnum.DELETE && myOperationType != RestOperationTypeEnum.UPDATE) {
return null;
}
@ -84,9 +94,4 @@ class ConditionalParamBinder implements IParameter {
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// nothing
}
}

View File

@ -27,11 +27,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -72,8 +72,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
IBaseResource conf = (IBaseResource) invokeServerMethod(theMethodParams);
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
IBaseResource conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
return new SimpleBundleProvider(conf);
}
@ -91,18 +91,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return null;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
@Override
public OtherOperationTypeEnum getOtherOperationType() {
return OtherOperationTypeEnum.METADATA;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.METADATA;
}
@Override

View File

@ -26,10 +26,9 @@ import java.util.Set;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
@ -39,13 +38,8 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.CREATE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.CREATE;
}
@Override

View File

@ -32,13 +32,12 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -87,13 +86,8 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.DELETE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.DELETE;
}
@Override

View File

@ -24,6 +24,7 @@ import java.lang.reflect.Method;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.DeleteTags;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
public class DeleteTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
@ -37,8 +38,8 @@ public class DeleteTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
}
@Override
public OtherOperationTypeEnum getOtherOperationType() {
return OtherOperationTypeEnum.DELETE_TAGS;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.DELETE_TAGS;
}
}

View File

@ -30,14 +30,14 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -86,23 +86,18 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
Object response = invokeServerMethod(theMethodParams);
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
return toResourceList(response);
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.SEARCH_TYPE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.SEARCH_TYPE;
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchMethodBinding.class);

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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 static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.param.CollectionBinder;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class ElementsParameter implements IParameter {
@SuppressWarnings("rawtypes")
private Class<? extends Collection> myInnerCollectionType;
@SuppressWarnings("unchecked")
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
if (theSourceClientArgument instanceof Collection) {
StringBuilder values = new StringBuilder();
for (String next : (Collection<String>) theSourceClientArgument) {
if (isNotBlank(next)) {
if (values.length() > 0) {
values.append(',');
}
values.append(next);
}
}
theTargetQueryArguments.put(Constants.PARAM_ELEMENTS, Collections.singletonList(values.toString()));
} else {
String elements = (String) theSourceClientArgument;
if (elements != null) {
theTargetQueryArguments.put(Constants.PARAM_ELEMENTS, Collections.singletonList(elements));
}
}
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
Set<String> value = getElementsValueOrNull(theRequest);
if (value == null || value.isEmpty()) {
return null;
}
if (myInnerCollectionType == null) {
return StringUtils.join(value, ',');
}
try {
Collection retVal = myInnerCollectionType.newInstance();
retVal.addAll(value);
return retVal;
} catch (InstantiationException e) {
throw new InternalErrorException("Failed to instantiate " + myInnerCollectionType, e);
} catch (IllegalAccessException e) {
throw new InternalErrorException("Failed to instantiate " + myInnerCollectionType, e);
}
}
public static Set<String> getElementsValueOrNull(RequestDetails theRequest) {
String[] summary = theRequest.getParameters().get(Constants.PARAM_ELEMENTS);
if (summary != null && summary.length > 0) {
Set<String> retVal = new HashSet<String>();
for (String next : summary) {
StringTokenizer tok = new StringTokenizer(next, ",");
while (tok.hasMoreTokens()) {
String token = tok.nextToken();
if (isNotBlank(token)) {
retVal.add(token);
}
}
}
if (retVal.isEmpty()) {
return null;
}
return retVal;
} else {
return null;
}
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is of type " + SummaryEnum.class + " but can not be a collection of collections");
}
if (theInnerCollectionType != null) {
myInnerCollectionType = CollectionBinder.getInstantiableCollectionType(theInnerCollectionType, SummaryEnum.class.getSimpleName());
}
}
}

View File

@ -36,13 +36,12 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -55,10 +54,10 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
private Class<? extends IBaseResource> myType;
private Integer myIdParamIndex;
private Integer myVersionIdParamIndex;
private String myResourceName;
private Class<? extends IBaseResource> myType;
private Integer myVersionIdParamIndex;
public GetTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, GetTags theAnnotation) {
super(theMethod, theConetxt, theProvider);
@ -68,7 +67,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
} else {
myType = theAnnotation.type();
}
if (!Modifier.isInterface(myType.getModifiers())) {
myResourceName = theConetxt.getResourceDefinition(myType).getName();
}
@ -77,35 +76,44 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
myVersionIdParamIndex = MethodUtil.findVersionIdParameterIndex(theMethod);
if (myIdParamIndex != null && myType.equals(IResource.class)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not specify a resource type, but has an @" + IdParam.class.getSimpleName() + " parameter. Please specity a resource type in the @" + GetTags.class.getSimpleName() + " annotation");
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not specify a resource type, but has an @" + IdParam.class.getSimpleName()
+ " parameter. Please specity a resource type in the @" + GetTags.class.getSimpleName() + " annotation");
}
}
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) {
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
TagList retVal = parser.parseTagList(theResponseReader);
return retVal;
} else {
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
}
@Override
public String getResourceName() {
return myResourceName;
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return null;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.GET_TAGS;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (theRequest.getRequestType() != RequestTypeEnum.GET) {
return false;
}
if (!Constants.PARAM_TAGS.equals(theRequest.getOperation())) {
return false;
}
if (myResourceName == null) {
if (getResourceName() != null) {
return false;
}
} else if (!myResourceName.equals(theRequest.getResourceName())) {
return false;
}
if ((myIdParamIndex != null) != (theRequest.getId() != null)) {
return false;
}
// if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
// return false;
// }
return true;
}
@Override
@ -125,7 +133,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if (id != null) {
if (versionId != null) {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
} else if (id.hasVersionIdPart()){
} else if (id.hasVersionIdPart()) {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, id.getVersionIdPart(), Constants.PARAM_TAGS);
} else {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_TAGS);
@ -147,6 +155,18 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
return retVal;
}
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) {
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
TagList retVal = parser.parseTagList(theResponseReader);
return retVal;
} else {
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
}
@Override
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
Object[] params = createParametersForServerRequest(theRequest, null);
@ -158,7 +178,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
params[myVersionIdParamIndex] = theRequest.getId();
}
TagList resp = (TagList) invokeServerMethod(params);
TagList resp = (TagList) invokeServerMethod(theServer, theRequest, params);
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
@ -167,13 +187,13 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
return;
}
}
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
HttpServletResponse response = theRequest.getServletResponse();
response.setContentType(responseEncoding.getResourceContentType());
response.setStatus(Constants.STATUS_HTTP_200_OK);
response.setCharacterEncoding(Constants.CHARSETNAME_UTF_8);
response.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
theServer.addHeadersToResponse(response);
@ -187,34 +207,4 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
}
}
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (theRequest.getRequestType()!=RequestTypeEnum.GET) {
return false;
}
if (!Constants.PARAM_TAGS.equals(theRequest.getOperation())) {
return false;
}
if (myResourceName == null) {
if (getResourceName() != null) {
return false;
}
} else if (!myResourceName.equals(theRequest.getResourceName())) {
return false;
}
if ((myIdParamIndex != null) != (theRequest.getId() != null)) {
return false;
}
// if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
// return false;
// }
return true;
}
@Override
public OtherOperationTypeEnum getOtherOperationType() {
return OtherOperationTypeEnum.GET_TAGS;
}
}

View File

@ -19,30 +19,31 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -50,8 +51,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
private final Integer myIdParamIndex;
private String myResourceName;
private final RestfulOperationTypeEnum myResourceOperationType;
private final RestfulOperationSystemEnum mySystemOperationType;
private final RestOperationTypeEnum myResourceOperationType;
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
@ -64,22 +64,19 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
if (theProvider instanceof IResourceProvider) {
type = ((IResourceProvider) theProvider).getResourceType();
if (myIdParamIndex != null) {
myResourceOperationType = RestfulOperationTypeEnum.HISTORY_INSTANCE;
myResourceOperationType = RestOperationTypeEnum.HISTORY_INSTANCE;
} else {
myResourceOperationType = RestfulOperationTypeEnum.HISTORY_TYPE;
myResourceOperationType = RestOperationTypeEnum.HISTORY_TYPE;
}
mySystemOperationType = null;
} else {
myResourceOperationType = null;
mySystemOperationType = RestfulOperationSystemEnum.HISTORY_SYSTEM;
myResourceOperationType = RestOperationTypeEnum.HISTORY_SYSTEM;
}
} else {
if (myIdParamIndex != null) {
myResourceOperationType = RestfulOperationTypeEnum.HISTORY_INSTANCE;
myResourceOperationType = RestOperationTypeEnum.HISTORY_INSTANCE;
} else {
myResourceOperationType = RestfulOperationTypeEnum.HISTORY_TYPE;
myResourceOperationType = RestOperationTypeEnum.HISTORY_TYPE;
}
mySystemOperationType = null;
}
if (type != IResource.class) {
@ -91,7 +88,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
public RestOperationTypeEnum getRestOperationType() {
return myResourceOperationType;
}
@ -105,22 +102,16 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
return ReturnTypeEnum.BUNDLE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return mySystemOperationType;
}
// ObjectUtils.equals is replaced by a JDK7 method..
@SuppressWarnings("deprecation")
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (!Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
return false;
}
if (theRequest.getResourceName() == null) {
return mySystemOperationType == RestfulOperationSystemEnum.HISTORY_SYSTEM;
return myResourceOperationType == RestOperationTypeEnum.HISTORY_SYSTEM;
}
if (!ObjectUtils.equals(theRequest.getResourceName(), myResourceName)) {
if (!StringUtils.equals(theRequest.getResourceName(), myResourceName)) {
return false;
}
@ -131,7 +122,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
if (theRequest.getId() == null) {
return myResourceOperationType == RestfulOperationTypeEnum.HISTORY_TYPE;
return myResourceOperationType == RestOperationTypeEnum.HISTORY_TYPE;
} else if (theRequest.getId().hasVersionIdPart()) {
return false;
}
@ -164,12 +155,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
Object response = invokeServerMethod(theMethodParams);
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
final IBundleProvider resources = toResourceList(response);

View File

@ -34,6 +34,7 @@ import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -71,23 +72,26 @@ class IncludeParameter extends BaseQueryParameter {
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
if (myInstantiableCollectionType == null) {
if (mySpecType == Include.class) {
retVal.add(QualifiedParamList.singleton(((Include) theObject).getValue()));
} else if (mySpecType == PathSpecification.class) {
retVal.add(QualifiedParamList.singleton(((PathSpecification) theObject).getValue()));
if (mySpecType == Include.class || mySpecType == PathSpecification.class) {
convertAndAddIncludeToList(retVal, (Include) theObject);
} else {
retVal.add(QualifiedParamList.singleton(((String) theObject)));
}
} else {
Collection<Include> val = (Collection<Include>) theObject;
for (Include pathSpec : val) {
retVal.add(QualifiedParamList.singleton(pathSpec.getValue()));
for (Include include : val) {
convertAndAddIncludeToList(retVal, include);
}
}
return retVal;
}
private void convertAndAddIncludeToList(ArrayList<QualifiedParamList> retVal, Include include) {
String qualifier = include.isRecurse() ? Constants.PARAM_INCLUDE_QUALIFIER_RECURSE : null;
retVal.add(QualifiedParamList.singleton(qualifier, include.getValue()));
}
public Set<String> getAllow() {
return myAllow;
}
@ -115,6 +119,7 @@ class IncludeParameter extends BaseQueryParameter {
@Override
public Object parse(FhirContext theContext, List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
Collection<Include> retValCollection = null;
if (myInstantiableCollectionType != null) {
try {
retValCollection = myInstantiableCollectionType.newInstance();
@ -123,7 +128,7 @@ class IncludeParameter extends BaseQueryParameter {
}
}
for (List<String> nextParamList : theString) {
for (QualifiedParamList nextParamList : theString) {
if (nextParamList.isEmpty()) {
continue;
}
@ -131,6 +136,8 @@ class IncludeParameter extends BaseQueryParameter {
throw new InvalidRequestException(theContext.getLocalizer().getMessage(IncludeParameter.class, "orIncludeInRequest"));
}
boolean recurse = Constants.PARAM_INCLUDE_QUALIFIER_RECURSE.equals(nextParamList.getQualifier());
String value = nextParamList.get(0);
if (myAllow != null && !myAllow.isEmpty()) {
if (!myAllow.contains(value)) {
@ -140,20 +147,14 @@ class IncludeParameter extends BaseQueryParameter {
}
}
}
if (retValCollection == null) {
if (myInstantiableCollectionType == null) {
if (mySpecType == String.class) {
return value;
} else if (mySpecType == PathSpecification.class) {
return new PathSpecification(value);
} else {
return new Include(value);
return new Include(value, recurse);
}
} else {
if (mySpecType == PathSpecification.class) {
retValCollection.add(new PathSpecification(value));
} else {
retValCollection.add(new Include(value));
}
retValCollection.add(new Include(value, recurse));
}
}

View File

@ -45,13 +45,13 @@ import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Elements;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.Operation;
@ -68,6 +68,8 @@ import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.OperationParameter.IConverter;
@ -84,7 +86,6 @@ import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.util.ReflectionUtil;
@ -348,7 +349,7 @@ public class MethodUtil {
}
@SuppressWarnings("unchecked")
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider, RestfulOperationTypeEnum theRestfulOperationTypeEnum) {
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) {
List<IParameter> parameters = new ArrayList<IParameter>();
Class<?>[] parameterTypes = theMethod.getParameterTypes();
@ -385,10 +386,10 @@ public class MethodUtil {
}
if (parameterType.equals(HttpServletRequest.class) || parameterType.equals(ServletRequest.class)) {
param = new ServletRequestParameter();
} else if (parameterType.equals(SummaryEnum.class)) {
param = new SummaryEnumParameter();
} else if (parameterType.equals(HttpServletResponse.class) || parameterType.equals(ServletResponse.class)) {
param = new ServletResponseParameter();
} else if (parameterType.equals(NarrativeModeEnum.class)) {
param = new NarrativeModeParameter();
} else {
for (int i = 0; i < annotations.length && param == null; i++) {
Annotation nextAnnotation = annotations[i];
@ -444,6 +445,8 @@ public class MethodUtil {
param = new NullParameter();
} else if (nextAnnotation instanceof ServerBase) {
param = new ServerBaseParamBinder();
} else if (nextAnnotation instanceof Elements) {
param = new ElementsParameter();
} else if (nextAnnotation instanceof Since) {
param = new SinceParameter();
} else if (nextAnnotation instanceof Count) {
@ -453,7 +456,7 @@ public class MethodUtil {
} else if (nextAnnotation instanceof TransactionParam) {
param = new TransactionParameter(theContext);
} else if (nextAnnotation instanceof ConditionalUrlParam) {
param = new ConditionalParamBinder(theRestfulOperationTypeEnum);
param = new ConditionalParamBinder(theRestfulOperationTypeEnum, ((ConditionalUrlParam)nextAnnotation).supportsMultiple());
} else if (nextAnnotation instanceof OperationParam) {
Operation op = theMethod.getAnnotation(Operation.class);
param = new OperationParameter(theContext, op.name(), ((OperationParam) nextAnnotation));

View File

@ -1,68 +0,0 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class NarrativeModeParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource)
throws InternalErrorException {
if (theSourceClientArgument != null) {
NarrativeModeEnum n = (NarrativeModeEnum) theSourceClientArgument;
theTargetQueryArguments.put(Constants.PARAM_NARRATIVE, Collections.singletonList(n.name().toLowerCase()));
}
}
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
String val = theRequest.getServletRequest().getParameter(Constants.PARAM_NARRATIVE);
if (val != null) {
try {
return NarrativeModeEnum.valueOfCaseInsensitive(val);
} catch (IllegalArgumentException e) {
ourLog.debug("Invalid {} parameger: {}", Constants.PARAM_NARRATIVE, val);
return null;
}
}
return null;
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// ignore
}
}

View File

@ -19,8 +19,8 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@ -41,15 +41,15 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
@ -64,7 +64,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
private final boolean myIdempotent;
private final Integer myIdParamIndex;
private final String myName;
private final OtherOperationTypeEnum myOtherOperatiopnType;
private final RestOperationTypeEnum myOtherOperatiopnType;
private List<ReturnType> myReturnParams;
private final ReturnTypeEnum myReturnType;
@ -118,11 +118,11 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
if (getResourceName() == null) {
myOtherOperatiopnType = OtherOperationTypeEnum.EXTENDED_OPERATION_SERVER;
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER;
} else if (myIdParamIndex == null) {
myOtherOperatiopnType = OtherOperationTypeEnum.EXTENDED_OPERATION_TYPE;
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
} else {
myOtherOperatiopnType = OtherOperationTypeEnum.EXTENDED_OPERATION_INSTANCE;
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE;
}
myReturnParams = new ArrayList<OperationMethodBinding.ReturnType>();
@ -167,15 +167,10 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public OtherOperationTypeEnum getOtherOperationType() {
public RestOperationTypeEnum getRestOperationType() {
return myOtherOperatiopnType;
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return null;
}
@Override
protected BundleTypeEnum getResponseBundleType() {
return null;
@ -190,11 +185,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
return myReturnType;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (getResourceName() == null) {
@ -233,7 +223,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
// always ok
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
@ -255,7 +245,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
Object response = invokeServerMethod(theMethodParams);
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
IBundleProvider retVal = toResourceList(response);
return retVal;
}

View File

@ -28,14 +28,17 @@ import java.util.List;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IAccessor;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.IRuntimeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeChildPrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -48,6 +51,7 @@ import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ParametersUtil;
public class OperationParameter implements IParameter {
@ -172,7 +176,7 @@ public class OperationParameter implements IParameter {
}
Class<? extends IBaseResource> wantedResourceType = theMethodBinding.getContext().getResourceDefinition("Parameters").getImplementingClass();
IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, theRequestContents, theMethodBinding, wantedResourceType);
IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, theMethodBinding, wantedResourceType);
RuntimeResourceDefinition def = ctx.getResourceDefinition(requestContents);
@ -229,6 +233,7 @@ public class OperationParameter implements IParameter {
}
}
@SuppressWarnings("unchecked")
private void tryToAddValues(List<IBase> theParamValues, List<Object> theMatchingParamValues) {
for (Object nextValue : theParamValues) {
if (nextValue == null) {
@ -238,12 +243,30 @@ public class OperationParameter implements IParameter {
nextValue = myConverter.incomingServer(nextValue);
}
if (!myParameterType.isAssignableFrom(nextValue.getClass())) {
throw new InvalidRequestException("Request has parameter " + myName + " of type " + nextValue.getClass().getSimpleName() + " but method expects type " + myParameterType.getSimpleName());
Class<? extends IBaseDatatype> sourceType = (Class<? extends IBaseDatatype>) nextValue.getClass();
Class<? extends IBaseDatatype> targetType = (Class<? extends IBaseDatatype>) myParameterType;
BaseRuntimeElementDefinition<?> sourceTypeDef = myContext.getElementDefinition(sourceType);
BaseRuntimeElementDefinition<?> targetTypeDef = myContext.getElementDefinition(targetType);
if (targetTypeDef instanceof IRuntimeDatatypeDefinition && sourceTypeDef instanceof IRuntimeDatatypeDefinition) {
IRuntimeDatatypeDefinition targetTypeDtDef = (IRuntimeDatatypeDefinition) targetTypeDef;
if (targetTypeDtDef.isProfileOf(sourceType)) {
FhirTerser terser = myContext.newTerser();
IBase newTarget = targetTypeDef.newInstance();
terser.cloneInto((IBase) nextValue, newTarget, true);
theMatchingParamValues.add(newTarget);
continue;
}
}
throwWrongParamType(nextValue);
}
theMatchingParamValues.add(nextValue);
}
}
private void throwWrongParamType(Object nextValue) {
throw new InvalidRequestException("Request has parameter " + myName + " of type " + nextValue.getClass().getSimpleName() + " but method expects type " + myParameterType.getSimpleName());
}
public interface IConverter {
Object incomingServer(Object theObject);

View File

@ -1,60 +0,0 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
public enum OtherOperationTypeEnum {
METADATA("metadata"),
ADD_TAGS("add-tags"),
DELETE_TAGS("delete-tags"),
GET_TAGS("get-tags"),
GET_PAGE("get-page"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_SERVER("extended-operation-server"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_TYPE("extended-operation-type"),
/**
* E.g. $everything, $validate, etc.
*/
EXTENDED_OPERATION_INSTANCE("extended-operation-instance");
private String myCode;
OtherOperationTypeEnum(String theName) {
myCode=theName;
}
public String getCode() {
return myCode;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
* HAPI FHIR - Core Library
@ -29,21 +31,21 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
public class QualifiedParamList extends ArrayList<String> {
private static final long serialVersionUID = 1L;
private String myQualifier;
public QualifiedParamList() {
super();
}
public QualifiedParamList(int theCapacity) {
super(theCapacity);
}
public QualifiedParamList(IQueryParameterOr<?> theNextOr) {
for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) {
if (myQualifier==null) {
myQualifier=next.getQueryParameterQualifier();
if (myQualifier == null) {
myQualifier = next.getQueryParameterQualifier();
}
add(next.getValueAsQueryToken());
}
@ -60,36 +62,68 @@ public class QualifiedParamList extends ArrayList<String> {
public static QualifiedParamList singleton(String theParamValue) {
return singleton(null, theParamValue);
}
public static QualifiedParamList singleton(String theQualifier, String theParamValue) {
QualifiedParamList retVal = new QualifiedParamList(1);
retVal.setQualifier(theQualifier);
retVal.add(theParamValue);
return retVal;
}
public static QualifiedParamList splitQueryStringByCommasIgnoreEscape(String theQualifier, String theParams){
QualifiedParamList retVal = new QualifiedParamList();
retVal.setQualifier(theQualifier);
StringTokenizer tok = new StringTokenizer(theParams,",");
String prev=null;
public static QualifiedParamList splitQueryStringByCommasIgnoreEscape(String theQualifier, String theParams) {
QualifiedParamList retVal = new QualifiedParamList();
retVal.setQualifier(theQualifier);
StringTokenizer tok = new StringTokenizer(theParams, ",", true);
String prev = null;
while (tok.hasMoreElements()) {
String str = tok.nextToken();
if (prev!=null&&prev.endsWith("\\")) {
int idx = retVal.size()-1;
String existing = retVal.get(idx);
retVal.set(idx, existing.substring(0, existing.length()-1) + "," + str);
}else {
retVal.add(str);
if (isBlank(str)) {
prev = null;
continue;
}
prev=str;
if (str.equals(",")) {
if (countTrailingSlashes(prev) % 2 == 1) {
int idx = retVal.size() - 1;
String existing = retVal.get(idx);
prev = existing.substring(0, existing.length() - 1) + ',';
retVal.set(idx, prev);
} else {
prev = null;
}
continue;
}
if (prev != null && prev.length() > 0 && prev.charAt(prev.length() - 1) == ',') {
int idx = retVal.size() - 1;
String existing = retVal.get(idx);
prev = existing + str;
retVal.set(idx, prev);
} else {
retVal.add(str);
prev = str;
}
}
return retVal;
}
private static int countTrailingSlashes(String theString) {
if(theString==null) {
return 0;
}
int retVal = 0;
for (int i = theString.length() - 1; i >= 0; i--) {
char nextChar = theString.charAt(i);
if (nextChar != '\\') {
break;
} else {
retVal++;
}
}
return retVal;
}
}

View File

@ -39,16 +39,17 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Elements;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -92,16 +93,26 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
}
@Override
public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
if (mySupportsVersion && theRequestDetails.getId().hasVersionIdPart()) {
return RestOperationTypeEnum.VREAD;
} else {
return RestOperationTypeEnum.READ;
}
}
@Override
public List<Class<?>> getAllowableParamAnnotations() {
ArrayList<Class<?>> retVal = new ArrayList<Class<?>>();
retVal.add(IdParam.class);
retVal.add(Elements.class);
return retVal;
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return isVread() ? RestfulOperationTypeEnum.VREAD : RestfulOperationTypeEnum.READ;
public RestOperationTypeEnum getRestOperationType() {
return isVread() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
}
@Override
@ -109,11 +120,6 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
return ReturnTypeEnum.RESOURCE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (!theRequest.getResourceName().equals(getResourceName())) {
@ -198,13 +204,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
theMethodParams[myIdIndex] = MethodUtil.convertIdToType(theRequest.getId(), myIdParameterType);
if (myVersionIdIndex != null) {
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
}
Object response = invokeServerMethod(theMethodParams);
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
IBundleProvider retVal = toResourceList(response);
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {

View File

@ -30,10 +30,9 @@ import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
public class RequestDetails {
@ -43,18 +42,17 @@ public class RequestDetails {
private String myFhirServerBase;
private IdDt myId;
private String myOperation;
private OtherOperationTypeEnum myOtherOperationType;
private Map<String, String[]> myParameters;
private byte[] myRawRequest;
private String myRequestPath;
private RequestTypeEnum myRequestType;
private String myResourceName;
private RestfulOperationTypeEnum myResourceOperationType;
private boolean myRespondGzip;
private RestOperationTypeEnum myRestOperationType;
private String mySecondaryOperation;
private RestfulServer myServer;
private HttpServletRequest myServletRequest;
private HttpServletResponse myServletResponse;
private RestfulOperationSystemEnum mySystemOperationType;
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
public String getCompartmentName() {
@ -77,14 +75,14 @@ public class RequestDetails {
return myOperation;
}
public OtherOperationTypeEnum getOtherOperationType() {
return myOtherOperationType;
}
public Map<String, String[]> getParameters() {
return myParameters;
}
public byte[] getRawRequest() {
return myRawRequest;
}
/**
* The part of the request URL that comes after the server base.
* <p>
@ -103,8 +101,8 @@ public class RequestDetails {
return myResourceName;
}
public RestfulOperationTypeEnum getResourceOperationType() {
return myResourceOperationType;
public RestOperationTypeEnum getRestOperationType() {
return myRestOperationType;
}
public String getSecondaryOperation() {
@ -123,10 +121,6 @@ public class RequestDetails {
return myServletResponse;
}
public RestfulOperationSystemEnum getSystemOperationType() {
return mySystemOperationType;
}
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
return myUnqualifiedToQualifiedNames;
}
@ -155,10 +149,6 @@ public class RequestDetails {
myOperation = theOperation;
}
public void setOtherOperationType(OtherOperationTypeEnum theOtherOperationType) {
myOtherOperationType = theOtherOperationType;
}
public void setParameters(Map<String, String[]> theParams) {
myParameters = theParams;
@ -187,6 +177,10 @@ public class RequestDetails {
}
public void setRawRequest(byte[] theRawRequest) {
myRawRequest = theRawRequest;
}
public void setRequestPath(String theRequestPath) {
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
myRequestPath = theRequestPath;
@ -200,14 +194,14 @@ public class RequestDetails {
myResourceName = theResourceName;
}
public void setResourceOperationType(RestfulOperationTypeEnum theResourceOperationType) {
myResourceOperationType = theResourceOperationType;
}
public void setRespondGzip(boolean theRespondGzip) {
myRespondGzip = theRespondGzip;
}
public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
myRestOperationType = theRestOperationType;
}
public void setSecondaryOperation(String theSecondaryOperation) {
mySecondaryOperation = theSecondaryOperation;
}
@ -224,10 +218,6 @@ public class RequestDetails {
myServletResponse = theServletResponse;
}
public void setSystemOperationType(RestfulOperationSystemEnum theSystemOperationType) {
mySystemOperationType = theSystemOperationType;
}
public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set<String> theParamNames) {
RequestDetails retVal = new RequestDetails();
retVal.setResourceName(theResourceName);

View File

@ -19,8 +19,8 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -39,16 +39,16 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -128,8 +128,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.SEARCH_TYPE;
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.SEARCH_TYPE;
}
@Override
@ -142,11 +142,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return ReturnTypeEnum.BUNDLE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if (!theRequest.getResourceName().equals(getResourceName())) {
@ -192,11 +187,14 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
if (qualifiers.passes(temp.getQualifierWhitelist(), temp.getQualifierBlacklist())) {
methodParamsTemp.add(name);
}
} else if (unqualifiedNames.contains(name)) {
}
if (unqualifiedNames.contains(name)) {
List<String> qualifiedNames = theRequest.getUnqualifiedToQualifiedNames().get(name);
qualifiedNames = processWhitelistAndBlacklist(qualifiedNames, temp.getQualifierWhitelist(), temp.getQualifierBlacklist());
methodParamsTemp.addAll(qualifiedNames);
} else {
}
if (!qualifiedParamNames.contains(name) && !unqualifiedNames.contains(name))
{
ourLog.trace("Method {} doesn't match param '{}' is not present", getMethod().getName(), name);
return false;
}
@ -207,11 +205,13 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
if (qualifiers.passes(temp.getQualifierWhitelist(), temp.getQualifierBlacklist())) {
methodParamsTemp.add(name);
}
} else if (unqualifiedNames.contains(name)) {
}
if (unqualifiedNames.contains(name)) {
List<String> qualifiedNames = theRequest.getUnqualifiedToQualifiedNames().get(name);
qualifiedNames = processWhitelistAndBlacklist(qualifiedNames, temp.getQualifierWhitelist(), temp.getQualifierBlacklist());
methodParamsTemp.addAll(qualifiedNames);
} else {
}
if (!qualifiedParamNames.contains(name) && !qualifiedParamNames.contains(name)) {
methodParamsTemp.add(name);
}
}
@ -283,12 +283,12 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
Object response = invokeServerMethod(theMethodParams);
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
return toResourceList(response);

Some files were not shown because too many files have changed in this diff Show More