001package ca.uhn.fhir.rest.api;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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 */
022import java.util.*;
023
024import org.apache.commons.lang3.ObjectUtils;
025
026import ca.uhn.fhir.context.FhirContext;
027import ca.uhn.fhir.parser.IParser;
028
029public enum EncodingEnum {
030
031        JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON_NEW, Constants.FORMAT_JSON) {
032                @Override
033                public IParser newParser(FhirContext theContext) {
034                        return theContext.newJsonParser();
035                }
036        },
037
038        XML(Constants.CT_FHIR_XML, Constants.CT_FHIR_XML_NEW, Constants.FORMAT_XML) {
039                @Override
040                public IParser newParser(FhirContext theContext) {
041                        return theContext.newXmlParser();
042                }
043        }
044
045        ;
046
047        /** "json" */
048        public  static final String JSON_PLAIN_STRING = "json";
049        private static Map<String, EncodingEnum> ourContentTypeToEncoding;
050        
051        private static Map<String, EncodingEnum> ourContentTypeToEncodingNonLegacy;
052        private static Map<String, EncodingEnum> ourContentTypeToEncodingStrict;
053        /** "xml" */
054        public static final String XML_PLAIN_STRING = "xml";
055
056        static {
057                ourContentTypeToEncoding = new HashMap<String, EncodingEnum>();
058                ourContentTypeToEncodingNonLegacy = new HashMap<String, EncodingEnum>();
059                
060                for (EncodingEnum next : values()) {
061                        ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy, next);
062                        ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy, next);
063                        ourContentTypeToEncodingNonLegacy.put(next.myResourceContentTypeNonLegacy, next);
064
065                        /*
066                         * See #346
067                         */
068                        ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next);
069                        ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy.replace('+', ' '), next);
070                        ourContentTypeToEncodingNonLegacy.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next);
071
072                }
073
074                // Add before we add the lenient ones
075                ourContentTypeToEncodingStrict = Collections.unmodifiableMap(new HashMap<String, EncodingEnum>(ourContentTypeToEncoding));
076
077                /*
078                 * These are wrong, but we add them just to be tolerant of other
079                 * people's mistakes
080                 */
081                ourContentTypeToEncoding.put("application/json", JSON);
082                ourContentTypeToEncoding.put("application/xml", XML);
083                ourContentTypeToEncoding.put("text/json", JSON);
084                ourContentTypeToEncoding.put("text/xml", XML);
085
086                /*
087                 * Plain values, used for parameter values
088                 */
089                ourContentTypeToEncoding.put(JSON_PLAIN_STRING, JSON);
090                ourContentTypeToEncoding.put(XML_PLAIN_STRING, XML);
091
092                ourContentTypeToEncodingNonLegacy = Collections.unmodifiableMap(ourContentTypeToEncodingNonLegacy);
093
094        }
095
096        private String myFormatContentType;
097        private String myResourceContentTypeLegacy;
098        private String myResourceContentTypeNonLegacy;
099
100        EncodingEnum(String theResourceContentTypeLegacy, String theResourceContentType, String theFormatContentType) {
101                myResourceContentTypeLegacy = theResourceContentTypeLegacy;
102                myResourceContentTypeNonLegacy = theResourceContentType;
103                myFormatContentType = theFormatContentType;
104        }
105
106        public String getFormatContentType() {
107                return myFormatContentType;
108        }
109
110        public String getRequestContentType() {
111                return myFormatContentType;
112        }
113
114        /**
115         * Will return application/xml+fhir style
116         */
117        public String getResourceContentType() {
118                return myResourceContentTypeLegacy;
119        }
120
121        /**
122         * Will return application/fhir+xml style
123         */
124        public String getResourceContentTypeNonLegacy() {
125                return myResourceContentTypeNonLegacy;
126        }
127
128        public abstract IParser newParser(FhirContext theContext);
129
130        public static EncodingEnum detectEncoding(String theBody) {
131                EncodingEnum retVal = detectEncodingNoDefault(theBody);
132                retVal = ObjectUtils.defaultIfNull(retVal, EncodingEnum.XML);
133                return retVal;
134        }
135
136        public static EncodingEnum detectEncodingNoDefault(String theBody) {
137                EncodingEnum retVal = null;
138                for (int i = 0; i < theBody.length() && retVal == null; i++) {
139                        switch (theBody.charAt(i)) {
140                                case '<':
141                                        retVal = EncodingEnum.XML;
142                                        break;
143                                case '{':
144                                        retVal = EncodingEnum.JSON;
145                                        break;
146                        }
147                }
148                return retVal;
149        }
150
151        /**
152         * Returns the encoding for a given content type, or <code>null</code> if no encoding
153         * is found. 
154         * <p>
155         * <b>This method is lenient!</b> Things like "application/xml" will return {@link EncodingEnum#XML}
156         * even if the "+fhir" part is missing from the expected content type.
157         * </p>
158         */
159        public static EncodingEnum forContentType(String theContentType) {
160                return ourContentTypeToEncoding.get(theContentType);
161        }
162
163
164        /**
165         * Returns the encoding for a given content type, or <code>null</code> if no encoding
166         * is found. 
167         * <p>
168         * <b>This method is NOT lenient!</b> Things like "application/xml" will return <code>null</code>
169         * </p>
170         * @see #forContentType(String)
171         */
172        public static EncodingEnum forContentTypeStrict(String theContentType) {
173                return ourContentTypeToEncodingStrict.get(theContentType);
174        }
175
176        public static boolean isNonLegacy(String theFormat) {
177                return ourContentTypeToEncodingNonLegacy.containsKey(theFormat);
178        }
179
180        
181}