001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.api; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.model.api.IQueryParameterOr; 024import ca.uhn.fhir.model.api.IQueryParameterType; 025 026import java.util.ArrayList; 027import java.util.StringTokenizer; 028 029import static org.apache.commons.lang3.StringUtils.isBlank; 030 031public class QualifiedParamList extends ArrayList<String> { 032 033 private static final long serialVersionUID = 1L; 034 035 private String myQualifier; 036 037 public QualifiedParamList() { 038 super(); 039 } 040 041 public QualifiedParamList(int theCapacity) { 042 super(theCapacity); 043 } 044 045 public QualifiedParamList(IQueryParameterOr<?> theNextOr, FhirContext theContext) { 046 for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) { 047 if (myQualifier == null) { 048 myQualifier = next.getQueryParameterQualifier(); 049 } 050 add(next.getValueAsQueryToken(theContext)); 051 } 052 } 053 054 public String getQualifier() { 055 return myQualifier; 056 } 057 058 public void setQualifier(String theQualifier) { 059 myQualifier = theQualifier; 060 } 061 062 public static QualifiedParamList singleton(String theParamValue) { 063 return singleton(null, theParamValue); 064 } 065 066 public static QualifiedParamList singleton(String theQualifier, String theParamValue) { 067 QualifiedParamList retVal = new QualifiedParamList(1); 068 retVal.setQualifier(theQualifier); 069 retVal.add(theParamValue); 070 return retVal; 071 } 072 073 public static QualifiedParamList splitQueryStringByCommasIgnoreEscape(String theQualifier, String theParams) { 074 QualifiedParamList retVal = new QualifiedParamList(); 075 retVal.setQualifier(theQualifier); 076 077 StringTokenizer tok = new StringTokenizer(theParams, ",", true); 078 String prev = null; 079 while (tok.hasMoreElements()) { 080 String str = tok.nextToken(); 081 if (isBlank(str)) { 082 prev = null; 083 continue; 084 } 085 086 if (str.equals(",")) { 087 if (countTrailingSlashes(prev) % 2 == 1) { 088 int idx = retVal.size() - 1; 089 String existing = retVal.get(idx); 090 prev = existing.substring(0, existing.length() - 1) + ','; 091 retVal.set(idx, prev); 092 } else { 093 prev = null; 094 } 095 continue; 096 } 097 098 if (prev != null && prev.length() > 0 && prev.charAt(prev.length() - 1) == ',') { 099 int idx = retVal.size() - 1; 100 String existing = retVal.get(idx); 101 prev = existing + str; 102 retVal.set(idx, prev); 103 } else { 104 retVal.add(str); 105 prev = str; 106 } 107 } 108 109 // If no value was found, at least add that empty string as a value. It should get ignored later, but at 110 // least this lets us give a sensible error message if the parameter name was bad. See 111 // ResourceProviderR4Test#testParameterWithNoValueThrowsError_InvalidChainOnCustomSearch for an example 112 if (retVal.size() == 0) { 113 retVal.add(""); 114 } 115 116 return retVal; 117 } 118 119 private static int countTrailingSlashes(String theString) { 120 if (theString == null) { 121 return 0; 122 } 123 int retVal = 0; 124 for (int i = theString.length() - 1; i >= 0; i--) { 125 char nextChar = theString.charAt(i); 126 if (nextChar != '\\') { 127 break; 128 } 129 retVal++; 130 } 131 return retVal; 132 } 133}