001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.transport.amqp.message; 018 019import java.nio.charset.Charset; 020import java.nio.charset.IllegalCharsetNameException; 021import java.nio.charset.StandardCharsets; 022import java.nio.charset.UnsupportedCharsetException; 023import java.util.StringTokenizer; 024 025public final class AmqpContentTypeSupport { 026 027 private static final String UTF_8 = "UTF-8"; 028 private static final String CHARSET = "charset"; 029 private static final String TEXT = "text"; 030 private static final String APPLICATION = "application"; 031 private static final String JAVASCRIPT = "javascript"; 032 private static final String XML = "xml"; 033 private static final String XML_VARIANT = "+xml"; 034 private static final String JSON = "json"; 035 private static final String JSON_VARIANT = "+json"; 036 private static final String XML_DTD = "xml-dtd"; 037 private static final String ECMASCRIPT = "ecmascript"; 038 039 /** 040 * @param contentType 041 * the contentType of the received message 042 * @return the character set to use, or null if not to treat the message as 043 * text 044 * @throws InvalidContentTypeException 045 * if the content-type is invalid in some way. 046 */ 047 public static Charset parseContentTypeForTextualCharset(final String contentType) throws InvalidContentTypeException { 048 if (contentType == null || contentType.trim().isEmpty()) { 049 throw new InvalidContentTypeException("Content type can't be null or empty"); 050 } 051 052 int subTypeSeparator = contentType.indexOf("/"); 053 if (subTypeSeparator == -1) { 054 throw new InvalidContentTypeException("Content type has no '/' separator: " + contentType); 055 } 056 057 final String type = contentType.substring(0, subTypeSeparator).toLowerCase().trim(); 058 059 String subTypePart = contentType.substring(subTypeSeparator + 1).toLowerCase().trim(); 060 061 String parameterPart = null; 062 int parameterSeparator = subTypePart.indexOf(";"); 063 if (parameterSeparator != -1) { 064 if (parameterSeparator < subTypePart.length() - 1) { 065 parameterPart = contentType.substring(subTypeSeparator + 1).toLowerCase().trim(); 066 } 067 subTypePart = subTypePart.substring(0, parameterSeparator).trim(); 068 } 069 070 if (subTypePart.isEmpty()) { 071 throw new InvalidContentTypeException("Content type has no subtype after '/'" + contentType); 072 } 073 074 final String subType = subTypePart; 075 076 if (isTextual(type, subType)) { 077 String charset = findCharset(parameterPart); 078 if (charset == null) { 079 charset = UTF_8; 080 } 081 082 if (UTF_8.equals(charset)) { 083 return StandardCharsets.UTF_8; 084 } else { 085 try { 086 return Charset.forName(charset); 087 } catch (IllegalCharsetNameException icne) { 088 throw new InvalidContentTypeException("Illegal charset: " + charset); 089 } catch (UnsupportedCharsetException uce) { 090 throw new InvalidContentTypeException("Unsupported charset: " + charset); 091 } 092 } 093 } 094 095 return null; 096 } 097 098 //----- Internal Content Type utilities ----------------------------------// 099 100 private static boolean isTextual(String type, String subType) { 101 if (TEXT.equals(type)) { 102 return true; 103 } 104 105 if (APPLICATION.equals(type)) { 106 if (XML.equals(subType) || JSON.equals(subType) || JAVASCRIPT.equals(subType) || subType.endsWith(XML_VARIANT) || subType.endsWith(JSON_VARIANT) 107 || XML_DTD.equals(subType) || ECMASCRIPT.equals(subType)) { 108 return true; 109 } 110 } 111 112 return false; 113 } 114 115 private static String findCharset(String paramaterPart) { 116 String charset = null; 117 118 if (paramaterPart != null) { 119 StringTokenizer tokenizer = new StringTokenizer(paramaterPart, ";"); 120 while (tokenizer.hasMoreTokens()) { 121 String parameter = tokenizer.nextToken().trim(); 122 int eqIndex = parameter.indexOf('='); 123 if (eqIndex != -1) { 124 String name = parameter.substring(0, eqIndex); 125 if (CHARSET.equalsIgnoreCase(name.trim())) { 126 String value = unquote(parameter.substring(eqIndex + 1)); 127 128 charset = value.toUpperCase(); 129 break; 130 } 131 } 132 } 133 } 134 135 return charset; 136 } 137 138 private static String unquote(String s) { 139 if (s.length() > 1 && (s.startsWith("\"") && s.endsWith("\""))) { 140 return s.substring(1, s.length() - 1); 141 } else { 142 return s; 143 } 144 } 145}