View Javadoc
1   package ca.uhn.fhir.rest.api;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.context.FhirContext;
24  import ca.uhn.fhir.parser.IParser;
25  import org.apache.commons.lang3.ObjectUtils;
26  
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.Map;
30  
31  public enum EncodingEnum {
32  
33  	JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON_NEW, Constants.FORMAT_JSON) {
34  		@Override
35  		public IParser newParser(FhirContext theContext) {
36  			return theContext.newJsonParser();
37  		}
38  	},
39  
40  	XML(Constants.CT_FHIR_XML, Constants.CT_FHIR_XML_NEW, Constants.FORMAT_XML) {
41  		@Override
42  		public IParser newParser(FhirContext theContext) {
43  			return theContext.newXmlParser();
44  		}
45  	};
46  
47  	/**
48  	 * "json"
49  	 */
50  	public static final String JSON_PLAIN_STRING = "json";
51  	/**
52  	 * "xml"
53  	 */
54  	public static final String XML_PLAIN_STRING = "xml";
55  	private static Map<String, EncodingEnum> ourContentTypeToEncoding;
56  	private static Map<String, EncodingEnum> ourContentTypeToEncodingLegacy;
57  	private static Map<String, EncodingEnum> ourContentTypeToEncodingStrict;
58  
59  	static {
60  		ourContentTypeToEncoding = new HashMap<>();
61  		ourContentTypeToEncodingLegacy = new HashMap<>();
62  
63  		for (EncodingEnum next : values()) {
64  			ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy, next);
65  			ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy, next);
66  			ourContentTypeToEncodingLegacy.put(next.myResourceContentTypeLegacy, next);
67  
68  			/*
69  			 * See #346
70  			 */
71  			ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next);
72  			ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy.replace('+', ' '), next);
73  			ourContentTypeToEncodingLegacy.put(next.myResourceContentTypeLegacy.replace('+', ' '), next);
74  
75  		}
76  
77  		// Add before we add the lenient ones
78  		ourContentTypeToEncodingStrict = Collections.unmodifiableMap(new HashMap<>(ourContentTypeToEncoding));
79  
80  		/*
81  		 * These are wrong, but we add them just to be tolerant of other
82  		 * people's mistakes
83  		 */
84  		ourContentTypeToEncoding.put("application/json", JSON);
85  		ourContentTypeToEncoding.put("application/xml", XML);
86  		ourContentTypeToEncoding.put("text/json", JSON);
87  		ourContentTypeToEncoding.put("text/xml", XML);
88  
89  		/*
90  		 * Plain values, used for parameter values
91  		 */
92  		ourContentTypeToEncoding.put(JSON_PLAIN_STRING, JSON);
93  		ourContentTypeToEncoding.put(XML_PLAIN_STRING, XML);
94  
95  		ourContentTypeToEncodingLegacy = Collections.unmodifiableMap(ourContentTypeToEncodingLegacy);
96  
97  	}
98  
99  	private String myFormatContentType;
100 	private String myResourceContentTypeLegacy;
101 	private String myResourceContentTypeNonLegacy;
102 
103 	EncodingEnum(String theResourceContentTypeLegacy, String theResourceContentType, String theFormatContentType) {
104 		myResourceContentTypeLegacy = theResourceContentTypeLegacy;
105 		myResourceContentTypeNonLegacy = theResourceContentType;
106 		myFormatContentType = theFormatContentType;
107 	}
108 
109 	public String getFormatContentType() {
110 		return myFormatContentType;
111 	}
112 
113 	/**
114 	 * Will return application/xml+fhir style
115 	 */
116 	public String getResourceContentType() {
117 		return myResourceContentTypeLegacy;
118 	}
119 
120 	/**
121 	 * Will return application/fhir+xml style
122 	 */
123 	public String getResourceContentTypeNonLegacy() {
124 		return myResourceContentTypeNonLegacy;
125 	}
126 
127 	public abstract IParser newParser(FhirContext theContext);
128 
129 	public static EncodingEnum detectEncoding(String theBody) {
130 		EncodingEnum retVal = detectEncodingNoDefault(theBody);
131 		retVal = ObjectUtils.defaultIfNull(retVal, EncodingEnum.XML);
132 		return retVal;
133 	}
134 
135 	public static EncodingEnum detectEncodingNoDefault(String theBody) {
136 		EncodingEnum retVal = null;
137 		for (int i = 0; i < theBody.length() && retVal == null; i++) {
138 			switch (theBody.charAt(i)) {
139 				case '<':
140 					retVal = EncodingEnum.XML;
141 					break;
142 				case '{':
143 					retVal = EncodingEnum.JSON;
144 					break;
145 			}
146 		}
147 		return retVal;
148 	}
149 
150 	/**
151 	 * Returns the encoding for a given content type, or <code>null</code> if no encoding
152 	 * is found.
153 	 * <p>
154 	 * <b>This method is lenient!</b> Things like "application/xml" will return {@link EncodingEnum#XML}
155 	 * even if the "+fhir" part is missing from the expected content type.
156 	 * </p>
157 	 */
158 	public static EncodingEnum forContentType(String theContentType) {
159 		String contentTypeSplitted = getTypeWithoutCharset(theContentType);
160 		if (contentTypeSplitted == null) {
161 			return null;
162 		} else {
163 			return ourContentTypeToEncoding.get(contentTypeSplitted );
164 		}
165 	}
166 
167 
168 	/**
169 	 * Returns the encoding for a given content type, or <code>null</code> if no encoding
170 	 * is found.
171 	 * <p>
172 	 * <b>This method is NOT lenient!</b> Things like "application/xml" will return <code>null</code>
173 	 * </p>
174 	 *
175 	 * @see #forContentType(String)
176 	 */
177 	public static EncodingEnum forContentTypeStrict(String theContentType) {
178 		String contentTypeSplitted = getTypeWithoutCharset(theContentType);
179 		if (contentTypeSplitted == null) {
180 			return null;
181 		} else {
182 			return ourContentTypeToEncodingStrict.get(contentTypeSplitted);
183 		}
184 	}
185 
186 	private static String getTypeWithoutCharset(String theContentType) {
187 		if (theContentType == null) {
188 			return null;
189 		} else {
190 			String[] contentTypeSplitted = theContentType.split(";");
191 			return contentTypeSplitted[0];
192 		}
193 	}
194 
195 	/**
196 	 * Is the given type a FHIR legacy (pre-DSTU3) content type?
197 	 */
198 	public static boolean isLegacy(String theContentType) {
199 		String contentTypeSplitted = getTypeWithoutCharset(theContentType);
200 		if (contentTypeSplitted == null) {
201 			return false;
202 		} else {
203 			return ourContentTypeToEncodingLegacy.containsKey(contentTypeSplitted);
204 		}
205 	}
206 
207 
208 }