From dd95a52240532e8c2201593b4cd3323991d66263 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 2 May 2016 18:04:42 -0400 Subject: [PATCH] Get fluentpath entirely working in JPA --- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 208 ++++++++++-------- .../dao/dstu3/SearchParamExtractorDstu3.java | 5 +- .../dstu3/SearchParamExtractorDstu3Test.java | 8 +- .../dstu3/ResourceProviderDstu3Test.java | 7 +- .../test/resources/document-father-dstu3.json | 22 +- .../validation/FhirInstanceValidator.java | 3 +- .../hapi/validation/HapiWorkerContext.java | 3 + .../org/hl7/fhir/dstu3/metamodel/Element.java | 15 ++ .../java/org/hl7/fhir/dstu3/model/Base.java | 2 +- .../dstu3/validation/InstanceValidator.java | 19 +- .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 1 + .../hl7/fhir/utilities/xhtml/XhtmlParser.java | 11 +- .../fhir/dstu3/utils/FhirPathEngineTest.java | 12 + .../fhir/utilities/xhtml/XhtmlNodeTest.java | 39 ++++ 14 files changed, 231 insertions(+), 124 deletions(-) create mode 100644 hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeTest.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 041137f4273..b4a4cc76f72 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -60,6 +60,7 @@ import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.StringType; 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.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -242,6 +243,14 @@ public abstract class BaseHapiFhirDao implements IDao { protected Set extractResourceLinks(ResourceTable theEntity, IBaseResource theResource) { Set retVal = new HashSet(); + + /* + * For now we don't try to load any of the links in a bundle if it's the + * actual bundle we're storing.. + */ + if (theResource instanceof IBaseBundle) { + return retVal; + } RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { @@ -261,105 +270,27 @@ public abstract class BaseHapiFhirDao implements IDao { } List refs = mySearchParamExtractor.extractResourceLinks(theResource, nextSpDef); - List> allowedTypesInField = null; for (PathAndRef nextPathAndRef : refs) { Object nextObject = nextPathAndRef.getRef(); ResourceLink nextEntity; + IIdType nextId; if (nextObject instanceof IBaseReference) { IBaseReference nextValue = (IBaseReference) nextObject; if (nextValue.isEmpty()) { continue; } - if (nextValue.getReferenceElement().isEmpty() || nextValue.getReferenceElement().getValue().startsWith("#")) { + nextId = nextValue.getReferenceElement(); + if (nextId.isEmpty() || nextId.getValue().startsWith("#")) { // This is a blank or contained resource reference continue; } - - String typeString = nextValue.getReferenceElement().getResourceType(); - if (isBlank(typeString)) { - throw new InvalidRequestException( - "Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReferenceElement().getValue()); - } - RuntimeResourceDefinition resourceDefinition; - try { - resourceDefinition = getContext().getResourceDefinition(typeString); - } catch (DataFormatException e) { - throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " - + nextValue.getReferenceElement().getValue()); - } - - Class type = resourceDefinition.getImplementingClass(); - String id = nextValue.getReferenceElement().getIdPart(); - if (StringUtils.isBlank(id)) { - throw new InvalidRequestException( - "Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextValue.getReferenceElement().getValue()); - } - - IFhirResourceDao dao = getDao(type); - if (dao == null) { - StringBuilder b = new StringBuilder(); - b.append("This server (version "); - b.append(myContext.getVersion().getVersion()); - b.append(") is not able to handle resources of type["); - b.append(nextValue.getReferenceElement().getResourceType()); - b.append("] - Valid resource types for this server: "); - b.append(myResourceTypeToDao.keySet().toString()); - - throw new InvalidRequestException(b.toString()); - } - Long valueOf; - try { - valueOf = translateForcedIdToPid(typeString, id); - } catch (ResourceNotFoundException e) { - String resName = getContext().getResourceDefinition(type).getName(); - throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); - } - ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf); - RuntimeResourceDefinition targetResourceDef = getContext().getResourceDefinition(type); - if (target == null) { - String resName = targetResourceDef.getName(); - throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); - } - - if (!typeString.equals(target.getResourceType())) { - throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReferenceElement().getValue() + " but resource with ID " - + nextValue.getReferenceElement().getIdPart() + " is actually of type " + target.getResourceType()); - } - - if (nextSpDef.getTargets() != null && !nextSpDef.getTargets().contains(typeString)) { + } else if (nextObject instanceof IBaseResource) { + nextId = ((IBaseResource) nextObject).getIdElement(); + if (nextId == null || nextId.hasIdPart() == false) { continue; } -// /* -// * Is the target type an allowable type of resource for the path where it is referenced? -// */ -// -// if (allowedTypesInField == null) { -// BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPathAndRef.getPath()); -// if (childDef instanceof RuntimeChildResourceDefinition) { -// RuntimeChildResourceDefinition resRefDef = (RuntimeChildResourceDefinition) childDef; -// allowedTypesInField = resRefDef.getResourceTypes(); -// } else { -// allowedTypesInField = new ArrayList>(); -// allowedTypesInField.add(IBaseResource.class); -// } -// } -// -// boolean acceptableLink = false; -// for (Class next : allowedTypesInField) { -// if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) { -// acceptableLink = true; -// break; -// } -// } -// -// if (!acceptableLink) { -// throw new UnprocessableEntityException( -// "Invalid reference found at path '" + nextPathAndRef.getPath() + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path"); -// } - - nextEntity = new ResourceLink(nextPathAndRef.getPath(), theEntity, target); } else { if (!multiType) { if (nextSpDef.getName().equals("sourceuri")) { @@ -370,6 +301,89 @@ public abstract class BaseHapiFhirDao implements IDao { continue; } } + + String typeString = nextId.getResourceType(); + if (isBlank(typeString)) { + throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextId.getValue()); + } + RuntimeResourceDefinition resourceDefinition; + try { + resourceDefinition = getContext().getResourceDefinition(typeString); + } catch (DataFormatException e) { + throw new InvalidRequestException( + "Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextId.getValue()); + } + + Class type = resourceDefinition.getImplementingClass(); + String id = nextId.getIdPart(); + if (StringUtils.isBlank(id)) { + throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextId.getValue()); + } + + IFhirResourceDao dao = getDao(type); + if (dao == null) { + StringBuilder b = new StringBuilder(); + b.append("This server (version "); + b.append(myContext.getVersion().getVersion()); + b.append(") is not able to handle resources of type["); + b.append(nextId.getResourceType()); + b.append("] - Valid resource types for this server: "); + b.append(myResourceTypeToDao.keySet().toString()); + + throw new InvalidRequestException(b.toString()); + } + Long valueOf; + try { + valueOf = translateForcedIdToPid(typeString, id); + } catch (ResourceNotFoundException e) { + String resName = getContext().getResourceDefinition(type).getName(); + throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); + } + ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf); + RuntimeResourceDefinition targetResourceDef = getContext().getResourceDefinition(type); + if (target == null) { + String resName = targetResourceDef.getName(); + throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); + } + + if (!typeString.equals(target.getResourceType())) { + throw new UnprocessableEntityException( + "Resource contains reference to " + nextId.getValue() + " but resource with ID " + nextId.getIdPart() + " is actually of type " + target.getResourceType()); + } + + if (nextSpDef.getTargets() != null && !nextSpDef.getTargets().contains(typeString)) { + continue; + } + + // /* + // * Is the target type an allowable type of resource for the path where it is referenced? + // */ + // + // if (allowedTypesInField == null) { + // BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPathAndRef.getPath()); + // if (childDef instanceof RuntimeChildResourceDefinition) { + // RuntimeChildResourceDefinition resRefDef = (RuntimeChildResourceDefinition) childDef; + // allowedTypesInField = resRefDef.getResourceTypes(); + // } else { + // allowedTypesInField = new ArrayList>(); + // allowedTypesInField.add(IBaseResource.class); + // } + // } + // + // boolean acceptableLink = false; + // for (Class next : allowedTypesInField) { + // if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) { + // acceptableLink = true; + // break; + // } + // } + // + // if (!acceptableLink) { + // throw new UnprocessableEntityException( + // "Invalid reference found at path '" + nextPathAndRef.getPath() + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path"); + // } + + nextEntity = new ResourceLink(nextPathAndRef.getPath(), theEntity, target); if (nextEntity != null) { retVal.add(nextEntity); } @@ -1457,10 +1471,10 @@ public abstract class BaseHapiFhirDao implements IDao { if (tag != null) { throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data"); } - + String resName = getContext().getResourceDefinition(theResource).getName(); validateChildReferences(theResource, resName); - + } private void validateChildReferences(IBase theElement, String thePath) { @@ -1471,23 +1485,23 @@ public abstract class BaseHapiFhirDao implements IDao { if (!(def instanceof BaseRuntimeElementCompositeDefinition)) { return; } - - BaseRuntimeElementCompositeDefinition cdef = (BaseRuntimeElementCompositeDefinition)def; + + BaseRuntimeElementCompositeDefinition cdef = (BaseRuntimeElementCompositeDefinition) def; for (BaseRuntimeChildDefinition nextChildDef : cdef.getChildren()) { - + List values = nextChildDef.getAccessor().getValues(theElement); if (values == null || values.isEmpty()) { continue; } - + String newPath = thePath + "." + nextChildDef.getElementName(); for (IBase nextChild : values) { validateChildReferences(nextChild, newPath); } - + if (nextChildDef instanceof RuntimeChildResourceDefinition) { - RuntimeChildResourceDefinition nextChildDefRes = (RuntimeChildResourceDefinition)nextChildDef; + RuntimeChildResourceDefinition nextChildDefRes = (RuntimeChildResourceDefinition) nextChildDef; Set validTypes = new HashSet(); boolean allowAny = false; for (Class nextValidType : nextChildDefRes.getResourceTypes()) { @@ -1497,17 +1511,17 @@ public abstract class BaseHapiFhirDao implements IDao { } validTypes.add(getContext().getResourceDefinition(nextValidType).getName()); } - + if (allowAny) { continue; } - + for (IBase nextChild : values) { - IBaseReference nextRef = (IBaseReference)nextChild; + IBaseReference nextRef = (IBaseReference) nextChild; if (!isBlank(nextRef.getReferenceElement().getResourceType())) { if (!validTypes.contains(nextRef.getReferenceElement().getResourceType())) { - throw new UnprocessableEntityException( - "Invalid reference found at path '" + newPath + "'. Resource type '" + nextRef.getReferenceElement().getResourceType() + "' is not valid for this path"); + throw new UnprocessableEntityException( + "Invalid reference found at path '" + newPath + "'. Resource type '" + nextRef.getReferenceElement().getResourceType() + "' is not valid for this path"); } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java index a24645e7098..2d6c3987eba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java @@ -666,7 +666,10 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen List values = new ArrayList(); try { - values.addAll(fp.evaluate((Base) theResource, thePaths)); + String[] nextPathsSplit = SPLIT.split(thePaths); + for (String nextPath : nextPathsSplit) { + values.addAll(fp.evaluate((Base) theResource, nextPath)); + } } catch (FHIRException e) { throw new InternalErrorException(e); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java index 74f29335d21..040ea3744c0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java @@ -8,6 +8,7 @@ import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport; import org.hl7.fhir.dstu3.model.Observation; import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; @@ -24,10 +25,13 @@ public class SearchParamExtractorDstu3Test { @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); - ourValidationSupport = new DefaultProfileValidationSupport(); } - + @BeforeClass + public static void beforeClass() { + ourValidationSupport = new DefaultProfileValidationSupport(); + } + @Test public void testParamWithOrInPath() { Observation obs = new Observation(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 29104c664d2..6118fd4ff61 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -146,6 +146,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { return retVal; } + // Y @Test public void testBundleCreate() throws Exception { IGenericClient client = ourClient; @@ -2515,14 +2516,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { ourLog.info(resp); assertEquals(412, response.getStatusLine().getStatusCode()); assertThat(resp, not(containsString("Resource has no id"))); - assertThat(resp, stringContainsInOrder(">ERROR<", "/f:Patient/f:contact", "
SHALL at least contain a contact's details or a reference to an organization
", "", "", - "", "")); + assertThat(resp, stringContainsInOrder(">ERROR<", "[Patient.contact]", "
SHALL at least contain a contact's details or a reference to an organization", ""));
 		} finally {
 			IOUtils.closeQuietly(response.getEntity().getContent());
 			response.close();
 		}
 	}
 
+	// Y
 	@Test
 	public void testValidateResourceHuge() throws IOException {
 
@@ -2555,6 +2556,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
 	public void testValidateResourceWithId() throws IOException {
 
 		Patient patient = new Patient();
+		patient.setId("123");
 		patient.addName().addGiven("James");
 		patient.setBirthDateElement(new DateType("2011-02-02"));
 
@@ -2578,6 +2580,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
 		}
 	}
 
+	// Y
 	@Test
 	public void testValidateResourceWithNoIdParameters() throws IOException {
 
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/document-father-dstu3.json b/hapi-fhir-jpaserver-base/src/test/resources/document-father-dstu3.json
index da361b3be22..7d5aa9a83c4 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/document-father-dstu3.json
+++ b/hapi-fhir-jpaserver-base/src/test/resources/document-father-dstu3.json
@@ -10,13 +10,11 @@
     ]
   },
   "type": "document",
-  "base": "http://fhir.healthintersections.com.au/open",
   "entry": [
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57",
       "resource": {
         "resourceType": "Composition",
-        "id": "180f219f-97a8-486d-99d9-ed631fe4fc57",
         "meta": {
           "lastUpdated": "2013-05-28T22:12:21Z"
         },
@@ -197,10 +195,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:d0dd51d3-3ab2-4c84-b697-a630c3e40e7a",
       "resource": {
         "resourceType": "List",
-        "id": "d0dd51d3-3ab2-4c84-b697-a630c3e40e7a",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
@@ -233,10 +230,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:541a72a8-df75-4484-ac89-ac4923f03b81",
       "resource": {
         "resourceType": "Observation",
-        "id": "541a72a8-df75-4484-ac89-ac4923f03b81",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
@@ -259,10 +255,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:673f8db5-0ffd-4395-9657-6da00420bbc1",
       "resource": {
         "resourceType": "List",
-        "id": "673f8db5-0ffd-4395-9657-6da00420bbc1",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
@@ -322,10 +317,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:124a6916-5d84-4b8c-b250-10cefb8e6e86",
       "resource": {
         "resourceType": "MedicationOrder",
-        "id": "124a6916-5d84-4b8c-b250-10cefb8e6e86",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
@@ -395,10 +389,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "fullUrl": "urn:uuid:68f86194-e6e1-4f65-b64a-5314256f8d7b",
       "resource": {
         "resourceType": "List",
-        "id": "68f86194-e6e1-4f65-b64a-5314256f8d7b",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
@@ -431,10 +424,9 @@
       }
     },
     {
-      "base": "urn:uuid:",
+      "base": "urn:uuid:47600e0f-b6b5-4308-84b5-5dec157f7637",
       "resource": {
         "resourceType": "AllergyIntolerance",
-        "id": "47600e0f-b6b5-4308-84b5-5dec157f7637",
         "meta": {
           "lastUpdated": "2013-05-05T16:13:03Z"
         },
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java
index 115438d4fc5..d4426a02178 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java
@@ -12,6 +12,7 @@ import org.apache.commons.lang3.Validate;
 import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
 import org.hl7.fhir.dstu3.model.StructureDefinition;
 import org.hl7.fhir.dstu3.validation.IResourceValidator.BestPracticeWarningLevel;
+import org.hl7.fhir.dstu3.validation.IResourceValidator.IdStatus;
 import org.hl7.fhir.dstu3.validation.InstanceValidator;
 import org.hl7.fhir.dstu3.validation.ValidationMessage;
 import org.w3c.dom.Document;
@@ -32,7 +33,6 @@ import ca.uhn.fhir.validation.IValidatorModule;
 
 public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
 
-	private static FhirContext ourHl7OrgCtx;
 	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
 	private BestPracticeWarningLevel myBestPracticeWarningLevel;
 	private DocumentBuilderFactory myDocBuilderFactory;
@@ -139,6 +139,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
 
 		v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
 		v.setAnyExtensionsAllowed(true);
+		v.setResourceIdRule(IdStatus.OPTIONAL);
 
 		List messages = new ArrayList();
 
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java
index 055fa825636..77a2d4755c7 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java
@@ -9,6 +9,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.lang3.Validate;
 import org.apache.commons.lang3.StringUtils;
 import org.hl7.fhir.dstu3.formats.IParser;
 import org.hl7.fhir.dstu3.formats.ParserType;
@@ -43,6 +44,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
 	private IValidationSupport myValidationSupport;
 
 	public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
+		Validate.notNull(theCtx, "theCtx must not be null");
+		Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
 		myCtx = theCtx;
 		myValidationSupport = theValidationSupport;
 	}
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/metamodel/Element.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/metamodel/Element.java
index 2ee4573bd73..d32769e20d5 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/metamodel/Element.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/metamodel/Element.java
@@ -1,5 +1,7 @@
 package org.hl7.fhir.dstu3.metamodel;
 
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -321,5 +323,18 @@ public class Element extends Base {
 		return this;
  	}
 
+	@Override
+	public boolean isEmpty() {
+		if (isNotBlank(value)) {
+			return false;
+		}
+		for (Element next : getChildren()) {
+			if (!next.isEmpty()) {
+				return false;
+			}
+		}
+		return true;
+	}
+
 
 }
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base.java
index a425c83c411..0078d75a93d 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base.java
@@ -103,7 +103,7 @@ private Map userData;
 	public boolean hasType(String... name) {
 		String t = fhirType();
 		for (String n : name)
-		  if (n.equals(t))
+		  if (n.equalsIgnoreCase(t))
 		  	return true;
 		return false;
 	}
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java
index e8de4b62d63..a7c3b5335dd 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java
@@ -1247,14 +1247,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
 
 	private boolean isParametersEntry(String path) {
 		String[] parts = path.split("\\.");
-		return parts.length > 2 && parts[parts.length - 1].equals("resource") && (parts[parts.length - 2].startsWith("parameter[") || parts[parts.length - 2].startsWith("part["));
+		return parts.length > 2 && parts[parts.length - 1].equals("resource") && (pathEntryHasName(parts[parts.length - 2], "parameter") || pathEntryHasName(parts[parts.length - 2], "part"));
 	}
 
 	private boolean isBundleEntry(String path) {
 		String[] parts = path.split("\\.");
-		return parts.length > 2 && parts[parts.length - 1].equals("resource") && parts[parts.length - 2].startsWith("entry[");
+		return parts.length > 2 && parts[parts.length - 1].equals("resource") && pathEntryHasName(parts[parts.length - 2], "entry");
 	}
 
+
+	private static boolean pathEntryHasName(String thePathEntry, String theName) {
+		if (thePathEntry.equals(theName)) {
+			return true;
+		}
+		if (thePathEntry.length() >= theName.length() + 3) {
+			if (thePathEntry.startsWith(theName)) {
+				if (thePathEntry.charAt(theName.length()) == '[') {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
 	private boolean isPrimitiveType(String type) {
 		return type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("integer") || type.equalsIgnoreCase("string") || type.equalsIgnoreCase("decimal") || type.equalsIgnoreCase("uri")
 				|| type.equalsIgnoreCase("base64Binary") || type.equalsIgnoreCase("instant") || type.equalsIgnoreCase("date") || type.equalsIgnoreCase("uuid") || type.equalsIgnoreCase("id")
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java
index 886dcc99cda..572881b1848 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java
@@ -75,6 +75,7 @@ public class XhtmlNode implements IBaseXhtml {
   }
 
   public void setName(String name) {
+    assert name.contains(":") == false : "Name should not contain any : but was " + name;
     this.name = name;
   }
 
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java
index 496dc63b9a0..73d60d46813 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java
@@ -42,8 +42,6 @@ import java.util.Set;
 
 import org.hl7.fhir.exceptions.FHIRException;
 import org.hl7.fhir.exceptions.FHIRFormatError;
-import org.hl7.fhir.utilities.xhtml.XhtmlParser.NSMap;
-import org.hl7.fhir.utilities.xhtml.XhtmlParser.QName;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -107,6 +105,7 @@ public class XhtmlParser {
   	public String getNs() {
   		return ns;
   	}
+  	
   }
 
 	private Set elements = new HashSet();
@@ -515,7 +514,7 @@ private boolean elementIsOk(String name) throws FHIRFormatError  {
           else
           {
             if (mustBeWellFormed)
-              throw new FHIRFormatError("Malformed XHTML: Found \"\" expecting \"\""+descLoc());
+              throw new FHIRFormatError("Malformed XHTML: Found \"\" expecting \"\""+descLoc());
             for (int i = parents.size() - 1; i >= 0; i--)
             {
               if (parents.get(i).getName().equals(n))
@@ -1106,6 +1105,12 @@ private boolean elementIsOk(String name) throws FHIRFormatError  {
     String n = readName().toLowerCase();
     readToTagEnd();
     XhtmlNode result = new XhtmlNode(NodeType.Element);
+    
+    int colonIndex = n.indexOf(':');
+    if (colonIndex != -1) {
+   	 n = n.substring(colonIndex + 1);
+    }
+    
     result.setName(n);
     unwindPoint = null;
     List p = new ArrayList();
diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java
index 4f00ca2dad9..54ce9443ba5 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java
@@ -9,7 +9,9 @@ import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
 import org.hl7.fhir.dstu3.hapi.validation.HapiWorkerContext;
 import org.hl7.fhir.dstu3.model.Base;
 import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.Observation;
 import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.StringType;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -23,6 +25,16 @@ public class FhirPathEngineTest {
 	private static FHIRPathEngine ourEngine;
 	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirPathEngineTest.class);
 
+	@Test
+	public void testAs() throws Exception {
+		Observation obs = new Observation();
+		obs.setValue(new StringType("FOO"));
+		
+		List value = ourEngine.evaluate(obs, "Observation.value.as(String)");
+		assertEquals(1, value.size());
+		assertEquals("FOO", ((StringType)value.get(0)).getValue());
+	}
+	
 	@Test
 	public void testExistsWithNoValue() throws FHIRException {
 		Patient patient = new Patient();
diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeTest.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeTest.java
new file mode 100644
index 00000000000..9911c9ca4fe
--- /dev/null
+++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeTest.java
@@ -0,0 +1,39 @@
+package org.hl7.fhir.utilities.xhtml;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class XhtmlNodeTest {
+	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XhtmlNodeTest.class);
+	@Test
+	public void testParseXhtmlUnqualified() {
+		
+		XhtmlNode node = new XhtmlNode();
+		node.setValueAsString("
" + + "\"Twitter" + + "@fhirabend" + + "
"); + + String output = node.getValueAsString(); + ourLog.info(output); + + assertEquals("
\"Twitter@fhirabend
", output); + } + + @Test + public void testParseXhtmlQualified() { + + XhtmlNode node = new XhtmlNode(); + node.setValueAsString("" + + "" + + "@fhirabend" + + ""); + + String output = node.getValueAsString(); + ourLog.info(output); + + assertEquals("
\"Twitter@fhirabend
", output); + } + +}