001package ca.uhn.fhir.rest.param; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2016 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import java.util.ArrayList; 024import java.util.Calendar; 025import java.util.Collections; 026import java.util.Date; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Set; 030 031import org.apache.commons.lang3.time.DateUtils; 032 033import ca.uhn.fhir.model.primitive.InstantDt; 034import ca.uhn.fhir.model.primitive.IntegerDt; 035import ca.uhn.fhir.util.UrlUtil; 036 037public class ParameterUtil { 038 039 private static final Set<Class<?>> BINDABLE_INTEGER_TYPES; 040 private static final Set<Class<?>> BINDABLE_TIME_TYPES; 041 042 static { 043 HashSet<Class<?>> intTypes = new HashSet<Class<?>>(); 044 intTypes.add(IntegerDt.class); 045 intTypes.add(Integer.class); 046 BINDABLE_INTEGER_TYPES = Collections.unmodifiableSet(intTypes); 047 048 HashSet<Class<?>> timeTypes = new HashSet<Class<?>>(); 049 timeTypes.add(InstantDt.class); 050 timeTypes.add(Date.class); 051 timeTypes.add(Calendar.class); 052 BINDABLE_TIME_TYPES = Collections.unmodifiableSet(timeTypes); 053 054 } 055 056 // public static Integer findSinceParameterIndex(Method theMethod) { 057 // return findParamIndex(theMethod, Since.class); 058 // } 059 060 public static int nonEscapedIndexOf(String theString, char theCharacter) { 061 for (int i = 0; i < theString.length(); i++) { 062 if (theString.charAt(i) == theCharacter) { 063 if (i == 0 || theString.charAt(i - 1) != '\\') { 064 return i; 065 } 066 } 067 } 068 return -1; 069 } 070 071 public static Object fromInstant(Class<?> theType, InstantDt theArgument) { 072 if (theType.equals(InstantDt.class)) { 073 if (theArgument == null) { 074 return new InstantDt(); 075 } 076 return theArgument; 077 } 078 if (theType.equals(Date.class)) { 079 if (theArgument == null) { 080 return null; 081 } 082 return theArgument.getValue(); 083 } 084 if (theType.equals(Calendar.class)) { 085 if (theArgument == null) { 086 return null; 087 } 088 return DateUtils.toCalendar(theArgument.getValue()); 089 } 090 throw new IllegalArgumentException("Invalid instant type:" + theType); 091 } 092 093 public static Object fromInteger(Class<?> theType, IntegerDt theArgument) { 094 if (theType.equals(IntegerDt.class)) { 095 if (theArgument == null) { 096 return new IntegerDt(); 097 } 098 return theArgument; 099 } 100 if (theType.equals(Integer.class)) { 101 if (theArgument == null) { 102 return null; 103 } 104 return theArgument.getValue(); 105 } 106 throw new IllegalArgumentException("Invalid Integer type:" + theType); 107 } 108 109 public static Set<Class<?>> getBindableInstantTypes() { 110 return BINDABLE_TIME_TYPES; 111 } 112 113 public static Set<Class<?>> getBindableIntegerTypes() { 114 return BINDABLE_INTEGER_TYPES; 115 } 116 117 public static InstantDt toInstant(Object theArgument) { 118 if (theArgument instanceof InstantDt) { 119 return (InstantDt) theArgument; 120 } 121 if (theArgument instanceof Date) { 122 return new InstantDt((Date) theArgument); 123 } 124 if (theArgument instanceof Calendar) { 125 return new InstantDt((Calendar) theArgument); 126 } 127 return null; 128 } 129 130 public static IntegerDt toInteger(Object theArgument) { 131 if (theArgument instanceof IntegerDt) { 132 return (IntegerDt) theArgument; 133 } 134 if (theArgument instanceof Integer) { 135 return new IntegerDt((Integer) theArgument); 136 } 137 return null; 138 } 139 140 static List<String> splitParameterString(String theInput, boolean theUnescapeComponents) { 141 return splitParameterString(theInput, ',', theUnescapeComponents); 142 } 143 144 static List<String> splitParameterString(String theInput, char theDelimiter, boolean theUnescapeComponents) { 145 ArrayList<String> retVal = new ArrayList<String>(); 146 if (theInput != null) { 147 StringBuilder b = new StringBuilder(); 148 for (int i = 0; i < theInput.length(); i++) { 149 char next = theInput.charAt(i); 150 if (next == theDelimiter) { 151 if (i == 0) { 152 b.append(next); 153 } else { 154 char prevChar = theInput.charAt(i - 1); 155 if (prevChar == '\\') { 156 b.append(next); 157 } else { 158 if (b.length() > 0) { 159 retVal.add(b.toString()); 160 b.setLength(0); 161 } 162 } 163 } 164 } else { 165 b.append(next); 166 } 167 } 168 if (b.length() > 0) { 169 retVal.add(b.toString()); 170 } 171 } 172 173 if (theUnescapeComponents) { 174 for (int i = 0; i < retVal.size(); i++) { 175 retVal.set(i, unescape(retVal.get(i))); 176 } 177 } 178 179 return retVal; 180 } 181 182 /** 183 * Escapes a string according to the rules for parameter escaping specified in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping 184 * Section</a> 185 */ 186 public static String escapeWithDefault(Object theValue) { 187 if (theValue == null) { 188 return ""; 189 } else { 190 return escape(theValue.toString()); 191 } 192 } 193 194 /** 195 * Escapes a string according to the rules for parameter escaping specified in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping 196 * Section</a> 197 */ 198 public static String escape(String theValue) { 199 if (theValue == null) { 200 return null; 201 } 202 StringBuilder b = new StringBuilder(); 203 204 for (int i = 0; i < theValue.length(); i++) { 205 char next = theValue.charAt(i); 206 switch (next) { 207 case '$': 208 case ',': 209 case '|': 210 case '\\': 211 b.append('\\'); 212 break; 213 default: 214 break; 215 } 216 b.append(next); 217 } 218 219 return b.toString(); 220 } 221 222 /** 223 * Escapes a string according to the rules for parameter escaping specified in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping 224 * Section</a> 225 */ 226 public static String escapeAndUrlEncode(String theValue) { 227 if (theValue == null) { 228 return null; 229 } 230 231 String escaped = escape(theValue); 232 return UrlUtil.escape(escaped); 233 } 234 235 /** 236 * Unescapes a string according to the rules for parameter escaping specified in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping 237 * Section</a> 238 */ 239 public static String unescape(String theValue) { 240 if (theValue == null) { 241 return theValue; 242 } 243 if (theValue.indexOf('\\') == -1) { 244 return theValue; 245 } 246 247 StringBuilder b = new StringBuilder(); 248 249 for (int i = 0; i < theValue.length(); i++) { 250 char next = theValue.charAt(i); 251 if (next == '\\') { 252 if (i == theValue.length() - 1) { 253 b.append(next); 254 } else { 255 switch (theValue.charAt(i + 1)) { 256 case '$': 257 case ',': 258 case '|': 259 case '\\': 260 continue; 261 default: 262 b.append(next); 263 } 264 } 265 } else { 266 b.append(next); 267 } 268 } 269 270 return b.toString(); 271 } 272 273}