From 6afe6804dbe919f3f5beaf8449f3f9b0014d3347 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sat, 25 Nov 2017 17:52:12 -0500 Subject: [PATCH 1/2] Fix #796 - Correctly escape spaces in generated URLs --- hapi-fhir-base/pom.xml | 11 + .../ca/uhn/fhir/rest/param/ParameterUtil.java | 13 - .../main/java/ca/uhn/fhir/util/UrlUtil.java | 120 ++- .../java/ca/uhn/fhir/util/UrlUtilTest.java | 54 +- .../client/impl/BaseHttpClientInvocation.java | 23 +- .../fhir/rest/client/impl/GenericClient.java | 2 +- .../method/HttpGetClientInvocation.java | 92 +- .../fhir/rest/client/method/MethodUtil.java | 4 +- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 7 +- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 9 +- .../uhn/fhir/jpa/dao/SearchParameterMap.java | 10 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 2 +- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 2 +- .../jpa/term/HapiTerminologySvcDstu3.java | 2 +- .../fhir/jpa/term/HapiTerminologySvcR4.java | 2 +- .../provider/ResourceProviderDstu2Test.java | 3 +- .../dstu3/ResourceProviderDstu3Test.java | 6 +- .../provider/r4/GraphQLProviderR4Test.java | 4 +- .../provider/r4/ResourceProviderR4Test.java | 7 +- .../interceptor/AnalyticsInterceptor.java | 6 +- .../fhir/rest/server/RestfulServerUtils.java | 198 ++-- .../interceptor/LoggingInterceptor.java | 12 +- .../ResponseHighlighterInterceptor.java | 4 +- .../BaseResourceReturningMethodBinding.java | 4 +- .../rest/server/CompositeParameterTest.java | 40 +- .../fhir/rest/server/DefaultEncodingTest.java | 34 +- .../uhn/fhir/rest/server/ReadDstu1Test.java | 38 +- .../rest/server/ReferenceParameterTest.java | 54 +- ...ServerWithSearchParamTypesDstu2_1Test.java | 10 +- ...onServerWithSearchParamTypesDstu2Test.java | 12 +- .../rest/server/ServerSearchDstu2Test.java | 31 +- .../ResponseHighlightingInterceptorTest.java | 5 +- .../codesystems/MapTransformEnumFactory.java | 244 ++--- ...onServerWithSearchParamTypesDstu3Test.java | 10 +- .../uhn/fhir/rest/server/SearchDstu3Test.java | 8 +- .../fhir/instance/utilities/xml/XMLUtil.java | 812 +++++++-------- .../r4/elementmodel/VerticalBarParser.java | 986 +++++++++--------- .../ca/uhn/fhir/rest/client/ClientR4Test.java | 70 +- .../fhir/rest/client/GenericClientTest.java | 91 +- .../rest/server/GraphQLR4ProviderTest.java | 8 +- .../fhir/rest/server/GraphQLR4RawTest.java | 6 +- .../ca/uhn/fhir/rest/server/SearchR4Test.java | 8 +- .../server/SearchSearchServerDstu1Test.java | 12 +- .../org/hl7/fhir/utilities/xml/XMLUtil.java | 944 ++++++++--------- .../rest/server/GraphQLDstu3ProviderTest.java | 8 +- src/changes/changes.xml | 6 + 46 files changed, 1996 insertions(+), 2038 deletions(-) diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 601a3050870..bcc5b1350f9 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -59,6 +59,13 @@ com.phloc phloc-commons true + + + + com.google.code.findbugs + annotations + + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java index 4ca33aa1156..543d2561369 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java @@ -138,19 +138,6 @@ public class ParameterUtil { return b.toString(); } - /** - * Escapes a string according to the rules for parameter escaping specified in the FHIR Specification Escaping - * Section - */ - public static String escapeAndUrlEncode(String theValue) { - if (theValue == null) { - return null; - } - - String escaped = escape(theValue); - return UrlUtil.escape(escaped); - } - /** * Escapes a string according to the rules for parameter escaping specified in the FHIR Specification Escaping * Section diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index bd43db5bf4a..5349f8bd25c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -1,16 +1,20 @@ package ca.uhn.fhir.util; -import static org.apache.commons.lang3.StringUtils.defaultIfBlank; -import static org.apache.commons.lang3.StringUtils.isBlank; - -import java.io.UnsupportedEncodingException; -import java.net.*; -import java.util.*; -import java.util.Map.Entry; - import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import com.google.common.escape.Escaper; +import com.google.common.net.PercentEscaper; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.*; +import java.util.Map.Entry; + +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; +import static org.apache.commons.lang3.StringUtils.isBlank; /* * #%L @@ -35,6 +39,10 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class UrlUtil { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UrlUtil.class); + private static final String URL_FORM_PARAMETER_OTHER_SAFE_CHARS = "-_.*"; + private static final Escaper PARAMETER_ESCAPER = new PercentEscaper(URL_FORM_PARAMETER_OTHER_SAFE_CHARS, false); + + /** * Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning and return theEndpoint if the input is invalid. */ @@ -88,19 +96,24 @@ public class UrlUtil { } /** - * URL encode a value + * URL encode a value according to RFC 3986 + *

+ * This method is intended to be applied to an individual parameter + * name or value. For example, if you are creating the URL + * http://example.com/fhir/Patient?key=føø + * it would be appropriate to pass the string "føø" to this method, + * but not appropriate to pass the entire URL since characters + * such as "/" and "?" would also be escaped. + *

*/ - public static String escape(String theValue) { - if (theValue == null) { + public static String escapeUrlParam(String theUnescaped) { + if (theUnescaped == null) { return null; } - try { - return URLEncoder.encode(theValue, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new Error("UTF-8 not supported on this platform"); - } + return PARAMETER_ESCAPER.escape(theUnescaped); } + public static boolean isAbsolute(String theValue) { String value = theValue.toLowerCase(); return value.startsWith("http://") || value.startsWith("https://"); @@ -147,7 +160,7 @@ public class UrlUtil { } public static void main(String[] args) { - System.out.println(escape("http://snomed.info/sct?fhir_vs=isa/126851005")); + System.out.println(escapeUrlParam("http://snomed.info/sct?fhir_vs=isa/126851005")); } public static Map parseQueryString(String theQueryString) { @@ -156,36 +169,20 @@ public class UrlUtil { return toQueryStringMap(map); } - public static Map parseQueryStrings(String... theQueryString) { - HashMap> map = new HashMap>(); - for (String next : theQueryString) { - parseQueryString(next, map); - } - return toQueryStringMap(map); - } - - private static Map toQueryStringMap(HashMap> map) { - HashMap retVal = new HashMap(); - for (Entry> nextEntry : map.entrySet()) { - retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[nextEntry.getValue().size()])); - } - return retVal; - } - private static void parseQueryString(String theQueryString, HashMap> map) { String query = theQueryString; if (query.startsWith("?")) { query = query.substring(1); } - - + + StringTokenizer tok = new StringTokenizer(query, "&"); while (tok.hasMoreTokens()) { String nextToken = tok.nextToken(); if (isBlank(nextToken)) { continue; } - + int equalsIndex = nextToken.indexOf('='); String nextValue; String nextKey; @@ -196,21 +193,28 @@ public class UrlUtil { nextKey = nextToken.substring(0, equalsIndex); nextValue = nextToken.substring(equalsIndex + 1); } - + nextKey = unescape(nextKey); nextValue = unescape(nextValue); - + List list = map.get(nextKey); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); map.put(nextKey, list); } list.add(nextValue); } } - //@formatter:off - /** + public static Map parseQueryStrings(String... theQueryString) { + HashMap> map = new HashMap>(); + for (String next : theQueryString) { + parseQueryString(next, map); + } + return toQueryStringMap(map); + } + + /** * Parse a URL in one of the following forms: *
    *
  • [Resource Type]?[Search Params] @@ -278,6 +282,16 @@ public class UrlUtil { } + //@formatter:off + + private static Map toQueryStringMap(HashMap> map) { + HashMap retVal = new HashMap(); + for (Entry> nextEntry : map.entrySet()) { + retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[nextEntry.getValue().size()])); + } + return retVal; + } + public static String unescape(String theString) { if (theString == null) { return null; @@ -305,30 +319,30 @@ public class UrlUtil { return myParams; } - public String getResourceId() { - return myResourceId; - } - - public String getResourceType() { - return myResourceType; - } - - public String getVersionId() { - return myVersionId; - } - public void setParams(String theParams) { myParams = theParams; } + public String getResourceId() { + return myResourceId; + } + public void setResourceId(String theResourceId) { myResourceId = theResourceId; } + public String getResourceType() { + return myResourceType; + } + public void setResourceType(String theResourceType) { myResourceType = theResourceType; } + public String getVersionId() { + return myVersionId; + } + public void setVersionId(String theVersionId) { myVersionId = theVersionId; } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java index d55b7a9a1cc..e949a6275dd 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java @@ -1,20 +1,42 @@ package ca.uhn.fhir.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import org.junit.Test; +import static org.junit.Assert.*; + public class UrlUtilTest { + @Test + public void testConstructAbsoluteUrl() { + assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz")); + assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "baz")); + assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "baz/")); + assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "./baz/")); + + assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "../baz/")); + assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "/baz/")); + } + + @Test + public void testConstructRelativeUrl() { + assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://boo/far/faz", "http://foo/bar/baz")); + assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://foo/far/faz", "http://foo/bar/baz")); + assertEquals("baz", UrlUtil.constructRelativeUrl("http://foo/bar/boo", "http://foo/bar/baz")); + } + + @Test + public void testEscape() { + assertEquals("A%20B", UrlUtil.escapeUrlParam("A B")); + assertEquals("A%2BB", UrlUtil.escapeUrlParam("A+B")); + } + @Test public void testIsValid() { assertTrue(UrlUtil.isValid("http://foo")); assertTrue(UrlUtil.isValid("https://foo")); assertTrue(UrlUtil.isValid("HTTP://Foo")); assertTrue(UrlUtil.isValid("HTTPS://Foo")); - + assertFalse(UrlUtil.isValid("file://foo")); assertFalse(UrlUtil.isValid("://foo")); assertFalse(UrlUtil.isValid("http:/ss")); @@ -24,7 +46,7 @@ public class UrlUtilTest { assertFalse(UrlUtil.isValid("")); assertFalse(UrlUtil.isValid(null)); } - + @Test public void testParseUrl() { assertEquals("ConceptMap", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde").getResourceType()); @@ -36,25 +58,5 @@ public class UrlUtilTest { assertEquals("a=b", UrlUtil.parseUrl("ConceptMap/ussgfht-loincde?a=b").getParams()); } - - @Test - public void testConstructAbsoluteUrl() { - assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz")); - assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz")); - assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz/")); - assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","./baz/")); - assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","../baz/")); - assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","/baz/")); - } - - - - @Test - public void testConstructRelativeUrl() { - assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://boo/far/faz", "http://foo/bar/baz")); - assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://foo/far/faz", "http://foo/bar/baz")); - assertEquals("baz", UrlUtil.constructRelativeUrl("http://foo/bar/boo", "http://foo/bar/baz")); - } - } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseHttpClientInvocation.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseHttpClientInvocation.java index b9fa14db159..1aa48b1286a 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseHttpClientInvocation.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseHttpClientInvocation.java @@ -20,13 +20,6 @@ package ca.uhn.fhir.rest.client.impl; * #L% */ -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum; @@ -34,6 +27,12 @@ import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; +import ca.uhn.fhir.util.UrlUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; public abstract class BaseHttpClientInvocation { @@ -115,13 +114,9 @@ public abstract class BaseHttpClientInvocation { } else { theUrlBuilder.append('&'); } - try { - theUrlBuilder.append(URLEncoder.encode(next.getKey(), "UTF-8")); - theUrlBuilder.append('='); - theUrlBuilder.append(URLEncoder.encode(nextValue, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error("UTF-8 not supported - This should not happen"); - } + theUrlBuilder.append(UrlUtil.escapeUrlParam(next.getKey())); + theUrlBuilder.append('='); + theUrlBuilder.append(UrlUtil.escapeUrlParam(nextValue)); } } } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index 60687a5f7b9..1432d8c5a00 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -354,7 +354,7 @@ public class GenericClient extends BaseClient implements IGenericClient { case '?': case '$': case ':': - b.append(UrlUtil.escape(Character.toString(nextChar))); + b.append(UrlUtil.escapeUrlParam(Character.toString(nextChar))); break; default: b.append(nextChar); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HttpGetClientInvocation.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HttpGetClientInvocation.java index 2f8b15e609a..21287d59ba8 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HttpGetClientInvocation.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HttpGetClientInvocation.java @@ -20,21 +20,18 @@ package ca.uhn.fhir.rest.client.method; * #L% */ -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.commons.lang3.StringUtils; - -import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * @author James Agnew @@ -51,51 +48,40 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation { myUrlPath = StringUtils.join(theUrlFragments, '/'); } - public HttpGetClientInvocation(FhirContext theContext, Map> theParameters, List theUrlFragments) { - super(theContext); - myParameters = theParameters; - myUrlPath = StringUtils.join(theUrlFragments, '/'); - } - public HttpGetClientInvocation(FhirContext theContext, String theUrlPath) { super(theContext); - myParameters = new HashMap>(); + myParameters = new HashMap<>(); myUrlPath = theUrlPath; } - public HttpGetClientInvocation(FhirContext theContext, String... theUrlFragments) { - super(theContext); - myParameters = new HashMap>(); - myUrlPath = StringUtils.join(theUrlFragments, '/'); - } + private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) { + boolean retVal = first; + if (retVal) { + b.append('?'); + retVal = false; + } else { + b.append('&'); + } + b.append(UrlUtil.escapeUrlParam(nextKey)); + b.append('='); + b.append(UrlUtil.escapeUrlParam(nextValue)); - public HttpGetClientInvocation(FhirContext theContext, List theUrlFragments) { - super(theContext); - myParameters = new HashMap>(); - myUrlPath = StringUtils.join(theUrlFragments, '/'); - } - - public Map> getParameters() { - return myParameters; - } - - public String getUrlPath() { - return myUrlPath; + return retVal; } @Override public IHttpRequest asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) { StringBuilder b = new StringBuilder(); - + if (!myUrlPath.contains("://")) { - b.append(theUrlBase); - if (!theUrlBase.endsWith("/") && !myUrlPath.startsWith("/")) { - b.append('/'); - } - } - b.append(myUrlPath); - + b.append(theUrlBase); + if (!theUrlBase.endsWith("/") && !myUrlPath.startsWith("/")) { + b.append('/'); + } + } + b.append(myUrlPath); + boolean first = b.indexOf("?") == -1; for (Entry> next : myParameters.entrySet()) { if (next.getValue() == null || next.getValue().isEmpty()) { @@ -112,22 +98,12 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation { return super.createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.GET); } - private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) { - boolean retVal = first; - if (retVal) { - b.append('?'); - retVal = false; - } else { - b.append('&'); - } - try { - b.append(URLEncoder.encode(nextKey, "UTF-8")); - b.append('='); - b.append(URLEncoder.encode(nextValue, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new ConfigurationException("Could not find UTF-8 encoding. This shouldn't happen.", e); - } - return retVal; + public Map> getParameters() { + return myParameters; + } + + public String getUrlPath() { + return myUrlPath; } } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java index ea1ad89d64a..5be18f1cf70 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java @@ -209,9 +209,9 @@ public class MethodUtil { for (String nextValue : nextEntry.getValue()) { b.append(haveQuestionMark ? '&' : '?'); haveQuestionMark = true; - b.append(UrlUtil.escape(nextEntry.getKey())); + b.append(UrlUtil.escapeUrlParam(nextEntry.getKey())); b.append('='); - b.append(UrlUtil.escape(nextValue)); + b.append(UrlUtil.escapeUrlParam(nextValue)); } } return b; 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 772638bb774..8707ab64240 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 @@ -35,7 +35,6 @@ import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; 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.model.primitive.XhtmlDt; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; @@ -229,14 +228,14 @@ public abstract class BaseHapiFhirDao implements IDao { ArrayList nextChoicesList = new ArrayList<>(); partsChoices.add(nextChoicesList); - String key = UrlUtil.escape(nextCompositeOf.getName()); + String key = UrlUtil.escapeUrlParam(nextCompositeOf.getName()); if (paramsListForCompositePart != null) { for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) { if (nextParam.getParamName().equals(nextCompositeOf.getName())) { IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType(); String value = nextParamAsClientParam.getValueAsQueryToken(getContext()); if (isNotBlank(value)) { - value = UrlUtil.escape(value); + value = UrlUtil.escapeUrlParam(value); nextChoicesList.add(key + "=" + value); } } @@ -246,7 +245,7 @@ public abstract class BaseHapiFhirDao implements IDao { for (ResourceLink nextLink : linksForCompositePart) { String value = nextLink.getTargetResource().getIdDt().toUnqualifiedVersionless().getValue(); if (isNotBlank(value)) { - value = UrlUtil.escape(value); + value = UrlUtil.escapeUrlParam(value); nextChoicesList.add(key + "=" + value); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index f791b404272..c4568fa629e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -60,7 +60,6 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.query.Query; import org.hl7.fhir.dstu3.model.BaseResource; -import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -175,7 +174,7 @@ public class SearchBuilder implements ISearchBuilder { if (valueBuilder.length() > 0) { valueBuilder.append(','); } - valueBuilder.append(UrlUtil.escape(next.getValueAsQueryToken(myContext))); + valueBuilder.append(UrlUtil.escapeUrlParam(next.getValueAsQueryToken(myContext))); targetResourceType = next.getTargetResourceType(); owningParameter = next.getOwningFieldName(); parameterName = next.getParameterName(); @@ -185,7 +184,7 @@ public class SearchBuilder implements ISearchBuilder { continue; } - String matchUrl = targetResourceType + '?' + UrlUtil.escape(parameterName) + '=' + valueBuilder.toString(); + String matchUrl = targetResourceType + '?' + UrlUtil.escapeUrlParam(parameterName) + '=' + valueBuilder.toString(); RuntimeResourceDefinition targetResourceDefinition; try { targetResourceDefinition = myContext.getResourceDefinition(targetResourceType); @@ -1272,13 +1271,13 @@ public class SearchBuilder implements ISearchBuilder { List> params = new ArrayList<>(); for (Entry>> nextParamNameToValues : theParams.entrySet()) { String nextParamName = nextParamNameToValues.getKey(); - nextParamName = UrlUtil.escape(nextParamName); + nextParamName = UrlUtil.escapeUrlParam(nextParamName); for (List nextAnd : nextParamNameToValues.getValue()) { ArrayList nextValueList = new ArrayList<>(); params.add(nextValueList); for (IQueryParameterType nextOr : nextAnd) { String nextOrValue = nextOr.getValueAsQueryToken(myContext); - nextOrValue = UrlUtil.escape(nextOrValue); + nextOrValue = UrlUtil.escapeUrlParam(nextOrValue); nextValueList.add(nextParamName + "=" + nextOrValue); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParameterMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParameterMap.java index 864f4531981..0b14c5d8e5f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParameterMap.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.dao; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -24,7 +23,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; */ import java.util.*; -import ca.uhn.fhir.rest.param.ReferenceParam; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -138,9 +136,9 @@ public class SearchParameterMap extends LinkedHashMap nextValuesAnd : nextValuesAndsOut) { addUrlParamSeparator(b); IQueryParameterType firstValue = nextValuesAnd.get(0); - b.append(UrlUtil.escape(nextKey)); + b.append(UrlUtil.escapeUrlParam(nextKey)); if (firstValue.getMissing() != null) { b.append(Constants.PARAMQUALIFIER_MISSING); @@ -340,7 +338,7 @@ public class SearchParameterMap extends LinkedHashMap { String nextReplacementIdPart = nextReplacementId.getValueAsString(); if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) { matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart); - matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart); + matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart); } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 9e9ab48f2c6..95be502404a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -667,7 +667,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { String nextReplacementIdPart = nextReplacementId.getValueAsString(); if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) { matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart); - matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart); + matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart); } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 2290cb34384..e94a2a71700 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -321,7 +321,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I cs.setUrl(theSystem); cs.setContent(CodeSystemContentMode.NOTPRESENT); - DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails); + DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escapeUrlParam(theSystem), theRequestDetails); IIdType csId = createOutcome.getId().toUnqualifiedVersionless(); if (createOutcome.getCreated() != Boolean.TRUE) { CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index 66336118352..8623d04e4f3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -325,7 +325,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal cs.setUrl(theSystem); cs.setContent(CodeSystemContentMode.NOTPRESENT); - DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails); + DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escapeUrlParam(theSystem), theRequestDetails); IIdType csId = createOutcome.getId().toUnqualifiedVersionless(); if (createOutcome.getCreated() != Boolean.TRUE) { CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index fa04bb5b350..ba49a17ffda 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -30,7 +30,6 @@ import ca.uhn.fhir.model.dstu2.resource.*; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.valueset.*; import ca.uhn.fhir.model.primitive.*; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.client.api.IGenericClient; @@ -528,7 +527,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { pt.addName().addFamily("FOO"); resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|")))); + HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|")))); put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); IdDt id2; 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 47baa956052..9dea55a78ed 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 @@ -1639,7 +1639,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { myObservationDao.create(obs, mySrd); } - String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escape("urn:system|FOO"); + String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escapeUrlParam("urn:system|FOO"); List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids, contains(pid0.getValue())); } @@ -2347,7 +2347,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { myPatientDao.create(p, mySrd); } - String uri = ourServerBase + "/Patient?name=" + URLEncoder.encode("Jernelöv", "UTF-8") + "&_count=5&_pretty=true"; + String uri = ourServerBase + "/Patient?name=" + UrlUtil.escapeUrlParam("Jernelöv") + "&_count=5&_pretty=true"; ourLog.info("URI: {}", uri); HttpGet get = new HttpGet(uri); CloseableHttpResponse resp = ourHttpClient.execute(get); @@ -3792,7 +3792,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { pt.addName().setFamily("FOO"); resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|")))); + HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|")))); put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); IdType id2; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/GraphQLProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/GraphQLProviderR4Test.java index 286c6d9ce0b..7842f99a75d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/GraphQLProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/GraphQLProviderR4Test.java @@ -25,7 +25,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test { initTestPatients(); String query = "{name{family,given}}"; - HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse response = ourHttpClient.execute(httpGet); try { @@ -50,7 +50,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test { initTestPatients(); String query = "{PatientList(given:\"given\"){name{family,given}}}"; - HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse response = ourHttpClient.execute(httpGet); try { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index eddaab5fbd0..1ee7135ecfc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -61,7 +61,6 @@ import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -1647,7 +1646,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); } - String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escape("urn:system|FOO"); + String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escapeUrlParam("urn:system|FOO"); List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids, contains(pid0.getValue())); } @@ -2355,7 +2354,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myPatientDao.create(p, mySrd); } - String uri = ourServerBase + "/Patient?name=" + URLEncoder.encode("Jernelöv", "UTF-8") + "&_count=5&_pretty=true"; + String uri = ourServerBase + "/Patient?name=" + UrlUtil.escapeUrlParam("Jernelöv") + "&_count=5&_pretty=true"; ourLog.info("URI: {}", uri); HttpGet get = new HttpGet(uri); CloseableHttpResponse resp = ourHttpClient.execute(get); @@ -3938,7 +3937,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { pt.addName().setFamily("FOO"); resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|")))); + HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|")))); put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); IdType id2; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java index b659903135a..64baf5158d4 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java @@ -80,13 +80,13 @@ public class AnalyticsInterceptor extends InterceptorAdapter { b.append("&tid=").append(myAnalyticsTid); b.append("&t=event"); - b.append("&an=").append(UrlUtil.escape(myHostname)).append('+').append(UrlUtil.escape(next.getApplicationName())); + b.append("&an=").append(UrlUtil.escapeUrlParam(myHostname)).append('+').append(UrlUtil.escapeUrlParam(next.getApplicationName())); b.append("&ec=").append(next.getResourceName()); b.append("&ea=").append(next.getRestOperation()); b.append("&cid=").append(next.getClientId()); - b.append("&uip=").append(UrlUtil.escape(next.getSourceIp())); - b.append("&ua=").append(UrlUtil.escape(next.getUserAgent())); + b.append("&uip=").append(UrlUtil.escapeUrlParam(next.getSourceIp())); + b.append("&ua=").append(UrlUtil.escapeUrlParam(next.getUserAgent())); b.append("\n"); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 0dfb36051e9..cbe3dd0fa84 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -19,34 +19,38 @@ package ca.uhn.fhir.rest.server; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.*; - -import java.io.*; -import java.net.URLEncoder; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.servlet.http.HttpServletRequest; - -import ca.uhn.fhir.util.BinaryUtil; -import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.model.api.*; +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.primitive.InstantDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.ElementsParameter; import ca.uhn.fhir.rest.server.method.SummaryEnumParameter; +import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.UrlUtil; +import org.hl7.fhir.instance.model.api.*; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.Writer; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.apache.commons.lang3.StringUtils.*; public class RestfulServerUtils { static final Pattern ACCEPT_HEADER_PATTERN = Pattern.compile("\\s*([a-zA-Z0-9+.*/-]+)\\s*(;\\s*([a-zA-Z]+)\\s*=\\s*([a-zA-Z0-9.]+)\\s*)?(,?)"); @@ -127,74 +131,70 @@ public class RestfulServerUtils { } public static String createPagingLink(Set theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, Map theRequestParameters, boolean thePrettyPrint, - BundleTypeEnum theBundleType) { - try { - StringBuilder b = new StringBuilder(); - b.append(theServerBase); - b.append('?'); - b.append(Constants.PARAM_PAGINGACTION); - b.append('='); - b.append(URLEncoder.encode(theSearchId, "UTF-8")); + BundleTypeEnum theBundleType) { + StringBuilder b = new StringBuilder(); + b.append(theServerBase); + b.append('?'); + b.append(Constants.PARAM_PAGINGACTION); + b.append('='); + b.append(UrlUtil.escapeUrlParam(theSearchId)); + b.append('&'); + b.append(Constants.PARAM_PAGINGOFFSET); + b.append('='); + b.append(theOffset); + b.append('&'); + b.append(Constants.PARAM_COUNT); + b.append('='); + b.append(theCount); + String[] strings = theRequestParameters.get(Constants.PARAM_FORMAT); + if (strings != null && strings.length > 0) { b.append('&'); - b.append(Constants.PARAM_PAGINGOFFSET); + b.append(Constants.PARAM_FORMAT); b.append('='); - b.append(theOffset); - b.append('&'); - b.append(Constants.PARAM_COUNT); - b.append('='); - b.append(theCount); - String[] strings = theRequestParameters.get(Constants.PARAM_FORMAT); - if (strings != null && strings.length > 0) { - b.append('&'); - b.append(Constants.PARAM_FORMAT); - b.append('='); - String format = strings[0]; - format = replace(format, " ", "+"); - b.append(UrlUtil.escape(format)); - } - if (thePrettyPrint) { - b.append('&'); - b.append(Constants.PARAM_PRETTY); - b.append('='); - b.append(Constants.PARAM_PRETTY_VALUE_TRUE); - } - - if (theIncludes != null) { - for (Include nextInclude : theIncludes) { - if (isNotBlank(nextInclude.getValue())) { - b.append('&'); - b.append(Constants.PARAM_INCLUDE); - b.append('='); - b.append(URLEncoder.encode(nextInclude.getValue(), "UTF-8")); - } - } - } - - if (theBundleType != null) { - b.append('&'); - b.append(Constants.PARAM_BUNDLETYPE); - b.append('='); - b.append(theBundleType.getCode()); - } - - String paramName = Constants.PARAM_ELEMENTS; - String[] params = theRequestParameters.get(paramName); - if (params != null) { - for (String nextValue : params) { - if (isNotBlank(nextValue)) { - b.append('&'); - b.append(paramName); - b.append('='); - b.append(UrlUtil.escape(nextValue)); - } - } - } - - return b.toString(); - } catch (UnsupportedEncodingException e) { - throw new Error("UTF-8 not supported", e);// should not happen + String format = strings[0]; + format = replace(format, " ", "+"); + b.append(UrlUtil.escapeUrlParam(format)); } + if (thePrettyPrint) { + b.append('&'); + b.append(Constants.PARAM_PRETTY); + b.append('='); + b.append(Constants.PARAM_PRETTY_VALUE_TRUE); + } + + if (theIncludes != null) { + for (Include nextInclude : theIncludes) { + if (isNotBlank(nextInclude.getValue())) { + b.append('&'); + b.append(Constants.PARAM_INCLUDE); + b.append('='); + b.append(UrlUtil.escapeUrlParam(nextInclude.getValue())); + } + } + } + + if (theBundleType != null) { + b.append('&'); + b.append(Constants.PARAM_BUNDLETYPE); + b.append('='); + b.append(theBundleType.getCode()); + } + + String paramName = Constants.PARAM_ELEMENTS; + String[] params = theRequestParameters.get(paramName); + if (params != null) { + for (String nextValue : params) { + if (isNotBlank(nextValue)) { + b.append('&'); + b.append(paramName); + b.append('='); + b.append(UrlUtil.escapeUrlParam(nextValue)); + } + } + } + + return b.toString(); } /** @@ -392,15 +392,15 @@ public class RestfulServerUtils { try { NarrativeModeEnum narrativeMode = NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]); switch (narrativeMode) { - case NORMAL: - retVal = Collections.singleton(SummaryEnum.FALSE); - break; - case ONLY: - retVal = Collections.singleton(SummaryEnum.TEXT); - break; - case SUPPRESS: - retVal = Collections.singleton(SummaryEnum.DATA); - break; + case NORMAL: + retVal = Collections.singleton(SummaryEnum.FALSE); + break; + case ONLY: + retVal = Collections.singleton(SummaryEnum.TEXT); + break; + case SUPPRESS: + retVal = Collections.singleton(SummaryEnum.DATA); + break; } } catch (IllegalArgumentException e) { ourLog.debug("Invalid {} parameter: {}", Constants.PARAM_NARRATIVE, narrative[0]); @@ -461,13 +461,13 @@ public class RestfulServerUtils { EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails).getEncoding(); IParser parser; switch (responseEncoding) { - case JSON: - parser = theContext.newJsonParser(); - break; - case XML: - default: - parser = theContext.newXmlParser(); - break; + case JSON: + parser = theContext.newJsonParser(); + break; + case XML: + default: + parser = theContext.newXmlParser(); + break; } configureResponseParser(theRequestDetails, parser); @@ -581,13 +581,13 @@ public class RestfulServerUtils { } public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int stausCode, boolean theAddContentLocationHeader, - boolean respondGzip, RequestDetails theRequestDetails) throws IOException { + boolean respondGzip, RequestDetails theRequestDetails) throws IOException { return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, null, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null); } public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int theStausCode, String theStatusMessage, - boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) - throws IOException { + boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) + throws IOException { IRestfulResponse response = theRequestDetails.getResponse(); // Determine response encoding @@ -706,7 +706,7 @@ public class RestfulServerUtils { try { return Integer.parseInt(retVal[0]); } catch (NumberFormatException e) { - ourLog.debug("Failed to parse {} value '{}': {}", new Object[] { theParamName, retVal[0], e }); + ourLog.debug("Failed to parse {} value '{}': {}", new Object[]{theParamName, retVal[0], e}); return null; } } @@ -752,7 +752,7 @@ public class RestfulServerUtils { if (theContentType.equals(EncodingEnum.JSON_PLAIN_STRING) || theContentType.equals(EncodingEnum.XML_PLAIN_STRING)) { FhirVersionEnum ctxtEnum = theCtx.getVersion().getVersion(); myNonLegacy = ctxtEnum.isNewerThan(FhirVersionEnum.DSTU3) - || (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && !"1.4.0".equals(theCtx.getVersion().getVersion().getFhirVersionString())); + || (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && !"1.4.0".equals(theCtx.getVersion().getVersion().getFhirVersionString())); } else { myNonLegacy = EncodingEnum.isNonLegacy(theContentType); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index 729ab9d9cb2..83e732e94b5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -25,7 +25,6 @@ import java.io.IOException; */ import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.Date; import java.util.Map.Entry; @@ -33,6 +32,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.text.StrLookup; @@ -277,13 +277,9 @@ public class LoggingInterceptor extends InterceptorAdapter { } else { b.append('&'); } - try { - b.append(URLEncoder.encode(next.getKey(), "UTF-8")); - b.append('='); - b.append(URLEncoder.encode(nextValue, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new ca.uhn.fhir.context.ConfigurationException("UTF-8 not supported", e); - } + b.append(UrlUtil.escapeUrlParam(next.getKey())); + b.append('='); + b.append(UrlUtil.escapeUrlParam(nextValue)); } } return b.toString(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index d3955dbc71b..91278e5f874 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -92,9 +92,9 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter { } else { rawB.append('&'); } - rawB.append(UrlUtil.escape(next)); + rawB.append(UrlUtil.escapeUrlParam(next)); rawB.append('='); - rawB.append(UrlUtil.escape(nextValue)); + rawB.append(UrlUtil.escapeUrlParam(nextValue)); } } if (rawB.length() == 0) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java index 60820b85046..88b41a0eab1 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java @@ -186,9 +186,9 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi } else { b.append('&'); } - b.append(UrlUtil.escape(nextParamName)); + b.append(UrlUtil.escapeUrlParam(nextParamName)); b.append('='); - b.append(UrlUtil.escape(nextParamValue)); + b.append(UrlUtil.escapeUrlParam(nextParamValue)); } } } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/CompositeParameterTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/CompositeParameterTest.java index 74f4ae7abbe..3d46074c6cb 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/CompositeParameterTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/CompositeParameterTest.java @@ -1,12 +1,15 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; - -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.BundleEntry; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu.resource.Observation; +import ca.uhn.fhir.model.primitive.DateTimeDt; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -20,20 +23,11 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.BundleEntry; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu.resource.Observation; -import ca.uhn.fhir.model.primitive.DateTimeDt; -import ca.uhn.fhir.rest.annotation.RequiredParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.CompositeAndListParam; -import ca.uhn.fhir.rest.param.CompositeOrListParam; -import ca.uhn.fhir.rest.param.CompositeParam; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; /** * Created by dsotnikov on 2/25/2014. @@ -48,7 +42,7 @@ public class CompositeParameterTest { @Test public void testSearchWithDateValue() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("foo\\$bar$2001-01-01", "UTF-8")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + UrlUtil.escape("foo\\$bar$2001-01-01")); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -66,7 +60,7 @@ public class CompositeParameterTest { @Test public void testSearchWithMultipleValue() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_STRING + "=" + URLEncoder.encode("l1$r1,l2$r2", "UTF-8") + "&" + Observation.SP_NAME_VALUE_STRING + "=" + URLEncoder.encode("l3$r3,l4$r4", "UTF-8")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_STRING + "=" + UrlUtil.escape("l1$r1,l2$r2") + "&" + Observation.SP_NAME_VALUE_STRING + "=" + UrlUtil.escape("l3$r3,l4$r4")); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/DefaultEncodingTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/DefaultEncodingTest.java index bf1de077702..b104010e93e 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/DefaultEncodingTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/DefaultEncodingTest.java @@ -1,15 +1,14 @@ package ca.uhn.fhir.rest.server; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.Assert.*; - -import java.net.URLEncoder; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu.resource.Patient; +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.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; -import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; @@ -22,20 +21,11 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import com.google.common.net.UrlEscapers; +import java.util.concurrent.TimeUnit; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu.composite.IdentifierDt; -import ca.uhn.fhir.model.dstu.resource.BaseResource; -import ca.uhn.fhir.model.dstu.resource.Binary; -import ca.uhn.fhir.model.dstu.resource.Organization; -import ca.uhn.fhir.model.dstu.resource.Patient; -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.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.*; public class DefaultEncodingTest { diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReadDstu1Test.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReadDstu1Test.java index deeb32b5f86..78626745bdd 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReadDstu1Test.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReadDstu1Test.java @@ -1,11 +1,17 @@ package ca.uhn.fhir.rest.server; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -import java.net.URLEncoder; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu.composite.IdentifierDt; +import ca.uhn.fhir.model.dstu.resource.BaseResource; +import ca.uhn.fhir.model.dstu.resource.Binary; +import ca.uhn.fhir.model.dstu.resource.Organization; +import ca.uhn.fhir.model.dstu.resource.Patient; +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.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HttpResponse; @@ -20,18 +26,10 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu.composite.IdentifierDt; -import ca.uhn.fhir.model.dstu.resource.BaseResource; -import ca.uhn.fhir.model.dstu.resource.Binary; -import ca.uhn.fhir.model.dstu.resource.Organization; -import ca.uhn.fhir.model.dstu.resource.Patient; -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.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; public class ReadDstu1Test { @@ -129,10 +127,10 @@ public class ReadDstu1Test { @Test public void testReadWithEscapedCharsInId() throws Exception { String id = "ABC!@#$--DEF"; - String idEscaped = URLEncoder.encode(id, "UTF-8"); + String idEscaped = UrlUtil.escapeid); String vid = "GHI:/:/JKL"; - String vidEscaped = URLEncoder.encode(vid, "UTF-8"); + String vidEscaped = UrlUtil.escape(vid); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/" + idEscaped + "/_history/" + vidEscaped); HttpResponse status = ourClient.execute(httpGet); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReferenceParameterTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReferenceParameterTest.java index 8a742b5e9e5..3c5aa97435e 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReferenceParameterTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ReferenceParameterTest.java @@ -1,41 +1,12 @@ package ca.uhn.fhir.rest.server; -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; -import ca.uhn.fhir.model.dstu.resource.Conformance; +import ca.uhn.fhir.model.dstu.resource.*; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam; -import ca.uhn.fhir.model.dstu.resource.Location; -import ca.uhn.fhir.model.dstu.resource.Observation; -import ca.uhn.fhir.model.dstu.resource.Organization; -import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; @@ -47,6 +18,27 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.*; /** * Created by dsotnikov on 2/25/2014. @@ -105,7 +97,7 @@ public class ReferenceParameterTest { @Test public void testReferenceParamViewToken() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?provider.name=" + URLEncoder.encode("foo|bar", "UTF-8")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?provider.name=" + UrlUtil.escape("foo|bar")); HttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java index 8a5b7058401..030ef32baa2 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java @@ -105,7 +105,7 @@ public class OperationServerWithSearchParamTypesDstu2_1Test { @Test public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,7 +118,7 @@ public class OperationServerWithSearchParamTypesDstu2_1Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -270,7 +270,7 @@ public class OperationServerWithSearchParamTypesDstu2_1Test { } @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -290,7 +290,7 @@ public class OperationServerWithSearchParamTypesDstu2_1Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -345,7 +345,7 @@ public class OperationServerWithSearchParamTypesDstu2_1Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java index 633302c1b4d..08bd9b6f10a 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java @@ -110,8 +110,8 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" - + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -214,7 +214,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -234,7 +234,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -289,8 +289,8 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" - + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java index 7617f7838e9..36d9e0aba94 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java @@ -1,12 +1,14 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.assertEquals; - -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -22,14 +24,11 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.dstu2.resource.Patient; -import ca.uhn.fhir.rest.annotation.RequiredParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; public class ServerSearchDstu2Test { @@ -106,7 +105,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchWithEncodedValue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=" + URLEncoder.encode("Jernelöv", "UTF-8")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=" + UrlUtil.escapeUrlParam("Jernelöv")); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java index 3b2818641d3..cd2543b912c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java @@ -14,7 +14,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @@ -186,7 +185,7 @@ public class ResponseHighlightingInterceptorTest { @Test public void testForceApplicationJsonPlusFhir() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=" + UrlUtil.escape("application/json+fhir")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=" + UrlUtil.escapeUrlParam("application/json+fhir")); httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); HttpResponse status = ourClient.execute(httpGet); @@ -225,7 +224,7 @@ public class ResponseHighlightingInterceptorTest { @Test public void testForceApplicationXmlPlusFhir() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=" + UrlUtil.escape("application/xml+fhir")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=" + UrlUtil.escapeUrlParam("application/xml+fhir")); httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); HttpResponse status = ourClient.execute(httpGet); diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/MapTransformEnumFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/MapTransformEnumFactory.java index 64fc92fd21b..b4f17d41118 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/MapTransformEnumFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/MapTransformEnumFactory.java @@ -1,122 +1,122 @@ -package org.hl7.fhir.dstu3.model.codesystems; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 - - -import org.hl7.fhir.dstu3.model.EnumFactory; - -public class MapTransformEnumFactory implements EnumFactory { - - public MapTransform fromCode(String codeString) throws IllegalArgumentException { - if (codeString == null || "".equals(codeString)) - return null; - if ("create".equals(codeString)) - return MapTransform.CREATE; - if ("copy".equals(codeString)) - return MapTransform.COPY; - if ("truncate".equals(codeString)) - return MapTransform.TRUNCATE; - if ("escape".equals(codeString)) - return MapTransform.ESCAPE; - if ("cast".equals(codeString)) - return MapTransform.CAST; - if ("append".equals(codeString)) - return MapTransform.APPEND; - if ("translate".equals(codeString)) - return MapTransform.TRANSLATE; - if ("reference".equals(codeString)) - return MapTransform.REFERENCE; - if ("dateOp".equals(codeString)) - return MapTransform.DATEOP; - if ("uuid".equals(codeString)) - return MapTransform.UUID; - if ("pointer".equals(codeString)) - return MapTransform.POINTER; - if ("evaluate".equals(codeString)) - return MapTransform.EVALUATE; - if ("cc".equals(codeString)) - return MapTransform.CC; - if ("c".equals(codeString)) - return MapTransform.C; - if ("qty".equals(codeString)) - return MapTransform.QTY; - if ("id".equals(codeString)) - return MapTransform.ID; - if ("cp".equals(codeString)) - return MapTransform.CP; - throw new IllegalArgumentException("Unknown MapTransform code '"+codeString+"'"); - } - - public String toCode(MapTransform code) { - if (code == MapTransform.CREATE) - return "create"; - if (code == MapTransform.COPY) - return "copy"; - if (code == MapTransform.TRUNCATE) - return "truncate"; - if (code == MapTransform.ESCAPE) - return "escape"; - if (code == MapTransform.CAST) - return "cast"; - if (code == MapTransform.APPEND) - return "append"; - if (code == MapTransform.TRANSLATE) - return "translate"; - if (code == MapTransform.REFERENCE) - return "reference"; - if (code == MapTransform.DATEOP) - return "dateOp"; - if (code == MapTransform.UUID) - return "uuid"; - if (code == MapTransform.POINTER) - return "pointer"; - if (code == MapTransform.EVALUATE) - return "evaluate"; - if (code == MapTransform.CC) - return "cc"; - if (code == MapTransform.C) - return "c"; - if (code == MapTransform.QTY) - return "qty"; - if (code == MapTransform.ID) - return "id"; - if (code == MapTransform.CP) - return "cp"; - return "?"; - } - - public String toSystem(MapTransform code) { - return code.getSystem(); - } - -} - +package org.hl7.fhir.dstu3.model.codesystems; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 + + +import org.hl7.fhir.dstu3.model.EnumFactory; + +public class MapTransformEnumFactory implements EnumFactory { + + public MapTransform fromCode(String codeString) throws IllegalArgumentException { + if (codeString == null || "".equals(codeString)) + return null; + if ("create".equals(codeString)) + return MapTransform.CREATE; + if ("copy".equals(codeString)) + return MapTransform.COPY; + if ("truncate".equals(codeString)) + return MapTransform.TRUNCATE; + if ("escapeUrlParam".equals(codeString)) + return MapTransform.ESCAPE; + if ("cast".equals(codeString)) + return MapTransform.CAST; + if ("append".equals(codeString)) + return MapTransform.APPEND; + if ("translate".equals(codeString)) + return MapTransform.TRANSLATE; + if ("reference".equals(codeString)) + return MapTransform.REFERENCE; + if ("dateOp".equals(codeString)) + return MapTransform.DATEOP; + if ("uuid".equals(codeString)) + return MapTransform.UUID; + if ("pointer".equals(codeString)) + return MapTransform.POINTER; + if ("evaluate".equals(codeString)) + return MapTransform.EVALUATE; + if ("cc".equals(codeString)) + return MapTransform.CC; + if ("c".equals(codeString)) + return MapTransform.C; + if ("qty".equals(codeString)) + return MapTransform.QTY; + if ("id".equals(codeString)) + return MapTransform.ID; + if ("cp".equals(codeString)) + return MapTransform.CP; + throw new IllegalArgumentException("Unknown MapTransform code '"+codeString+"'"); + } + + public String toCode(MapTransform code) { + if (code == MapTransform.CREATE) + return "create"; + if (code == MapTransform.COPY) + return "copy"; + if (code == MapTransform.TRUNCATE) + return "truncate"; + if (code == MapTransform.ESCAPE) + return "escapeUrlParam"; + if (code == MapTransform.CAST) + return "cast"; + if (code == MapTransform.APPEND) + return "append"; + if (code == MapTransform.TRANSLATE) + return "translate"; + if (code == MapTransform.REFERENCE) + return "reference"; + if (code == MapTransform.DATEOP) + return "dateOp"; + if (code == MapTransform.UUID) + return "uuid"; + if (code == MapTransform.POINTER) + return "pointer"; + if (code == MapTransform.EVALUATE) + return "evaluate"; + if (code == MapTransform.CC) + return "cc"; + if (code == MapTransform.C) + return "c"; + if (code == MapTransform.QTY) + return "qty"; + if (code == MapTransform.ID) + return "id"; + if (code == MapTransform.CP) + return "cp"; + return "?"; + } + + public String toSystem(MapTransform code) { + return code.getSystem(); + } + +} + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java index 68235bea36a..9566989b0a0 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java @@ -105,7 +105,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,7 +118,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -270,7 +270,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { } @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -290,7 +290,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escape("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -345,7 +345,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escape("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escape("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java index a8a0128f563..351328a4c89 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java @@ -137,25 +137,25 @@ public class SearchDstu3Test { httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utilities/xml/XMLUtil.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utilities/xml/XMLUtil.java index c279b41c0f8..98c81ddc3ea 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utilities/xml/XMLUtil.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utilities/xml/XMLUtil.java @@ -1,406 +1,406 @@ -/* -Copyright (c) 2011+, HL7, Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ -package org.hl7.fhir.instance.utilities.xml; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.List; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.hl7.fhir.instance.utilities.Utilities; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; - -public class XMLUtil { - - public static final String SPACE_CHAR = "\u00A0"; - - public static boolean isNMToken(String name) { - if (name == null) - return false; - for (int i = 0; i < name.length(); i++) - if (!isNMTokenChar(name.charAt(i))) - return false; - return name.length() > 0; - } - - public static boolean isNMTokenChar(char c) { - return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); - } - - private static boolean isDigit(char c) { - return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || - (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || - (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || - (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || - (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); - } - - private static boolean isCombiningChar(char c) { - return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || - (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || - c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || - c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || - (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || - (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || - (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || - (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || - c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || - (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || - (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || - (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || - (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || - (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || - (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || - (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || - (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || - (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || - (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || - c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || - (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || - (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || - (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || - (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || - c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; - } - - private static boolean isExtender(char c) { - return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || - c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || - (c >= '\u30FC' && c <= '\u30FE'); - } - - private static boolean isLetter(char c) { - return isBaseChar(c) || isIdeographic(c); - } - - private static boolean isBaseChar(char c) { - return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || - (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || - (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || - (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || - (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || - c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || - (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || - c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || - (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || - (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || - (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || - c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || - (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || - (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || - c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || - (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || - (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || - (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || - (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || - (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || - (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || - c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || - (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || - (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || - (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || - (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || - c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || - (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || - (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || - (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || - (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || - (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || - (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || - (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || - c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || - (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || - (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || - (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || - c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || - (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || - (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || - (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || - (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || - (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || - (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || - (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || - c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || - (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || - (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || - c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || - (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || - c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || - (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || - (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || - (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || - c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || - (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || - (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || - (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || - (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || - (c >= '\uAC00' && c <= '\uD7A3'); - } - - private static boolean isIdeographic(char c) { - return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); - } - - public static String determineEncoding(InputStream stream) throws IOException { - stream.mark(20000); - try { - int b0 = stream.read(); - int b1 = stream.read(); - int b2 = stream.read(); - int b3 = stream.read(); - - if (b0 == 0xFE && b1 == 0xFF) - return "UTF-16BE"; - else if (b0 == 0xFF && b1 == 0xFE) - return "UTF-16LE"; - else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) - return "UTF-8"; - else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) - return "UTF-16BE"; - else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) - return "UTF-16LE"; - else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { -// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding -// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding -// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns -// for the relevant ASCII characters, the encoding declaration itself may be read reliably - InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); - String hdr = readFirstLine(rdr); - return extractEncoding(hdr); - } else - return null; - } finally { - stream.reset(); - } - } - - private static String extractEncoding(String hdr) { - int i = hdr.indexOf("encoding="); - if (i == -1) - return null; - hdr = hdr.substring(i+9); - char sep = hdr.charAt(0); - hdr = hdr.substring(1); - i = hdr.indexOf(sep); - if (i == -1) - return null; - return hdr.substring(0, i); - } - - private static String readFirstLine(InputStreamReader rdr) throws IOException { - char[] buf = new char[1]; - StringBuffer bldr = new StringBuffer(); - rdr.read(buf); - while (buf[0] != '>') { - bldr.append(buf[0]); - rdr.read(buf); - } - return bldr.toString(); - } - - - public static boolean charSetImpliesAscii(String charset) { - return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); - } - - - /** - * Converts the raw characters to XML escape characters. - * - * @param rawContent - * @param charset Null when charset is not known, so we assume it's unicode - * @param isNoLines - * @return escape string - */ - public static String escapeXML(String rawContent, String charset, boolean isNoLines) { - if (rawContent == null) - return ""; - else { - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < rawContent.length(); i++) { - char ch = rawContent.charAt(i); - if (ch == '\'') - sb.append("'"); - else if (ch == '&') - sb.append("&"); - else if (ch == '"') - sb.append("""); - else if (ch == '<') - sb.append("<"); - else if (ch == '>') - sb.append(">"); - else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) - // TODO - why is hashcode the only way to get the unicode number for the character - // in jre 5.0? - sb.append("&#x"+Integer.toHexString(new Character(ch).hashCode()).toUpperCase()+";"); - else if (isNoLines) { - if (ch == '\r') - sb.append(" "); - else if (ch != '\n') - sb.append(ch); - } - else - sb.append(ch); - } - return sb.toString(); - } - } - - public static Element getFirstChild(Element e) { - if (e == null) - return null; - Node n = e.getFirstChild(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getNextSibling(); - return (Element) n; - } - - public static Element getNamedChild(Element e, String name) { - Element c = getFirstChild(e); - while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) - c = getNextSibling(c); - return c; - } - - public static Element getNextSibling(Element e) { - Node n = e.getNextSibling(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getNextSibling(); - return (Element) n; - } - - public static void getNamedChildren(Element e, String name, List set) { - Element c = getFirstChild(e); - while (c != null) { - if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) - set.add(c); - c = getNextSibling(c); - } - } - - public static String htmlToXmlEscapedPlainText(Element r) { - StringBuilder s = new StringBuilder(); - Node n = r.getFirstChild(); - boolean ws = false; - while (n != null) { - if (n.getNodeType() == Node.TEXT_NODE) { - String t = n.getTextContent().trim(); - if (Utilities.noString(t)) - ws = true; - else { - if (ws) - s.append(" "); - ws = false; - s.append(t); - } - } - if (n.getNodeType() == Node.ELEMENT_NODE) { - if (ws) - s.append(" "); - ws = false; - s.append(htmlToXmlEscapedPlainText((Element) n)); - if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) - s.append("\r\n"); - } - n = n.getNextSibling(); - } - return s.toString(); - } - - public static String htmlToXmlEscapedPlainText(String definition) throws Exception { - return htmlToXmlEscapedPlainText(parseToDom("
    "+definition+"
    ").getDocumentElement()); - } - - public static String elementToString(Element el) { - if (el == null) - return ""; - Document document = el.getOwnerDocument(); - DOMImplementationLS domImplLS = (DOMImplementationLS) document - .getImplementation(); - LSSerializer serializer = domImplLS.createLSSerializer(); - return serializer.writeToString(el); - } - - public static String getNamedChildValue(Element element, String name) { - Element e = getNamedChild(element, name); - return e == null ? null : e.getAttribute("value"); - } - - public static void getNamedChildrenWithWildcard(Element focus, String name, List children) { - Element c = getFirstChild(focus); - while (c != null) { - String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); - if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) - children.add(c); - c = getNextSibling(c); - } - - } - - public static boolean hasNamedChild(Element e, String name) { - Element c = getFirstChild(e); - while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) - c = getNextSibling(c); - return c != null; - } - - public static Document parseToDom(String content) throws Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - return builder.parse(new ByteArrayInputStream(content.getBytes())); - } - - public static Element getLastChild(Element e) { - if (e == null) - return null; - Node n = e.getLastChild(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getPreviousSibling(); - return (Element) n; - } - - public static Element getPrevSibling(Element e) { - Node n = e.getPreviousSibling(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getPreviousSibling(); - return (Element) n; - } - - -} +/* +Copyright (c) 2011+, HL7, Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +package org.hl7.fhir.instance.utilities.xml; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.hl7.fhir.instance.utilities.Utilities; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +public class XMLUtil { + + public static final String SPACE_CHAR = "\u00A0"; + + public static boolean isNMToken(String name) { + if (name == null) + return false; + for (int i = 0; i < name.length(); i++) + if (!isNMTokenChar(name.charAt(i))) + return false; + return name.length() > 0; + } + + public static boolean isNMTokenChar(char c) { + return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); + } + + private static boolean isDigit(char c) { + return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || + (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || + (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || + (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || + (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); + } + + private static boolean isCombiningChar(char c) { + return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || + (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || + c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || + c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || + (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || + (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || + (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || + (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || + c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || + (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || + (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || + (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || + (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || + (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || + (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || + (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || + (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || + (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || + (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || + c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || + (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || + (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || + (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || + (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || + c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; + } + + private static boolean isExtender(char c) { + return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || + c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || + (c >= '\u30FC' && c <= '\u30FE'); + } + + private static boolean isLetter(char c) { + return isBaseChar(c) || isIdeographic(c); + } + + private static boolean isBaseChar(char c) { + return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || + (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || + (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || + (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || + (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || + c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || + (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || + c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || + (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || + (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || + (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || + c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || + (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || + (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || + c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || + (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || + (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || + (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || + (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || + (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || + (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || + c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || + (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || + (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || + (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || + (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || + c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || + (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || + (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || + (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || + (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || + (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || + (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || + (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || + c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || + (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || + (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || + (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || + c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || + (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || + (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || + (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || + (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || + (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || + (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || + (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || + c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || + (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || + (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || + c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || + (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || + c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || + (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || + (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || + (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || + c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || + (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || + (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || + (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || + (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || + (c >= '\uAC00' && c <= '\uD7A3'); + } + + private static boolean isIdeographic(char c) { + return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); + } + + public static String determineEncoding(InputStream stream) throws IOException { + stream.mark(20000); + try { + int b0 = stream.read(); + int b1 = stream.read(); + int b2 = stream.read(); + int b3 = stream.read(); + + if (b0 == 0xFE && b1 == 0xFF) + return "UTF-16BE"; + else if (b0 == 0xFF && b1 == 0xFE) + return "UTF-16LE"; + else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) + return "UTF-8"; + else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) + return "UTF-16BE"; + else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) + return "UTF-16LE"; + else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { +// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding +// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding +// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns +// for the relevant ASCII characters, the encoding declaration itself may be read reliably + InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); + String hdr = readFirstLine(rdr); + return extractEncoding(hdr); + } else + return null; + } finally { + stream.reset(); + } + } + + private static String extractEncoding(String hdr) { + int i = hdr.indexOf("encoding="); + if (i == -1) + return null; + hdr = hdr.substring(i+9); + char sep = hdr.charAt(0); + hdr = hdr.substring(1); + i = hdr.indexOf(sep); + if (i == -1) + return null; + return hdr.substring(0, i); + } + + private static String readFirstLine(InputStreamReader rdr) throws IOException { + char[] buf = new char[1]; + StringBuffer bldr = new StringBuffer(); + rdr.read(buf); + while (buf[0] != '>') { + bldr.append(buf[0]); + rdr.read(buf); + } + return bldr.toString(); + } + + + public static boolean charSetImpliesAscii(String charset) { + return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); + } + + + /** + * Converts the raw characters to XML escapeUrlParam characters. + * + * @param rawContent + * @param charset Null when charset is not known, so we assume it's unicode + * @param isNoLines + * @return escapeUrlParam string + */ + public static String escapeXML(String rawContent, String charset, boolean isNoLines) { + if (rawContent == null) + return ""; + else { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < rawContent.length(); i++) { + char ch = rawContent.charAt(i); + if (ch == '\'') + sb.append("'"); + else if (ch == '&') + sb.append("&"); + else if (ch == '"') + sb.append("""); + else if (ch == '<') + sb.append("<"); + else if (ch == '>') + sb.append(">"); + else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) + // TODO - why is hashcode the only way to get the unicode number for the character + // in jre 5.0? + sb.append("&#x"+Integer.toHexString(new Character(ch).hashCode()).toUpperCase()+";"); + else if (isNoLines) { + if (ch == '\r') + sb.append(" "); + else if (ch != '\n') + sb.append(ch); + } + else + sb.append(ch); + } + return sb.toString(); + } + } + + public static Element getFirstChild(Element e) { + if (e == null) + return null; + Node n = e.getFirstChild(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getNextSibling(); + return (Element) n; + } + + public static Element getNamedChild(Element e, String name) { + Element c = getFirstChild(e); + while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) + c = getNextSibling(c); + return c; + } + + public static Element getNextSibling(Element e) { + Node n = e.getNextSibling(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getNextSibling(); + return (Element) n; + } + + public static void getNamedChildren(Element e, String name, List set) { + Element c = getFirstChild(e); + while (c != null) { + if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) + set.add(c); + c = getNextSibling(c); + } + } + + public static String htmlToXmlEscapedPlainText(Element r) { + StringBuilder s = new StringBuilder(); + Node n = r.getFirstChild(); + boolean ws = false; + while (n != null) { + if (n.getNodeType() == Node.TEXT_NODE) { + String t = n.getTextContent().trim(); + if (Utilities.noString(t)) + ws = true; + else { + if (ws) + s.append(" "); + ws = false; + s.append(t); + } + } + if (n.getNodeType() == Node.ELEMENT_NODE) { + if (ws) + s.append(" "); + ws = false; + s.append(htmlToXmlEscapedPlainText((Element) n)); + if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) + s.append("\r\n"); + } + n = n.getNextSibling(); + } + return s.toString(); + } + + public static String htmlToXmlEscapedPlainText(String definition) throws Exception { + return htmlToXmlEscapedPlainText(parseToDom("
    "+definition+"
    ").getDocumentElement()); + } + + public static String elementToString(Element el) { + if (el == null) + return ""; + Document document = el.getOwnerDocument(); + DOMImplementationLS domImplLS = (DOMImplementationLS) document + .getImplementation(); + LSSerializer serializer = domImplLS.createLSSerializer(); + return serializer.writeToString(el); + } + + public static String getNamedChildValue(Element element, String name) { + Element e = getNamedChild(element, name); + return e == null ? null : e.getAttribute("value"); + } + + public static void getNamedChildrenWithWildcard(Element focus, String name, List children) { + Element c = getFirstChild(focus); + while (c != null) { + String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); + if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) + children.add(c); + c = getNextSibling(c); + } + + } + + public static boolean hasNamedChild(Element e, String name) { + Element c = getFirstChild(e); + while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) + c = getNextSibling(c); + return c != null; + } + + public static Document parseToDom(String content) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(new ByteArrayInputStream(content.getBytes())); + } + + public static Element getLastChild(Element e) { + if (e == null) + return null; + Node n = e.getLastChild(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getPreviousSibling(); + return (Element) n; + } + + public static Element getPrevSibling(Element e) { + Node n = e.getPreviousSibling(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getPreviousSibling(); + return (Element) n; + } + + +} diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/elementmodel/VerticalBarParser.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/elementmodel/VerticalBarParser.java index 40181ec4472..45e39593a35 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/elementmodel/VerticalBarParser.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/elementmodel/VerticalBarParser.java @@ -1,493 +1,493 @@ -package org.hl7.fhir.r4.elementmodel; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; - -import org.hl7.fhir.r4.context.IWorkerContext; -import org.hl7.fhir.r4.formats.IParser.OutputStyle; -import org.hl7.fhir.r4.model.StructureDefinition; -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; - -/** - * This class provides special support for parsing v2 by the v2 logical model - * For the logical model, see the FHIRPath spec - * - * @author Grahame Grieve - * - */ -public class VerticalBarParser extends ParserBase { - - /** - * Delimiters for a message. Note that the application rarely needs to concern - * itself with this information; it mainly exists for internal use. However if - * a message is being written to a spec that calls for non-standard delimiters, - * the application can set them here. - * - * @author Grahame - * - */ - public class Delimiters { - - /** - * Hl7 defined default delimiter for a field - */ - public final static char DEFAULT_DELIMITER_FIELD = '|'; - - /** - * Hl7 defined default delimiter for a component - */ - public final static char DEFAULT_DELIMITER_COMPONENT = '^'; - - /** - * Hl7 defined default delimiter for a subcomponent - */ - public final static char DEFAULT_DELIMITER_SUBCOMPONENT = '&'; - - /** - * Hl7 defined default delimiter for a repeat - */ - public final static char DEFAULT_DELIMITER_REPETITION = '~'; - - /** - * Hl7 defined default delimiter for an escape - */ - public final static char DEFAULT_CHARACTER_ESCAPE = '\\'; - - - /** - * defined escape character for this message - */ - private char escapeCharacter; - - /** - * defined repetition character for this message - */ - private char repetitionDelimiter; - - /** - * defined field character for this message - */ - private char fieldDelimiter; - - /** - * defined subComponent character for this message - */ - private char subComponentDelimiter; - - /** - * defined component character for this message - */ - private char componentDelimiter; - - /** - * create - * - */ - public Delimiters() { - super(); - reset(); - } - - public boolean matches(Delimiters other) { - return escapeCharacter == other.escapeCharacter && - repetitionDelimiter == other.repetitionDelimiter && - fieldDelimiter == other.fieldDelimiter && - subComponentDelimiter == other.subComponentDelimiter && - componentDelimiter == other.componentDelimiter; - } - - /** - * get defined component character for this message - * @return - */ - public char getComponentDelimiter() { - return componentDelimiter; - } - - /** - * set defined component character for this message - * @param componentDelimiter - */ - public void setComponentDelimiter(char componentDelimiter) { - this.componentDelimiter = componentDelimiter; - } - - /** - * get defined escape character for this message - * @return - */ - public char getEscapeCharacter() { - return escapeCharacter; - } - - /** - * set defined escape character for this message - * @param escapeCharacter - */ - public void setEscapeCharacter(char escapeCharacter) { - this.escapeCharacter = escapeCharacter; - } - - /** - * get defined field character for this message - * @return - */ - public char getFieldDelimiter() { - return fieldDelimiter; - } - - /** - * set defined field character for this message - * @param fieldDelimiter - */ - public void setFieldDelimiter(char fieldDelimiter) { - this.fieldDelimiter = fieldDelimiter; - } - - /** - * get repeat field character for this message - * @return - */ - public char getRepetitionDelimiter() { - return repetitionDelimiter; - } - - /** - * set repeat field character for this message - * @param repetitionDelimiter - */ - public void setRepetitionDelimiter(char repetitionDelimiter) { - this.repetitionDelimiter = repetitionDelimiter; - } - - /** - * get sub-component field character for this message - * @return - */ - public char getSubComponentDelimiter() { - return subComponentDelimiter; - } - - /** - * set sub-component field character for this message - * @param subComponentDelimiter - */ - public void setSubComponentDelimiter(char subComponentDelimiter) { - this.subComponentDelimiter = subComponentDelimiter; - } - - /** - * reset to default HL7 values - * - */ - public void reset () { - fieldDelimiter = DEFAULT_DELIMITER_FIELD; - componentDelimiter = DEFAULT_DELIMITER_COMPONENT; - subComponentDelimiter = DEFAULT_DELIMITER_SUBCOMPONENT; - repetitionDelimiter = DEFAULT_DELIMITER_REPETITION; - escapeCharacter = DEFAULT_CHARACTER_ESCAPE; - } - - /** - * check that the delimiters are valid - * - * @throws FHIRException - */ - public void check() throws FHIRException { - rule(componentDelimiter != fieldDelimiter, "Delimiter Error: \""+componentDelimiter+"\" is used for both CPComponent and CPField"); - rule(subComponentDelimiter != fieldDelimiter, "Delimiter Error: \""+subComponentDelimiter+"\" is used for both CPSubComponent and CPField"); - rule(subComponentDelimiter != componentDelimiter, "Delimiter Error: \""+subComponentDelimiter+"\" is used for both CPSubComponent and CPComponent"); - rule(repetitionDelimiter != fieldDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPField"); - rule(repetitionDelimiter != componentDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPComponent"); - rule(repetitionDelimiter != subComponentDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPSubComponent"); - rule(escapeCharacter != fieldDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPField"); - rule(escapeCharacter != componentDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPComponent"); - rule(escapeCharacter != subComponentDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPSubComponent"); - rule(escapeCharacter != repetitionDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and Repetition"); - } - - /** - * check to see whether ch is a delimiter character (vertical bar parser support) - * @param ch - * @return - */ - public boolean isDelimiter(char ch) { - return ch == escapeCharacter || ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter || ch == componentDelimiter; - } - - /** - * check to see whether ch is a cell delimiter char (vertical bar parser support) - * @param ch - * @return - */ - public boolean isCellDelimiter(char ch) { - return ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter || ch == componentDelimiter; - } - - /** - * get the escape for a character - * @param ch - * @return - */ - public String getEscape(char ch) { - if (ch == escapeCharacter) - return escapeCharacter + "E" + escapeCharacter; - else if (ch == fieldDelimiter) - return escapeCharacter + "F" + escapeCharacter; - else if (ch == componentDelimiter) - return escapeCharacter + "S" + escapeCharacter; - else if (ch == subComponentDelimiter) - return escapeCharacter + "T" + escapeCharacter; - else if (ch == repetitionDelimiter) - return escapeCharacter + "R" + escapeCharacter; - else - return null; - } - - /** - * build the MSH-2 content - * @return - */ - public String forMSH2() { - return "" + componentDelimiter + repetitionDelimiter + escapeCharacter + subComponentDelimiter; - } - - /** - * check to see whether ch represents a delimiter escape - * @param ch - * @return - */ - public boolean isDelimiterEscape(char ch) { - return ch == 'F' || ch == 'S' || ch == 'E' || ch == 'T' || ch == 'R'; - } - - /** - * get escape for ch in an escape - * @param ch - * @return - * @throws DefinitionException - * @throws FHIRException - */ - public char getDelimiterEscapeChar(char ch) throws DefinitionException { - if (ch == 'E') - return escapeCharacter; - else if (ch == 'F') - return fieldDelimiter; - else if (ch == 'S') - return componentDelimiter; - else if (ch == 'T') - return subComponentDelimiter; - else if (ch == 'R') - return repetitionDelimiter; - else - throw new DefinitionException("internal error in getDelimiterEscapeChar"); - } - } - - public class VerticalBarParserReader { - - - private BufferedInputStream stream; - private String charsetName; - private InputStreamReader reader = null; - private boolean finished; - private char peeked; - private char lastValue; - private int offset; - private int lineNumber; - - public VerticalBarParserReader(BufferedInputStream stream, String charsetName) throws FHIRException { - super(); - setStream(stream); - setCharsetName(charsetName); - open(); - } - - public String getCharsetName() { - return charsetName; - } - - public void setCharsetName(String charsetName) { - this.charsetName = charsetName; - } - - public BufferedInputStream getStream() { - return stream; - } - - public void setStream(BufferedInputStream stream) { - this.stream = stream; - } - - private void open() throws FHIRException { - try { - stream.mark(2048); - reader = new InputStreamReader(stream, charsetName); - offset = 0; - lineNumber = 0; - lastValue = ' '; - next(); - } catch (Exception e) { - throw new FHIRException(e); - } - } - - private void next() throws IOException, FHIRException { - finished = !reader.ready(); - if (!finished) { - char[] temp = new char[1]; - rule(reader.read(temp, 0, 1) == 1, "unable to read 1 character from the stream"); - peeked = temp[0]; - } - } - - public String read(int charCount) throws FHIRException { - String value = ""; - for (int i = 0; i < charCount; i++) - value = value + read(); - return value; - } - - public void skipEOL () throws FHIRException { - while (!finished && (peek() == '\r' || peek() == '\n')) - read(); - } - - public char read () throws FHIRException { - rule(!finished, "No more content to read"); - char value = peek(); - offset++; - if (value == '\r' || value == '\n') { - if (lastValue != '\r' || value != '\n') - lineNumber++; - } - lastValue = value; - try { - next(); - } catch (Exception e) { - throw new FHIRException(e); - } - return value; - } - - public boolean isFinished () { - return finished; - } - - public char peek() throws FHIRException { - rule(!finished, "Cannot peek"); - return peeked; - } - - public void mark() { - stream.mark(2048); - } - - public void reset() throws FHIRException { - try { - stream.reset(); - } catch (IOException e) { - throw new FHIRException(e); - } - open(); - } - - public boolean IsEOL() throws FHIRException { - return peek() == '\r' || peek() == '\n'; - } - - public int getLineNumber() { - return lineNumber; - } - - public int getOffset() { - return offset; - } - - } - - public VerticalBarParser(IWorkerContext context) { - super(context); - } - - private String charset = "ASCII"; - private Delimiters delimiters = new Delimiters(); - - @Override - public Element parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message"); - Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd)); - VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset); - - preDecode(reader); - while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size())) - readSegment(message, reader); - - return message; - } - - private void preDecode(VerticalBarParserReader reader) throws FHIRException { - reader.skipEOL(); - String temp = reader.read(3); - rule(temp.equals("MSH") || temp.equals("FHS"), "Found '" + temp + "' looking for 'MSH' or 'FHS'"); - readDelimiters(reader); - // readVersion(message); - probably don't need to do that? - // readCharacterSet(); - reader.reset(); // ready to read message now - } - - private void rule(boolean test, String msg) throws FHIRException { - if (!test) - throw new FHIRException(msg); - } - - private void readDelimiters(VerticalBarParserReader reader) throws FHIRException { - delimiters.setFieldDelimiter(reader.read()); - if (!(reader.peek() == delimiters.getFieldDelimiter())) - delimiters.setComponentDelimiter(reader.read()); - if (!(reader.peek() == delimiters.getFieldDelimiter())) - delimiters.setRepetitionDelimiter(reader.read()); - if (!(reader.peek() == delimiters.getFieldDelimiter())) - delimiters.setEscapeCharacter(reader.read()); - if (!(reader.peek() == delimiters.getFieldDelimiter())) - delimiters.setSubComponentDelimiter(reader.read()); - delimiters.check(); - } - - private void readSegment(Element message, VerticalBarParserReader reader) throws FHIRException { - Element segment = new Element("segment", message.getProperty().getChild("segment")); - message.getChildren().add(segment); - Element segmentCode = new Element("code", segment.getProperty().getChild("code")); - segment.getChildren().add(segmentCode); - segmentCode.setValue(reader.read(3)); - - int index = 0; - while (!reader.isFinished() && !reader.IsEOL()) { - index++; - readField(reader, segment, index); - if (!reader.isFinished() && !reader.IsEOL()) - rule(reader.read() == delimiters.getFieldDelimiter(), "Expected to find field delimiter"); - } - if (!reader.isFinished()) - reader.skipEOL(); - } - - - - private void readField(VerticalBarParserReader reader, Element segment, int index) { - // TODO Auto-generated method stub - - } - - @Override - public void compose(Element e, OutputStream destination, OutputStyle style, String base) { - // TODO Auto-generated method stub - - } - -} +package org.hl7.fhir.r4.elementmodel; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import org.hl7.fhir.r4.context.IWorkerContext; +import org.hl7.fhir.r4.formats.IParser.OutputStyle; +import org.hl7.fhir.r4.model.StructureDefinition; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; + +/** + * This class provides special support for parsing v2 by the v2 logical model + * For the logical model, see the FHIRPath spec + * + * @author Grahame Grieve + * + */ +public class VerticalBarParser extends ParserBase { + + /** + * Delimiters for a message. Note that the application rarely needs to concern + * itself with this information; it mainly exists for internal use. However if + * a message is being written to a spec that calls for non-standard delimiters, + * the application can set them here. + * + * @author Grahame + * + */ + public class Delimiters { + + /** + * Hl7 defined default delimiter for a field + */ + public final static char DEFAULT_DELIMITER_FIELD = '|'; + + /** + * Hl7 defined default delimiter for a component + */ + public final static char DEFAULT_DELIMITER_COMPONENT = '^'; + + /** + * Hl7 defined default delimiter for a subcomponent + */ + public final static char DEFAULT_DELIMITER_SUBCOMPONENT = '&'; + + /** + * Hl7 defined default delimiter for a repeat + */ + public final static char DEFAULT_DELIMITER_REPETITION = '~'; + + /** + * Hl7 defined default delimiter for an escapeUrlParam + */ + public final static char DEFAULT_CHARACTER_ESCAPE = '\\'; + + + /** + * defined escapeUrlParam character for this message + */ + private char escapeCharacter; + + /** + * defined repetition character for this message + */ + private char repetitionDelimiter; + + /** + * defined field character for this message + */ + private char fieldDelimiter; + + /** + * defined subComponent character for this message + */ + private char subComponentDelimiter; + + /** + * defined component character for this message + */ + private char componentDelimiter; + + /** + * create + * + */ + public Delimiters() { + super(); + reset(); + } + + public boolean matches(Delimiters other) { + return escapeCharacter == other.escapeCharacter && + repetitionDelimiter == other.repetitionDelimiter && + fieldDelimiter == other.fieldDelimiter && + subComponentDelimiter == other.subComponentDelimiter && + componentDelimiter == other.componentDelimiter; + } + + /** + * get defined component character for this message + * @return + */ + public char getComponentDelimiter() { + return componentDelimiter; + } + + /** + * set defined component character for this message + * @param componentDelimiter + */ + public void setComponentDelimiter(char componentDelimiter) { + this.componentDelimiter = componentDelimiter; + } + + /** + * get defined escapeUrlParam character for this message + * @return + */ + public char getEscapeCharacter() { + return escapeCharacter; + } + + /** + * set defined escapeUrlParam character for this message + * @param escapeCharacter + */ + public void setEscapeCharacter(char escapeCharacter) { + this.escapeCharacter = escapeCharacter; + } + + /** + * get defined field character for this message + * @return + */ + public char getFieldDelimiter() { + return fieldDelimiter; + } + + /** + * set defined field character for this message + * @param fieldDelimiter + */ + public void setFieldDelimiter(char fieldDelimiter) { + this.fieldDelimiter = fieldDelimiter; + } + + /** + * get repeat field character for this message + * @return + */ + public char getRepetitionDelimiter() { + return repetitionDelimiter; + } + + /** + * set repeat field character for this message + * @param repetitionDelimiter + */ + public void setRepetitionDelimiter(char repetitionDelimiter) { + this.repetitionDelimiter = repetitionDelimiter; + } + + /** + * get sub-component field character for this message + * @return + */ + public char getSubComponentDelimiter() { + return subComponentDelimiter; + } + + /** + * set sub-component field character for this message + * @param subComponentDelimiter + */ + public void setSubComponentDelimiter(char subComponentDelimiter) { + this.subComponentDelimiter = subComponentDelimiter; + } + + /** + * reset to default HL7 values + * + */ + public void reset () { + fieldDelimiter = DEFAULT_DELIMITER_FIELD; + componentDelimiter = DEFAULT_DELIMITER_COMPONENT; + subComponentDelimiter = DEFAULT_DELIMITER_SUBCOMPONENT; + repetitionDelimiter = DEFAULT_DELIMITER_REPETITION; + escapeCharacter = DEFAULT_CHARACTER_ESCAPE; + } + + /** + * check that the delimiters are valid + * + * @throws FHIRException + */ + public void check() throws FHIRException { + rule(componentDelimiter != fieldDelimiter, "Delimiter Error: \""+componentDelimiter+"\" is used for both CPComponent and CPField"); + rule(subComponentDelimiter != fieldDelimiter, "Delimiter Error: \""+subComponentDelimiter+"\" is used for both CPSubComponent and CPField"); + rule(subComponentDelimiter != componentDelimiter, "Delimiter Error: \""+subComponentDelimiter+"\" is used for both CPSubComponent and CPComponent"); + rule(repetitionDelimiter != fieldDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPField"); + rule(repetitionDelimiter != componentDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPComponent"); + rule(repetitionDelimiter != subComponentDelimiter, "Delimiter Error: \""+repetitionDelimiter+"\" is used for both Repetition and CPSubComponent"); + rule(escapeCharacter != fieldDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPField"); + rule(escapeCharacter != componentDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPComponent"); + rule(escapeCharacter != subComponentDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and CPSubComponent"); + rule(escapeCharacter != repetitionDelimiter, "Delimiter Error: \""+escapeCharacter+"\" is used for both Escape and Repetition"); + } + + /** + * check to see whether ch is a delimiter character (vertical bar parser support) + * @param ch + * @return + */ + public boolean isDelimiter(char ch) { + return ch == escapeCharacter || ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter || ch == componentDelimiter; + } + + /** + * check to see whether ch is a cell delimiter char (vertical bar parser support) + * @param ch + * @return + */ + public boolean isCellDelimiter(char ch) { + return ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter || ch == componentDelimiter; + } + + /** + * get the escapeUrlParam for a character + * @param ch + * @return + */ + public String getEscape(char ch) { + if (ch == escapeCharacter) + return escapeCharacter + "E" + escapeCharacter; + else if (ch == fieldDelimiter) + return escapeCharacter + "F" + escapeCharacter; + else if (ch == componentDelimiter) + return escapeCharacter + "S" + escapeCharacter; + else if (ch == subComponentDelimiter) + return escapeCharacter + "T" + escapeCharacter; + else if (ch == repetitionDelimiter) + return escapeCharacter + "R" + escapeCharacter; + else + return null; + } + + /** + * build the MSH-2 content + * @return + */ + public String forMSH2() { + return "" + componentDelimiter + repetitionDelimiter + escapeCharacter + subComponentDelimiter; + } + + /** + * check to see whether ch represents a delimiter escapeUrlParam + * @param ch + * @return + */ + public boolean isDelimiterEscape(char ch) { + return ch == 'F' || ch == 'S' || ch == 'E' || ch == 'T' || ch == 'R'; + } + + /** + * get escapeUrlParam for ch in an escapeUrlParam + * @param ch + * @return + * @throws DefinitionException + * @throws FHIRException + */ + public char getDelimiterEscapeChar(char ch) throws DefinitionException { + if (ch == 'E') + return escapeCharacter; + else if (ch == 'F') + return fieldDelimiter; + else if (ch == 'S') + return componentDelimiter; + else if (ch == 'T') + return subComponentDelimiter; + else if (ch == 'R') + return repetitionDelimiter; + else + throw new DefinitionException("internal error in getDelimiterEscapeChar"); + } + } + + public class VerticalBarParserReader { + + + private BufferedInputStream stream; + private String charsetName; + private InputStreamReader reader = null; + private boolean finished; + private char peeked; + private char lastValue; + private int offset; + private int lineNumber; + + public VerticalBarParserReader(BufferedInputStream stream, String charsetName) throws FHIRException { + super(); + setStream(stream); + setCharsetName(charsetName); + open(); + } + + public String getCharsetName() { + return charsetName; + } + + public void setCharsetName(String charsetName) { + this.charsetName = charsetName; + } + + public BufferedInputStream getStream() { + return stream; + } + + public void setStream(BufferedInputStream stream) { + this.stream = stream; + } + + private void open() throws FHIRException { + try { + stream.mark(2048); + reader = new InputStreamReader(stream, charsetName); + offset = 0; + lineNumber = 0; + lastValue = ' '; + next(); + } catch (Exception e) { + throw new FHIRException(e); + } + } + + private void next() throws IOException, FHIRException { + finished = !reader.ready(); + if (!finished) { + char[] temp = new char[1]; + rule(reader.read(temp, 0, 1) == 1, "unable to read 1 character from the stream"); + peeked = temp[0]; + } + } + + public String read(int charCount) throws FHIRException { + String value = ""; + for (int i = 0; i < charCount; i++) + value = value + read(); + return value; + } + + public void skipEOL () throws FHIRException { + while (!finished && (peek() == '\r' || peek() == '\n')) + read(); + } + + public char read () throws FHIRException { + rule(!finished, "No more content to read"); + char value = peek(); + offset++; + if (value == '\r' || value == '\n') { + if (lastValue != '\r' || value != '\n') + lineNumber++; + } + lastValue = value; + try { + next(); + } catch (Exception e) { + throw new FHIRException(e); + } + return value; + } + + public boolean isFinished () { + return finished; + } + + public char peek() throws FHIRException { + rule(!finished, "Cannot peek"); + return peeked; + } + + public void mark() { + stream.mark(2048); + } + + public void reset() throws FHIRException { + try { + stream.reset(); + } catch (IOException e) { + throw new FHIRException(e); + } + open(); + } + + public boolean IsEOL() throws FHIRException { + return peek() == '\r' || peek() == '\n'; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getOffset() { + return offset; + } + + } + + public VerticalBarParser(IWorkerContext context) { + super(context); + } + + private String charset = "ASCII"; + private Delimiters delimiters = new Delimiters(); + + @Override + public Element parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message"); + Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd)); + VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset); + + preDecode(reader); + while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size())) + readSegment(message, reader); + + return message; + } + + private void preDecode(VerticalBarParserReader reader) throws FHIRException { + reader.skipEOL(); + String temp = reader.read(3); + rule(temp.equals("MSH") || temp.equals("FHS"), "Found '" + temp + "' looking for 'MSH' or 'FHS'"); + readDelimiters(reader); + // readVersion(message); - probably don't need to do that? + // readCharacterSet(); + reader.reset(); // ready to read message now + } + + private void rule(boolean test, String msg) throws FHIRException { + if (!test) + throw new FHIRException(msg); + } + + private void readDelimiters(VerticalBarParserReader reader) throws FHIRException { + delimiters.setFieldDelimiter(reader.read()); + if (!(reader.peek() == delimiters.getFieldDelimiter())) + delimiters.setComponentDelimiter(reader.read()); + if (!(reader.peek() == delimiters.getFieldDelimiter())) + delimiters.setRepetitionDelimiter(reader.read()); + if (!(reader.peek() == delimiters.getFieldDelimiter())) + delimiters.setEscapeCharacter(reader.read()); + if (!(reader.peek() == delimiters.getFieldDelimiter())) + delimiters.setSubComponentDelimiter(reader.read()); + delimiters.check(); + } + + private void readSegment(Element message, VerticalBarParserReader reader) throws FHIRException { + Element segment = new Element("segment", message.getProperty().getChild("segment")); + message.getChildren().add(segment); + Element segmentCode = new Element("code", segment.getProperty().getChild("code")); + segment.getChildren().add(segmentCode); + segmentCode.setValue(reader.read(3)); + + int index = 0; + while (!reader.isFinished() && !reader.IsEOL()) { + index++; + readField(reader, segment, index); + if (!reader.isFinished() && !reader.IsEOL()) + rule(reader.read() == delimiters.getFieldDelimiter(), "Expected to find field delimiter"); + } + if (!reader.isFinished()) + reader.skipEOL(); + } + + + + private void readField(VerticalBarParserReader reader, Element segment, int index) { + // TODO Auto-generated method stub + + } + + @Override + public void compose(Element e, OutputStream destination, OutputStyle style, String base) { + // TODO Auto-generated method stub + + } + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java index 97ee6c306e1..60c788985dd 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java @@ -1,21 +1,35 @@ package ca.uhn.fhir.rest.client; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.InputStream; -import java.io.StringReader; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.*; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest; +import ca.uhn.fhir.rest.client.api.IBasicClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; +import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; -import org.apache.http.*; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicStatusLine; import org.hamcrest.core.StringContains; @@ -23,29 +37,23 @@ import org.hamcrest.core.StringEndsWith; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import com.google.common.base.Charsets; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.util.*; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.api.annotation.ResourceDef; -import ca.uhn.fhir.model.base.resource.BaseConformance; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest; -import ca.uhn.fhir.rest.client.api.IBasicClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; -import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.server.exceptions.*; -import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.UrlUtil; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ClientR4Test { @@ -569,7 +577,7 @@ public class ClientR4Test { DateParam date = new DateParam("2001-01-01"); client.getObservationByNameValueDate(new CompositeParam(str, date)); - assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01", "UTF-8"), capt.getValue().getURI().toString()); + assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + UrlUtil.escapeUrlParam("FOO\\$BAR$2001-01-01"), capt.getValue().getURI().toString()); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java index 34579616a06..1565d6f4fcb 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java @@ -1,45 +1,6 @@ package ca.uhn.fhir.rest.client; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.*; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.*; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.input.ReaderInputStream; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.*; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicStatusLine; -import org.hamcrest.Matchers; -import org.hamcrest.core.StringContains; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.hl7.fhir.r4.model.Bundle.HTTPVerb; -import org.junit.*; -import org.mockito.ArgumentCaptor; -import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.*; -import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.client.api.IGenericClient; @@ -49,7 +10,43 @@ import ca.uhn.fhir.rest.client.impl.BaseClient; import ca.uhn.fhir.rest.client.impl.GenericClient; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.*; +import ca.uhn.fhir.util.BundleUtil; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.hamcrest.Matchers; +import org.hamcrest.core.StringContains; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle.BundleType; +import org.hl7.fhir.r4.model.Bundle.HTTPVerb; +import org.junit.*; +import org.mockito.ArgumentCaptor; +import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.Charset; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class GenericClientTest { @@ -782,7 +779,7 @@ public class GenericClientTest { .returnBundle(Bundle.class) .execute(); - assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01", "UTF-8"), capt.getValue().getURI().toString()); + assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + UrlUtil.escapeUrlParam("FOO\\$BAR$2001-01-01"), capt.getValue().getURI().toString()); } @@ -1025,7 +1022,7 @@ public class GenericClientTest { .returnBundle(Bundle.class) .execute(); - assertEquals("http://example.com/fhir/Patient?name=" + URLEncoder.encode("AAA,BBB,C\\,C", "UTF-8"), capt.getAllValues().get(1).getURI().toString()); + assertEquals("http://example.com/fhir/Patient?name=" + UrlUtil.escapeUrlParam("AAA,BBB,C\\,C"), capt.getAllValues().get(1).getURI().toString()); } @@ -1116,7 +1113,7 @@ public class GenericClientTest { .returnBundle(Bundle.class) .execute(); - assertEquals("http://example.com/fhir/Patient?identifier=" + URLEncoder.encode("A|B,C|D", "UTF-8"), capt.getAllValues().get(2).getURI().toString()); + assertEquals("http://example.com/fhir/Patient?identifier=" + UrlUtil.escapeUrlParam("A|B,C|D"), capt.getAllValues().get(2).getURI().toString()); } @@ -1152,7 +1149,7 @@ public class GenericClientTest { String url = capt.getAllValues().get(index).getURI().toString(); assertThat(url, Matchers.startsWith(wantPrefix)); assertEquals(wantValue, UrlUtil.unescape(url.substring(wantPrefix.length()))); - assertEquals(UrlUtil.escape(wantValue), url.substring(wantPrefix.length())); + assertEquals(UrlUtil.escapeUrlParam(wantValue), url.substring(wantPrefix.length())); index++; response = client.search() @@ -1164,7 +1161,7 @@ public class GenericClientTest { url = capt.getAllValues().get(index).getURI().toString(); assertThat(url, Matchers.startsWith(wantPrefix)); assertEquals(wantValue, UrlUtil.unescape(url.substring(wantPrefix.length()))); - assertEquals(UrlUtil.escape(wantValue), url.substring(wantPrefix.length())); + assertEquals(UrlUtil.escapeUrlParam(wantValue), url.substring(wantPrefix.length())); index++; } @@ -1234,8 +1231,8 @@ public class GenericClientTest { .execute(); assertThat(capt.getValue().getURI().toString(), containsString("http://example.com/fhir/Patient?")); - assertThat(capt.getValue().getURI().toString(), containsString("_include=" + UrlUtil.escape(Patient.INCLUDE_ORGANIZATION.getValue()))); - assertThat(capt.getValue().getURI().toString(), containsString("_include%3Arecurse=" + UrlUtil.escape(Patient.INCLUDE_LINK.getValue()))); + assertThat(capt.getValue().getURI().toString(), containsString("_include=" + UrlUtil.escapeUrlParam(Patient.INCLUDE_ORGANIZATION.getValue()))); + assertThat(capt.getValue().getURI().toString(), containsString("_include%3Arecurse=" + UrlUtil.escapeUrlParam(Patient.INCLUDE_LINK.getValue()))); assertThat(capt.getValue().getURI().toString(), containsString("_include=*")); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4ProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4ProviderTest.java index 973835eb88f..ba8ff1d8796 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4ProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4ProviderTest.java @@ -54,7 +54,7 @@ public class GraphQLR4ProviderTest { @Test public void testGraphInstance() throws Exception { String query = "{name{family,given}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -80,7 +80,7 @@ public class GraphQLR4ProviderTest { @Test public void testGraphInstanceWithFhirpath() throws Exception { String query = "{name(fhirpath:\"family.exists()\"){text,given,family}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -104,7 +104,7 @@ public class GraphQLR4ProviderTest { @Test public void testGraphSystemInstance() throws Exception { String query = "{Patient(id:123){id,name{given,family}}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -132,7 +132,7 @@ public class GraphQLR4ProviderTest { @Test public void testGraphSystemList() throws Exception { String query = "{PatientList(name:\"pet\"){name{family,given}}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4RawTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4RawTest.java index 372ee9d626e..1d3a9785efe 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4RawTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/GraphQLR4RawTest.java @@ -91,7 +91,7 @@ public class GraphQLR4RawTest { ourNextRetVal = "{\"foo\"}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escape("{name{family,given}}")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escapeUrlParam("{name{family,given}}")); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -114,7 +114,7 @@ public class GraphQLR4RawTest { ourNextRetVal = "{\"foo\"}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Condition/123/$graphql?query=" + UrlUtil.escape("{name{family,given}}")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Condition/123/$graphql?query=" + UrlUtil.escapeUrlParam("{name{family,given}}")); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -132,7 +132,7 @@ public class GraphQLR4RawTest { ourNextRetVal = "{\"foo\"}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escape("{name{family,given}}")); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escapeUrlParam("{name{family,given}}")); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java index 86f43d9114c..73d0a03d23d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java @@ -131,25 +131,25 @@ public class SearchR4Test { httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW))); + assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java index 7ac6de97773..840cb4d2de7 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.rest.server; -import static ca.uhn.fhir.util.UrlUtil.escape; +import static ca.uhn.fhir.util.UrlUtil.escapeUrlParam; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -110,10 +110,10 @@ public class SearchSearchServerDstu1Test { b.append("http://localhost:"); b.append(ourPort); b.append("/Patient?"); - b.append(escape("findPatientWithAndList")).append('=').append(escape("NE\\,NE,NE\\,NE")).append('&'); - b.append(escape("findPatientWithAndList")).append('=').append(escape("NE\\\\NE")).append('&'); - b.append(escape("findPatientWithAndList:exact")).append('=').append(escape("E\\$E")).append('&'); - b.append(escape("findPatientWithAndList:exact")).append('=').append(escape("E\\|E")).append('&'); + b.append(escapeUrlParam("findPatientWithAndList")).append('=').append(escapeUrlParam("NE\\,NE,NE\\,NE")).append('&'); + b.append(escapeUrlParam("findPatientWithAndList")).append('=').append(escapeUrlParam("NE\\\\NE")).append('&'); + b.append(escapeUrlParam("findPatientWithAndList:exact")).append('=').append(escapeUrlParam("E\\$E")).append('&'); + b.append(escapeUrlParam("findPatientWithAndList:exact")).append('=').append(escapeUrlParam("E\\|E")).append('&'); HttpGet httpGet = new HttpGet(b.toString()); @@ -416,7 +416,7 @@ public class SearchSearchServerDstu1Test { @Test public void testSearchWithTokenParameter() throws Exception { - String token = UrlUtil.escape("http://www.dmix.gov/vista/2957|301"); + String token = UrlUtil.escapeUrlParam("http://www.dmix.gov/vista/2957|301"); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?tokenParam=" + token); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); diff --git a/hapi-fhir-utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java b/hapi-fhir-utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java index 5c8c2106963..4ceae56abd1 100644 --- a/hapi-fhir-utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java +++ b/hapi-fhir-utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java @@ -1,472 +1,472 @@ -/* -Copyright (c) 2011+, HL7, Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ -package org.hl7.fhir.utilities.xml; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.List; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.utilities.Utilities; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; -import org.xml.sax.SAXException; - -public class XMLUtil { - - public static final String SPACE_CHAR = "\u00A0"; - - public static boolean isNMToken(String name) { - if (name == null) - return false; - for (int i = 0; i < name.length(); i++) - if (!isNMTokenChar(name.charAt(i))) - return false; - return name.length() > 0; - } - - public static boolean isNMTokenChar(char c) { - return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); - } - - private static boolean isDigit(char c) { - return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || - (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || - (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || - (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || - (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); - } - - private static boolean isCombiningChar(char c) { - return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || - (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || - c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || - c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || - (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || - (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || - (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || - (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || - c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || - (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || - (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || - (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || - (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || - (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || - (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || - (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || - (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || - (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || - (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || - c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || - (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || - (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || - (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || - (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || - c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; - } - - private static boolean isExtender(char c) { - return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || - c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || - (c >= '\u30FC' && c <= '\u30FE'); - } - - private static boolean isLetter(char c) { - return isBaseChar(c) || isIdeographic(c); - } - - private static boolean isBaseChar(char c) { - return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || - (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || - (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || - (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || - (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || - c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || - (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || - c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || - (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || - (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || - (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || - c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || - (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || - (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || - c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || - (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || - (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || - (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || - (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || - (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || - (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || - c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || - (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || - (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || - (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || - (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || - c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || - (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || - (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || - (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || - (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || - (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || - (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || - (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || - c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || - (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || - (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || - (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || - c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || - (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || - (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || - (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || - (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || - (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || - (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || - (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || - c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || - (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || - (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || - c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || - (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || - c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || - (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || - (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || - (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || - c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || - (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || - (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || - (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || - (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || - (c >= '\uAC00' && c <= '\uD7A3'); - } - - private static boolean isIdeographic(char c) { - return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); - } - - public static String determineEncoding(InputStream stream) throws IOException { - stream.mark(20000); - try { - int b0 = stream.read(); - int b1 = stream.read(); - int b2 = stream.read(); - int b3 = stream.read(); - - if (b0 == 0xFE && b1 == 0xFF) - return "UTF-16BE"; - else if (b0 == 0xFF && b1 == 0xFE) - return "UTF-16LE"; - else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) - return "UTF-8"; - else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) - return "UTF-16BE"; - else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) - return "UTF-16LE"; - else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { -// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding -// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding -// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns -// for the relevant ASCII characters, the encoding declaration itself may be read reliably - InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); - String hdr = readFirstLine(rdr); - return extractEncoding(hdr); - } else - return null; - } finally { - stream.reset(); - } - } - - private static String extractEncoding(String hdr) { - int i = hdr.indexOf("encoding="); - if (i == -1) - return null; - hdr = hdr.substring(i+9); - char sep = hdr.charAt(0); - hdr = hdr.substring(1); - i = hdr.indexOf(sep); - if (i == -1) - return null; - return hdr.substring(0, i); - } - - private static String readFirstLine(InputStreamReader rdr) throws IOException { - char[] buf = new char[1]; - StringBuffer bldr = new StringBuffer(); - rdr.read(buf); - while (buf[0] != '>') { - bldr.append(buf[0]); - rdr.read(buf); - } - return bldr.toString(); - } - - - public static boolean charSetImpliesAscii(String charset) { - return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); - } - - - /** - * Converts the raw characters to XML escape characters. - * - * @param rawContent - * @param charset Null when charset is not known, so we assume it's unicode - * @param isNoLines - * @return escape string - */ - public static String escapeXML(String rawContent, String charset, boolean isNoLines) { - if (rawContent == null) - return ""; - else { - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < rawContent.length(); i++) { - char ch = rawContent.charAt(i); - if (ch == '\'') - sb.append("'"); - else if (ch == '&') - sb.append("&"); - else if (ch == '"') - sb.append("""); - else if (ch == '<') - sb.append("<"); - else if (ch == '>') - sb.append(">"); - else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) - // TODO - why is hashcode the only way to get the unicode number for the character - // in jre 5.0? - sb.append("&#x"+Integer.toHexString(ch).toUpperCase()+";"); - else if (isNoLines) { - if (ch == '\r') - sb.append(" "); - else if (ch != '\n') - sb.append(ch); - } - else - sb.append(ch); - } - return sb.toString(); - } - } - - public static Element getFirstChild(Element e) { - if (e == null) - return null; - Node n = e.getFirstChild(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getNextSibling(); - return (Element) n; - } - - public static Element getNamedChild(Element e, String name) { - Element c = getFirstChild(e); - while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) - c = getNextSibling(c); - return c; - } - - public static Element getNextSibling(Element e) { - Node n = e.getNextSibling(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getNextSibling(); - return (Element) n; - } - - public static void getNamedChildren(Element e, String name, List set) { - Element c = getFirstChild(e); - while (c != null) { - if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) - set.add(c); - c = getNextSibling(c); - } - } - - public static String htmlToXmlEscapedPlainText(Element r) { - StringBuilder s = new StringBuilder(); - Node n = r.getFirstChild(); - boolean ws = false; - while (n != null) { - if (n.getNodeType() == Node.TEXT_NODE) { - String t = n.getTextContent().trim(); - if (Utilities.noString(t)) - ws = true; - else { - if (ws) - s.append(" "); - ws = false; - s.append(t); - } - } - if (n.getNodeType() == Node.ELEMENT_NODE) { - if (ws) - s.append(" "); - ws = false; - s.append(htmlToXmlEscapedPlainText((Element) n)); - if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) - s.append("\r\n"); - } - n = n.getNextSibling(); - } - return s.toString(); - } - - public static String htmlToXmlEscapedPlainText(String definition) throws ParserConfigurationException, SAXException, IOException { - return htmlToXmlEscapedPlainText(parseToDom("
    "+definition+"
    ").getDocumentElement()); - } - - public static String elementToString(Element el) { - if (el == null) - return ""; - Document document = el.getOwnerDocument(); - DOMImplementationLS domImplLS = (DOMImplementationLS) document - .getImplementation(); - LSSerializer serializer = domImplLS.createLSSerializer(); - return serializer.writeToString(el); - } - - public static String getNamedChildValue(Element element, String name) { - Element e = getNamedChild(element, name); - return e == null ? null : e.getAttribute("value"); - } - - public static void setNamedChildValue(Element element, String name, String value) throws FHIRException { - Element e = getNamedChild(element, name); - if (e == null) - throw new FHIRException("unable to find element "+name); - e.setAttribute("value", value); - } - - - public static void getNamedChildrenWithWildcard(Element focus, String name, List children) { - Element c = getFirstChild(focus); - while (c != null) { - String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); - if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) - children.add(c); - c = getNextSibling(c); - } - } - - public static void getNamedChildrenWithTails(Element focus, String name, List children, Set typeTails) { - Element c = getFirstChild(focus); - while (c != null) { - String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); - if (n.equals(name) || (!n.equals("responseCode") && (n.startsWith(name) && typeTails.contains(n.substring(name.length()))))) - children.add(c); - c = getNextSibling(c); - } - } - - public static boolean hasNamedChild(Element e, String name) { - Element c = getFirstChild(e); - while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) - c = getNextSibling(c); - return c != null; - } - - public static Document parseToDom(String content) throws ParserConfigurationException, SAXException, IOException { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - return builder.parse(new ByteArrayInputStream(content.getBytes())); - } - - public static Document parseFileToDom(String filename) throws ParserConfigurationException, SAXException, IOException { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - return builder.parse(new FileInputStream(filename)); - } - - public static Element getLastChild(Element e) { - if (e == null) - return null; - Node n = e.getLastChild(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getPreviousSibling(); - return (Element) n; - } - - public static Element getPrevSibling(Element e) { - Node n = e.getPreviousSibling(); - while (n != null && n.getNodeType() != Node.ELEMENT_NODE) - n = n.getPreviousSibling(); - return (Element) n; - } - - public static String getNamedChildAttribute(Element element, String name, String aname) { - Element e = getNamedChild(element, name); - return e == null ? null : e.getAttribute(aname); - } - - public static void writeDomToFile(Document doc, String filename) throws TransformerException { - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult streamResult = new StreamResult(new File(filename)); - transformer.transform(source, streamResult); - } - - public static String getXsiType(org.w3c.dom.Element element) { - Attr a = element.getAttributeNodeNS("http://www.w3.org/2001/XMLSchema-instance", "type"); - return (a == null ? null : a.getTextContent()); - - } - - public static String getDirectText(org.w3c.dom.Element node) { - Node n = node.getFirstChild(); - StringBuilder b = new StringBuilder(); - while (n != null) { - if (n.getNodeType() == Node.TEXT_NODE) - b.append(n.getTextContent()); - n = n.getNextSibling(); - } - return b.toString().trim(); - } - - -} +/* +Copyright (c) 2011+, HL7, Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +package org.hl7.fhir.utilities.xml; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.utilities.Utilities; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.SAXException; + +public class XMLUtil { + + public static final String SPACE_CHAR = "\u00A0"; + + public static boolean isNMToken(String name) { + if (name == null) + return false; + for (int i = 0; i < name.length(); i++) + if (!isNMTokenChar(name.charAt(i))) + return false; + return name.length() > 0; + } + + public static boolean isNMTokenChar(char c) { + return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); + } + + private static boolean isDigit(char c) { + return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || + (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || + (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || + (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || + (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); + } + + private static boolean isCombiningChar(char c) { + return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || + (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || + c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || + c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || + (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || + (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || + (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || + (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || + c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || + (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || + (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || + (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || + (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || + (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || + (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || + (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || + (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || + (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || + (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || + c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || + (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || + (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || + (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || + (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || + c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; + } + + private static boolean isExtender(char c) { + return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || + c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || + (c >= '\u30FC' && c <= '\u30FE'); + } + + private static boolean isLetter(char c) { + return isBaseChar(c) || isIdeographic(c); + } + + private static boolean isBaseChar(char c) { + return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || + (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || + (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || + (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || + (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || + c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || + (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || + c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || + (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || + (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || + (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || + c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || + (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || + (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || + c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || + (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || + (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || + (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || + (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || + (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || + (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || + c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || + (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || + (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || + (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || + (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || + c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || + (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || + (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || + (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || + (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || + (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || + (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || + (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || + c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || + (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || + (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || + (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || + c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || + (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || + (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || + (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || + (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || + (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || + (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || + (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || + c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || + (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || + (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || + c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || + (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || + c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || + (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || + (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || + (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || + c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || + (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || + (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || + (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || + (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || + (c >= '\uAC00' && c <= '\uD7A3'); + } + + private static boolean isIdeographic(char c) { + return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); + } + + public static String determineEncoding(InputStream stream) throws IOException { + stream.mark(20000); + try { + int b0 = stream.read(); + int b1 = stream.read(); + int b2 = stream.read(); + int b3 = stream.read(); + + if (b0 == 0xFE && b1 == 0xFF) + return "UTF-16BE"; + else if (b0 == 0xFF && b1 == 0xFE) + return "UTF-16LE"; + else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) + return "UTF-8"; + else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) + return "UTF-16BE"; + else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) + return "UTF-16LE"; + else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { +// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding +// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding +// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns +// for the relevant ASCII characters, the encoding declaration itself may be read reliably + InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); + String hdr = readFirstLine(rdr); + return extractEncoding(hdr); + } else + return null; + } finally { + stream.reset(); + } + } + + private static String extractEncoding(String hdr) { + int i = hdr.indexOf("encoding="); + if (i == -1) + return null; + hdr = hdr.substring(i+9); + char sep = hdr.charAt(0); + hdr = hdr.substring(1); + i = hdr.indexOf(sep); + if (i == -1) + return null; + return hdr.substring(0, i); + } + + private static String readFirstLine(InputStreamReader rdr) throws IOException { + char[] buf = new char[1]; + StringBuffer bldr = new StringBuffer(); + rdr.read(buf); + while (buf[0] != '>') { + bldr.append(buf[0]); + rdr.read(buf); + } + return bldr.toString(); + } + + + public static boolean charSetImpliesAscii(String charset) { + return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); + } + + + /** + * Converts the raw characters to XML escapeUrlParam characters. + * + * @param rawContent + * @param charset Null when charset is not known, so we assume it's unicode + * @param isNoLines + * @return escapeUrlParam string + */ + public static String escapeXML(String rawContent, String charset, boolean isNoLines) { + if (rawContent == null) + return ""; + else { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < rawContent.length(); i++) { + char ch = rawContent.charAt(i); + if (ch == '\'') + sb.append("'"); + else if (ch == '&') + sb.append("&"); + else if (ch == '"') + sb.append("""); + else if (ch == '<') + sb.append("<"); + else if (ch == '>') + sb.append(">"); + else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) + // TODO - why is hashcode the only way to get the unicode number for the character + // in jre 5.0? + sb.append("&#x"+Integer.toHexString(ch).toUpperCase()+";"); + else if (isNoLines) { + if (ch == '\r') + sb.append(" "); + else if (ch != '\n') + sb.append(ch); + } + else + sb.append(ch); + } + return sb.toString(); + } + } + + public static Element getFirstChild(Element e) { + if (e == null) + return null; + Node n = e.getFirstChild(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getNextSibling(); + return (Element) n; + } + + public static Element getNamedChild(Element e, String name) { + Element c = getFirstChild(e); + while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) + c = getNextSibling(c); + return c; + } + + public static Element getNextSibling(Element e) { + Node n = e.getNextSibling(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getNextSibling(); + return (Element) n; + } + + public static void getNamedChildren(Element e, String name, List set) { + Element c = getFirstChild(e); + while (c != null) { + if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) + set.add(c); + c = getNextSibling(c); + } + } + + public static String htmlToXmlEscapedPlainText(Element r) { + StringBuilder s = new StringBuilder(); + Node n = r.getFirstChild(); + boolean ws = false; + while (n != null) { + if (n.getNodeType() == Node.TEXT_NODE) { + String t = n.getTextContent().trim(); + if (Utilities.noString(t)) + ws = true; + else { + if (ws) + s.append(" "); + ws = false; + s.append(t); + } + } + if (n.getNodeType() == Node.ELEMENT_NODE) { + if (ws) + s.append(" "); + ws = false; + s.append(htmlToXmlEscapedPlainText((Element) n)); + if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) + s.append("\r\n"); + } + n = n.getNextSibling(); + } + return s.toString(); + } + + public static String htmlToXmlEscapedPlainText(String definition) throws ParserConfigurationException, SAXException, IOException { + return htmlToXmlEscapedPlainText(parseToDom("
    "+definition+"
    ").getDocumentElement()); + } + + public static String elementToString(Element el) { + if (el == null) + return ""; + Document document = el.getOwnerDocument(); + DOMImplementationLS domImplLS = (DOMImplementationLS) document + .getImplementation(); + LSSerializer serializer = domImplLS.createLSSerializer(); + return serializer.writeToString(el); + } + + public static String getNamedChildValue(Element element, String name) { + Element e = getNamedChild(element, name); + return e == null ? null : e.getAttribute("value"); + } + + public static void setNamedChildValue(Element element, String name, String value) throws FHIRException { + Element e = getNamedChild(element, name); + if (e == null) + throw new FHIRException("unable to find element "+name); + e.setAttribute("value", value); + } + + + public static void getNamedChildrenWithWildcard(Element focus, String name, List children) { + Element c = getFirstChild(focus); + while (c != null) { + String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); + if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) + children.add(c); + c = getNextSibling(c); + } + } + + public static void getNamedChildrenWithTails(Element focus, String name, List children, Set typeTails) { + Element c = getFirstChild(focus); + while (c != null) { + String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); + if (n.equals(name) || (!n.equals("responseCode") && (n.startsWith(name) && typeTails.contains(n.substring(name.length()))))) + children.add(c); + c = getNextSibling(c); + } + } + + public static boolean hasNamedChild(Element e, String name) { + Element c = getFirstChild(e); + while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) + c = getNextSibling(c); + return c != null; + } + + public static Document parseToDom(String content) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(new ByteArrayInputStream(content.getBytes())); + } + + public static Document parseFileToDom(String filename) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(new FileInputStream(filename)); + } + + public static Element getLastChild(Element e) { + if (e == null) + return null; + Node n = e.getLastChild(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getPreviousSibling(); + return (Element) n; + } + + public static Element getPrevSibling(Element e) { + Node n = e.getPreviousSibling(); + while (n != null && n.getNodeType() != Node.ELEMENT_NODE) + n = n.getPreviousSibling(); + return (Element) n; + } + + public static String getNamedChildAttribute(Element element, String name, String aname) { + Element e = getNamedChild(element, name); + return e == null ? null : e.getAttribute(aname); + } + + public static void writeDomToFile(Document doc, String filename) throws TransformerException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + StreamResult streamResult = new StreamResult(new File(filename)); + transformer.transform(source, streamResult); + } + + public static String getXsiType(org.w3c.dom.Element element) { + Attr a = element.getAttributeNodeNS("http://www.w3.org/2001/XMLSchema-instance", "type"); + return (a == null ? null : a.getTextContent()); + + } + + public static String getDirectText(org.w3c.dom.Element node) { + Node n = node.getFirstChild(); + StringBuilder b = new StringBuilder(); + while (n != null) { + if (n.getNodeType() == Node.TEXT_NODE) + b.append(n.getTextContent()); + n = n.getNextSibling(); + } + return b.toString().trim(); + } + + +} diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/GraphQLDstu3ProviderTest.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/GraphQLDstu3ProviderTest.java index b8da72f1894..f9668d839b4 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/GraphQLDstu3ProviderTest.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/GraphQLDstu3ProviderTest.java @@ -57,7 +57,7 @@ public class GraphQLDstu3ProviderTest { @Ignore public void testGraphInstance() throws Exception { String query = "{name{family,given}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -84,7 +84,7 @@ public class GraphQLDstu3ProviderTest { @Ignore public void testGraphInstanceWithFhirpath() throws Exception { String query = "{name(fhirpath:\"family.exists()\"){text,given,family}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -109,7 +109,7 @@ public class GraphQLDstu3ProviderTest { @org.junit.Ignore public void testGraphSystemInstance() throws Exception { String query = "{Patient(id:123){id,name{given,family}}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -138,7 +138,7 @@ public class GraphQLDstu3ProviderTest { @Ignore public void testGraphSystemList() throws Exception { String query = "{PatientList(name:\"pet\"){name{family,given}}}"; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escape(query)); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$graphql?query=" + UrlUtil.escapeUrlParam(query)); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4cf2c689611..26188e4c9ab 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -11,6 +11,12 @@ Fix a crash in JPA server when performing a recursive _include]]> which doesn't actually find any matches. + + When encoding URL parameter values, HAPI FHIR would incorrectly escape + a space (" ") as a plus ("+") insetad of as "%20" as required by + RFC 3986. This affects client calls, as well as URLs generated by + the server (e.g. REST HOOK calls). Thanks to James Daily for reporting! + From 863c4b370ca0606d9386510dc723368bd0d64ba5 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sat, 25 Nov 2017 20:32:42 -0500 Subject: [PATCH 2/2] Add some test debug logs --- .../jpa/search/SearchCoordinatorSvcImpl.java | 10 ++- .../uhn/fhir/jpa/config/TestDstu2Config.java | 2 +- .../uhn/fhir/jpa/config/TestDstu3Config.java | 2 +- .../ca/uhn/fhir/jpa/config/TestR4Config.java | 2 +- .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 75 +++++++++++-------- .../dao/dstu2/FhirResourceDaoDstu2Test.java | 10 ++- ...rResourceDaoDstu3SearchPageExpiryTest.java | 35 +++++---- 7 files changed, 82 insertions(+), 54 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 6f2a727e59e..340e258f617 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -593,6 +593,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { shouldSync = true; } + // If no abort was requested, bail out + Validate.isTrue(myAbortRequested == false, "Abort has been requested"); + if (shouldSync) { saveUnsynced(theResultIterator); } @@ -605,10 +608,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { } } - // Check if an abort got requested - Validate.isTrue(myAbortRequested == false, "Abort has been requested"); - } + + // If no abort was requested, bail out + Validate.isTrue(myAbortRequested == false, "Abort has been requested"); + saveUnsynced(theResultIterator); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java index 49c9107ce28..0017646f79e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java @@ -30,7 +30,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { public DataSource dataSource() { BasicDataSource retVal = new BasicDataSource(); retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver()); - retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true"); + retVal.setUrl("jdbc:derby:memory:myUnitTestDBDstu2;create=true"); retVal.setUsername(""); retVal.setPassword(""); return retVal; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java index 46fb60ba025..4c7420a3c1d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java @@ -90,7 +90,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 { }; retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver()); - retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true"); + retVal.setUrl("jdbc:derby:memory:myUnitTestDBDstu3;create=true"); retVal.setMaxWaitMillis(10000); retVal.setUsername(""); retVal.setPassword(""); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index d40ea5d2284..c84e1f4748a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -85,7 +85,7 @@ public class TestR4Config extends BaseJavaConfigR4 { }; retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver()); - retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true"); + retVal.setUrl("jdbc:derby:memory:myUnitTestDBR4;create=true"); retVal.setMaxWaitMillis(10000); retVal.setUsername(""); retVal.setPassword(""); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index 468845de9a4..130fc65d85c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -1,38 +1,12 @@ package ca.uhn.fhir.jpa.dao; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.InputStream; -import java.sql.SQLException; -import java.util.*; -import java.util.concurrent.Callable; - -import javax.persistence.EntityManager; - -import ca.uhn.fhir.jpa.util.StopWatch; -import ca.uhn.fhir.model.dstu2.resource.Observation; -import org.apache.commons.io.IOUtils; -import org.hibernate.search.jpa.Search; -import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.dstu3.model.Resource; -import org.hl7.fhir.instance.model.api.*; -import org.junit.AfterClass; -import org.junit.Before; -import org.mockito.Mockito; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.term.VersionIndependentConcept; +import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -41,6 +15,31 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.hibernate.search.jpa.Search; +import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.AfterClass; +import org.junit.Before; +import org.mockito.Mockito; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.persistence.EntityManager; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.Callable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public abstract class BaseJpaTest { @@ -53,13 +52,23 @@ public abstract class BaseJpaTest { public void beforeCreateSrd() { mySrd = mock(ServletRequestDetails.class, Mockito.RETURNS_DEEP_STUBS); when(mySrd.getRequestOperationCallback()).thenReturn(myRequestOperationCallback); - myServerInterceptorList = new ArrayList(); + myServerInterceptorList = new ArrayList<>(); when(mySrd.getServer().getInterceptors()).thenReturn(myServerInterceptorList); - when(mySrd.getUserData()).thenReturn(new HashMap()); + when(mySrd.getUserData()).thenReturn(new HashMap<>()); } protected abstract FhirContext getContext(); + /** + * Sleep until at least 1 ms has elapsed + */ + public void sleepUntilTimeChanges() throws InterruptedException { + StopWatch sw = new StopWatch(); + while (sw.getMillis() == 0) { + Thread.sleep(10); + } + } + protected org.hl7.fhir.dstu3.model.Bundle toBundle(IBundleProvider theSearch) { org.hl7.fhir.dstu3.model.Bundle bundle = new org.hl7.fhir.dstu3.model.Bundle(); for (IBaseResource next : theSearch.getResources(0, theSearch.size())) { @@ -76,11 +85,11 @@ public abstract class BaseJpaTest { return bundle; } - @SuppressWarnings({ "rawtypes" }) + @SuppressWarnings({"rawtypes"}) protected List toList(IBundleProvider theSearch) { return theSearch.getResources(0, theSearch.size()); } - + protected List toUnqualifiedIdValues(IBaseBundle theFound) { List retVal = new ArrayList(); @@ -214,9 +223,9 @@ public abstract class BaseJpaTest { } public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager, ISearchParamPresenceSvc theSearchParamPresenceSvc, ISearchCoordinatorSvc theSearchCoordinatorSvc, ISearchParamRegistry theSearchParamRegistry) { - + theSearchCoordinatorSvc.cancelAllActiveSearches(); - + TransactionTemplate txTemplate = new TransactionTemplate(theTxManager); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.execute(new TransactionCallback() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index f725737ef79..ac5777116c2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -2346,7 +2346,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } @Test - public void testSortByLastUpdated() { + public void testSortByLastUpdated() throws InterruptedException { String methodName = "testSortByLastUpdated"; Patient p = new Patient(); @@ -2354,21 +2354,29 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { p.addName().addFamily(methodName); IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + sleepUntilTimeChanges(); + p = new Patient(); p.addIdentifier().setSystem("urn:system2").setValue(methodName); p.addName().addFamily(methodName); IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + sleepUntilTimeChanges(); + p = new Patient(); p.addIdentifier().setSystem("urn:system3").setValue(methodName); p.addName().addFamily(methodName); IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + sleepUntilTimeChanges(); + p = new Patient(); p.addIdentifier().setSystem("urn:system4").setValue(methodName); p.addName().addFamily(methodName); IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + sleepUntilTimeChanges(); + SearchParameterMap pm; List actual; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchPageExpiryTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchPageExpiryTest.java index ea2fd0c1158..f70f9a81f3d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchPageExpiryTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchPageExpiryTest.java @@ -1,14 +1,17 @@ package ca.uhn.fhir.jpa.dao.dstu3; -import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.junit.Assert.*; - +import ca.uhn.fhir.jpa.dao.SearchParameterMap; +import ca.uhn.fhir.jpa.entity.Search; +import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl; import ca.uhn.fhir.jpa.util.StopWatch; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.StringParam; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.util.AopTestUtils; @@ -16,10 +19,11 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import ca.uhn.fhir.jpa.dao.SearchParameterMap; -import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.StringParam; +import java.util.concurrent.atomic.AtomicLong; + +import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.*; public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoDstu3SearchPageExpiryTest.class); @@ -104,14 +108,17 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test { // Search just got used so it shouldn't be deleted myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); + final AtomicLong search3timestamp = new AtomicLong(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - assertNotNull(mySearchEntityDao.findByUuid(searchUuid3)); + Search search3 = mySearchEntityDao.findByUuid(searchUuid3); + assertNotNull(search3); + search3timestamp.set(search3.getCreated().getTime()); } }); - StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 1400); + StaleSearchDeletingSvcImpl.setNowForUnitTests(search3timestamp.get() + 1400); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @@ -127,14 +134,14 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test { } }); - StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 2200); + StaleSearchDeletingSvcImpl.setNowForUnitTests(search3timestamp.get() + 2200); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - assertNull(mySearchEntityDao.findByUuid(searchUuid1)); - assertNull(mySearchEntityDao.findByUuid(searchUuid3)); + assertNull("Search 1 still exists", mySearchEntityDao.findByUuid(searchUuid1)); + assertNull("Search 3 still exists", mySearchEntityDao.findByUuid(searchUuid3)); } });