diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java
index a3192a190b5..6b92adc15c5 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java
@@ -34,7 +34,6 @@ import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource;
-import ca.uhn.fhir.model.dstu.valueset.FHIRDefinedTypeEnum;
import ca.uhn.fhir.model.view.ViewGenerator;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.DataFormatException;
@@ -97,7 +96,7 @@ public class FhirContext {
if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) {
myVersion = FhirVersionEnum.DSTU1.getVersionImplementation();
} else {
- throw new IllegalStateException("Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI version 0.7, a separate FHIR strcture JAR must be added to your classpath or project pom.xml");
+ throw new IllegalStateException("Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath or project pom.xml");
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java
index eeb9b1204f4..d5a3f5f2591 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java
@@ -2,7 +2,6 @@ package ca.uhn.fhir.context;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
-import ca.uhn.fhir.validation.FhirValidator;
public enum FhirVersionEnum {
diff --git a/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dstu/FhirDev.java b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dstu/FhirDev.java
new file mode 100644
index 00000000000..51856c58c5d
--- /dev/null
+++ b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dstu/FhirDev.java
@@ -0,0 +1,44 @@
+package ca.uhn.fhir.model.dstu;
+
+import org.apache.commons.lang3.StringUtils;
+
+import ca.uhn.fhir.context.RuntimeResourceDefinition;
+import ca.uhn.fhir.model.api.IFhirVersion;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.dev.resource.Profile;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
+import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
+
+public class FhirDev implements IFhirVersion {
+
+ private String myId;
+
+ @Override
+ public Object createServerConformanceProvider(RestfulServer theServer) {
+ return new ServerConformanceProvider(theServer);
+ }
+
+ @Override
+ public IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition) {
+ Profile retVal = new Profile();
+
+ RuntimeResourceDefinition def = theRuntimeResourceDefinition;
+
+ myId = def.getId();
+ if (StringUtils.isBlank(myId)) {
+ myId = theRuntimeResourceDefinition.getName().toLowerCase();
+ }
+
+ retVal.setId(new IdDt(myId));
+ return retVal;
+ }
+
+ @Override
+ public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) {
+ return new ServerProfileProvider(theRestfulServer.getFhirContext());
+ }
+
+}
diff --git a/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
new file mode 100644
index 00000000000..16800bdf8b9
--- /dev/null
+++ b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
@@ -0,0 +1,359 @@
+package ca.uhn.fhir.rest.server.provider;
+
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 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.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.lang3.StringUtils;
+
+import ca.uhn.fhir.context.RuntimeResourceDefinition;
+import ca.uhn.fhir.context.RuntimeSearchParam;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.dev.resource.Conformance;
+import ca.uhn.fhir.model.dev.resource.OperationDefinition;
+import ca.uhn.fhir.model.dev.resource.Conformance.Rest;
+import ca.uhn.fhir.model.dev.resource.Conformance.RestOperation;
+import ca.uhn.fhir.model.dev.resource.Conformance.RestResource;
+import ca.uhn.fhir.model.dev.resource.Conformance.RestResourceSearchParam;
+import ca.uhn.fhir.model.dev.resource.OperationDefinition.Parameter;
+import ca.uhn.fhir.model.dev.valueset.RestfulConformanceModeEnum;
+import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
+import ca.uhn.fhir.model.primitive.BooleanDt;
+import ca.uhn.fhir.model.primitive.CodeDt;
+import ca.uhn.fhir.model.primitive.DateTimeDt;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.model.primitive.StringDt;
+import ca.uhn.fhir.rest.annotation.Metadata;
+import ca.uhn.fhir.rest.method.BaseMethodBinding;
+import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
+import ca.uhn.fhir.rest.method.IParameter;
+import ca.uhn.fhir.rest.method.SearchMethodBinding;
+import ca.uhn.fhir.rest.method.SearchParameter;
+import ca.uhn.fhir.rest.server.Constants;
+import ca.uhn.fhir.rest.server.ResourceBinding;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.util.ExtensionConstants;
+
+/**
+ * Server FHIR Provider which serves the conformance statement for a RESTful server implementation
+ *
+ *
+ * Note: This class is safe to extend, but it is important to note that the same instance of {@link Conformance} is
+ * always returned unless {@link #setCache(boolean)} is called with a value of false
. This means that if
+ * you are adding anything to the returned conformance instance on each call you should call
+ * setCache(false)
in your provider constructor.
+ *
+ */
+public class ServerConformanceProvider {
+
+ private boolean myCache = true;
+ private volatile Conformance myConformance;
+ private String myPublisher = "Not provided";
+ private final RestfulServer myRestfulServer;
+
+ public ServerConformanceProvider(RestfulServer theRestfulServer) {
+ myRestfulServer = theRestfulServer;
+ }
+
+ /**
+ * Gets the value of the "publisher" that will be placed in the generated conformance statement. As this
+ * is a mandatory element, the value should not be null (although this is not enforced). The value defaults
+ * to "Not provided" but may be set to null, which will cause this element to be omitted.
+ */
+ public String getPublisher() {
+ return myPublisher;
+ }
+
+ /**
+ * Actually create and return the conformance statement
+ *
+ * See the class documentation for an important note if you are extending this class
+ */
+ @Metadata
+ public Conformance getServerConformance() {
+ if (myConformance != null && myCache) {
+ return myConformance;
+ }
+
+ Conformance retVal = new Conformance();
+
+ retVal.setPublisher(myPublisher);
+ retVal.setDate(DateTimeDt.withCurrentTime());
+ retVal.setFhirVersion("0.80"); // TODO: pull from model
+ retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
+
+ retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
+ retVal.getSoftware().setName(myRestfulServer.getServerName());
+ retVal.getSoftware().setVersion(myRestfulServer.getServerVersion());
+ retVal.addFormat(Constants.CT_FHIR_XML);
+ retVal.addFormat(Constants.CT_FHIR_JSON);
+
+ Rest rest = retVal.addRest();
+ rest.setMode(RestfulConformanceModeEnum.SERVER);
+
+// Set systemOps = new HashSet();
+//
+// List bindings = new ArrayList(myRestfulServer.getResourceBindings());
+// Collections.sort(bindings, new Comparator() {
+// @Override
+// public int compare(ResourceBinding theArg0, ResourceBinding theArg1) {
+// return theArg0.getResourceName().compareToIgnoreCase(theArg1.getResourceName());
+// }
+// });
+//
+// for (ResourceBinding next : bindings) {
+//
+// Set resourceOps = new HashSet();
+// RestResource resource = rest.addResource();
+//
+// String resourceName = next.getResourceName();
+// RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
+// resource.getType().setValue(def.getName());
+// resource.getProfile().setReference(new IdDt(def.getResourceProfile()));
+//
+// TreeSet includes = new TreeSet();
+//
+// // Map nameToSearchParam = new HashMap();
+// for (BaseMethodBinding> nextMethodBinding : next.getMethodBindings()) {
+// RestfulOperationTypeEnum resOp = nextMethodBinding.getResourceOperationType();
+// if (resOp != null) {
+// if (resourceOps.contains(resOp) == false) {
+// resourceOps.add(resOp);
+// resource.addOperation().setCode(resOp);
+// }
+// }
+//
+// RestfulOperationSystemEnum sysOp = nextMethodBinding.getSystemOperationType();
+// if (sysOp != null) {
+// if (systemOps.contains(sysOp) == false) {
+// systemOps.add(sysOp);
+// rest.addOperation().setCode(sysOp);
+// }
+// }
+//
+// if (nextMethodBinding instanceof SearchMethodBinding) {
+// handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
+// } else if (nextMethodBinding instanceof DynamicSearchMethodBinding) {
+// handleDynamicSearchMethodBinding(resource, def, includes, (DynamicSearchMethodBinding) nextMethodBinding);
+// }
+//
+// Collections.sort(resource.getOperation(), new Comparator() {
+// @Override
+// public int compare(RestResourceOperation theO1, RestResourceOperation theO2) {
+// RestfulOperationTypeEnum o1 = theO1.getCode().getValueAsEnum();
+// RestfulOperationTypeEnum o2 = theO2.getCode().getValueAsEnum();
+// if (o1 == null && o2 == null) {
+// return 0;
+// }
+// if (o1 == null) {
+// return 1;
+// }
+// if (o2 == null) {
+// return -1;
+// }
+// return o1.ordinal() - o2.ordinal();
+// }
+// });
+//
+// }
+//
+// for (String nextInclude : includes) {
+// resource.addSearchInclude(nextInclude);
+// }
+//
+// }
+
+ myConformance = retVal;
+ return retVal;
+ }
+
+ private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet includes, DynamicSearchMethodBinding searchMethodBinding) {
+ includes.addAll(searchMethodBinding.getIncludes());
+
+ List searchParameters = new ArrayList();
+ searchParameters.addAll(searchMethodBinding.getSearchParams());
+ sortRuntimeSearchParameters(searchParameters);
+
+ if (!searchParameters.isEmpty()) {
+
+ for (RuntimeSearchParam nextParameter : searchParameters) {
+
+ String nextParamName = nextParameter.getName();
+
+ // String chain = null;
+ String nextParamUnchainedName = nextParamName;
+ if (nextParamName.contains(".")) {
+ // chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
+ nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
+ }
+
+ String nextParamDescription = nextParameter.getDescription();
+
+ /*
+ * If the parameter has no description, default to the one from the resource
+ */
+ if (StringUtils.isBlank(nextParamDescription)) {
+ RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
+ if (paramDef != null) {
+ nextParamDescription = paramDef.getDescription();
+ }
+ }
+
+ RestResourceSearchParam param;
+ param = resource.addSearchParam();
+
+ param.setName(nextParamName);
+ // if (StringUtils.isNotBlank(chain)) {
+ // param.addChain(chain);
+ // }
+ param.setDocumentation(nextParamDescription);
+// param.setType(nextParameter.getParamType());
+ }
+ }
+ }
+
+ private void handleSearchMethodBinding(Rest rest, RestResource resource, String resourceName, RuntimeResourceDefinition def, TreeSet includes, SearchMethodBinding searchMethodBinding) {
+ includes.addAll(searchMethodBinding.getIncludes());
+
+ List params = searchMethodBinding.getParameters();
+ List searchParameters = new ArrayList();
+ for (IParameter nextParameter : params) {
+ if ((nextParameter instanceof SearchParameter)) {
+ searchParameters.add((SearchParameter) nextParameter);
+ }
+ }
+ sortSearchParameters(searchParameters);
+ if (!searchParameters.isEmpty()) {
+ boolean allOptional = searchParameters.get(0).isRequired() == false;
+
+ OperationDefinition query = null;
+ if (!allOptional) {
+ RestOperation operation = rest.addOperation();
+ query = new OperationDefinition();
+ operation.setDefinition(new ResourceReferenceDt(query));
+ query.getDescription().setValue(searchMethodBinding.getDescription());
+ query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
+ for (String nextInclude : searchMethodBinding.getIncludes()) {
+ query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
+ }
+ }
+
+ for (SearchParameter nextParameter : searchParameters) {
+
+ String nextParamName = nextParameter.getName();
+
+ // String chain = null;
+ String nextParamUnchainedName = nextParamName;
+ if (nextParamName.contains(".")) {
+ // chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
+ nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
+ }
+
+ String nextParamDescription = nextParameter.getDescription();
+
+ /*
+ * If the parameter has no description, default to the one from the resource
+ */
+ if (StringUtils.isBlank(nextParamDescription)) {
+ RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
+ if (paramDef != null) {
+ nextParamDescription = paramDef.getDescription();
+ }
+ }
+
+ Parameter param;
+ if (query == null) {
+// param = resource.addSearchParam();
+ } else {
+ param = query.addParameter();
+ param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
+ }
+
+// param.setName(nextParamName);
+ // if (StringUtils.isNotBlank(chain)) {
+ // param.addChain(chain);
+ // }
+// param.setDocumentation(nextParamDescription);
+// param.setType(nextParameter.getParamType());
+ for (Class extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
+ RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
+ if (targetDef != null) {
+// ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
+// if (code != null) {
+// param.addTarget(code);
+// }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the cache property (default is true). If set to true, the same response will be returned for each
+ * invocation.
+ *
+ * See the class documentation for an important note if you are extending this class
+ *
+ */
+ public void setCache(boolean theCache) {
+ myCache = theCache;
+ }
+
+ /**
+ * Sets the value of the "publisher" that will be placed in the generated conformance statement. As this
+ * is a mandatory element, the value should not be null (although this is not enforced). The value defaults
+ * to "Not provided" but may be set to null, which will cause this element to be omitted.
+ */
+ public void setPublisher(String thePublisher) {
+ myPublisher = thePublisher;
+ }
+
+ private void sortRuntimeSearchParameters(List searchParameters) {
+ Collections.sort(searchParameters, new Comparator() {
+ @Override
+ public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
+ return theO1.getName().compareTo(theO2.getName());
+ }
+ });
+ }
+
+ private void sortSearchParameters(List searchParameters) {
+ Collections.sort(searchParameters, new Comparator() {
+ @Override
+ public int compare(SearchParameter theO1, SearchParameter theO2) {
+ if (theO1.isRequired() == theO2.isRequired()) {
+ return theO1.getName().compareTo(theO2.getName());
+ }
+ if (theO1.isRequired()) {
+ return -1;
+ }
+ return 1;
+ }
+ });
+ }
+}
diff --git a/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerProfileProvider.java b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerProfileProvider.java
new file mode 100644
index 00000000000..2db9bb386e0
--- /dev/null
+++ b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/rest/server/provider/ServerProfileProvider.java
@@ -0,0 +1,79 @@
+package ca.uhn.fhir.rest.server.provider;
+
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 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.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.RuntimeResourceDefinition;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.dev.resource.Profile;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.IdParam;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+
+public class ServerProfileProvider implements IResourceProvider {
+
+ private FhirContext myContext;
+
+ public ServerProfileProvider(FhirContext theCtx) {
+ myContext = theCtx;
+ }
+
+ @Override
+ public Class extends IResource> getResourceType() {
+ return Profile.class;
+ }
+
+ @Read()
+ public Profile getProfileById(@IdParam IdDt theId) {
+ RuntimeResourceDefinition retVal = myContext.getResourceDefinitionById(theId.getValue());
+ if (retVal==null) {
+ return null;
+ }
+ return (Profile) retVal.toProfile();
+ }
+
+ @Search()
+ public List getAllProfiles() {
+ List defs = new ArrayList(myContext.getResourceDefinitions());
+ Collections.sort(defs, new Comparator() {
+ @Override
+ public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
+ int cmp = theO1.getName().compareTo(theO2.getName());
+ if (cmp==0) {
+ cmp=theO1.getResourceProfile().compareTo(theO2.getResourceProfile());
+ }
+ return cmp;
+ }});
+ ArrayList retVal = new ArrayList();
+ for (RuntimeResourceDefinition next : defs) {
+ retVal.add((Profile) next.toProfile());
+ }
+ return retVal;
+ }
+
+}