ContentTypeField.java revision 96c5af40d639d629267794f4f0338a267ff94ce5
1/**************************************************************** 2 * Licensed to the Apache Software Foundation (ASF) under one * 3 * or more contributor license agreements. See the NOTICE file * 4 * distributed with this work for additional information * 5 * regarding copyright ownership. The ASF licenses this file * 6 * to you under the Apache License, Version 2.0 (the * 7 * "License"); you may not use this file except in compliance * 8 * with the License. You may obtain a copy of the License at * 9 * * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, * 13 * software distributed under the License is distributed on an * 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15 * KIND, either express or implied. See the License for the * 16 * specific language governing permissions and limitations * 17 * under the License. * 18 ****************************************************************/ 19 20package org.apache.james.mime4j.field; 21 22import java.io.StringReader; 23import java.util.ArrayList; 24import java.util.Collections; 25import java.util.HashMap; 26import java.util.Map; 27 28import org.apache.commons.logging.Log; 29import org.apache.commons.logging.LogFactory; 30import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser; 31import org.apache.james.mime4j.field.contenttype.parser.ParseException; 32import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError; 33 34/** 35 * Represents a <code>Content-Type</code> field. 36 * 37 * <p>TODO: Remove dependency on Java 1.4 regexps</p> 38 * 39 * 40 * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $ 41 */ 42public class ContentTypeField extends Field { 43 44 /** 45 * The prefix of all <code>multipart</code> MIME types. 46 */ 47 public static final String TYPE_MULTIPART_PREFIX = "multipart/"; 48 /** 49 * The <code>multipart/digest</code> MIME type. 50 */ 51 public static final String TYPE_MULTIPART_DIGEST = "multipart/digest"; 52 /** 53 * The <code>text/plain</code> MIME type. 54 */ 55 public static final String TYPE_TEXT_PLAIN = "text/plain"; 56 /** 57 * The <code>message/rfc822</code> MIME type. 58 */ 59 public static final String TYPE_MESSAGE_RFC822 = "message/rfc822"; 60 /** 61 * The name of the <code>boundary</code> parameter. 62 */ 63 public static final String PARAM_BOUNDARY = "boundary"; 64 /** 65 * The name of the <code>charset</code> parameter. 66 */ 67 public static final String PARAM_CHARSET = "charset"; 68 69 private String mimeType = ""; 70 private Map parameters = null; 71 private ParseException parseException; 72 73 protected ContentTypeField(String name, String body, String raw, String mimeType, Map parameters, ParseException parseException) { 74 super(name, body, raw); 75 this.mimeType = mimeType; 76 this.parameters = parameters; 77 this.parseException = parseException; 78 } 79 80 /** 81 * Gets the exception that was raised during parsing of 82 * the field value, if any; otherwise, null. 83 */ 84 public ParseException getParseException() { 85 return parseException; 86 } 87 88 /** 89 * Gets the MIME type defined in this Content-Type field. 90 * 91 * @return the MIME type or an empty string if not set. 92 */ 93 public String getMimeType() { 94 return mimeType; 95 } 96 97 /** 98 * Gets the MIME type defined in the child's 99 * Content-Type field or derives a MIME type from the parent 100 * if child is <code>null</code> or hasn't got a MIME type value set. 101 * If child's MIME type is multipart but no boundary 102 * has been set the MIME type of child will be derived from 103 * the parent. 104 * 105 * @param child the child. 106 * @param parent the parent. 107 * @return the MIME type. 108 */ 109 public static String getMimeType(ContentTypeField child, 110 ContentTypeField parent) { 111 112 if (child == null || child.getMimeType().length() == 0 113 || child.isMultipart() && child.getBoundary() == null) { 114 115 if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) { 116 return TYPE_MESSAGE_RFC822; 117 } else { 118 return TYPE_TEXT_PLAIN; 119 } 120 } 121 122 return child.getMimeType(); 123 } 124 125 /** 126 * Gets the value of a parameter. Parameter names are case-insensitive. 127 * 128 * @param name the name of the parameter to get. 129 * @return the parameter value or <code>null</code> if not set. 130 */ 131 public String getParameter(String name) { 132 return parameters != null 133 ? (String) parameters.get(name.toLowerCase()) 134 : null; 135 } 136 137 /** 138 * Gets all parameters. 139 * 140 * @return the parameters. 141 */ 142 public Map getParameters() { 143 return parameters != null 144 ? Collections.unmodifiableMap(parameters) 145 : Collections.EMPTY_MAP; 146 } 147 148 /** 149 * Gets the value of the <code>boundary</code> parameter if set. 150 * 151 * @return the <code>boundary</code> parameter value or <code>null</code> 152 * if not set. 153 */ 154 public String getBoundary() { 155 return getParameter(PARAM_BOUNDARY); 156 } 157 158 /** 159 * Gets the value of the <code>charset</code> parameter if set. 160 * 161 * @return the <code>charset</code> parameter value or <code>null</code> 162 * if not set. 163 */ 164 public String getCharset() { 165 return getParameter(PARAM_CHARSET); 166 } 167 168 /** 169 * Gets the value of the <code>charset</code> parameter if set for the 170 * given field. Returns the default <code>us-ascii</code> if not set or if 171 * <code>f</code> is <code>null</code>. 172 * 173 * @return the <code>charset</code> parameter value. 174 */ 175 public static String getCharset(ContentTypeField f) { 176 if (f != null) { 177 if (f.getCharset() != null && f.getCharset().length() > 0) { 178 return f.getCharset(); 179 } 180 } 181 return "us-ascii"; 182 } 183 184 /** 185 * Determines if the MIME type of this field matches the given one. 186 * 187 * @param mimeType the MIME type to match against. 188 * @return <code>true</code> if the MIME type of this field matches, 189 * <code>false</code> otherwise. 190 */ 191 public boolean isMimeType(String mimeType) { 192 return this.mimeType.equalsIgnoreCase(mimeType); 193 } 194 195 /** 196 * Determines if the MIME type of this field is <code>multipart/*</code>. 197 * 198 * @return <code>true</code> if this field is has a <code>multipart/*</code> 199 * MIME type, <code>false</code> otherwise. 200 */ 201 public boolean isMultipart() { 202 return mimeType.startsWith(TYPE_MULTIPART_PREFIX); 203 } 204 205 public static class Parser implements FieldParser { 206 private static Log log = LogFactory.getLog(Parser.class); 207 208 public Field parse(final String name, final String body, final String raw) { 209 ParseException parseException = null; 210 String mimeType = ""; 211 Map parameters = null; 212 213 ContentTypeParser parser = new ContentTypeParser(new StringReader(body)); 214 try { 215 parser.parseAll(); 216 } 217 catch (ParseException e) { 218 if (log.isDebugEnabled()) { 219 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 220 } 221 parseException = e; 222 } 223 catch (TokenMgrError e) { 224 if (log.isDebugEnabled()) { 225 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 226 } 227 parseException = new ParseException(e.getMessage()); 228 } 229 230 try { 231 final String type = parser.getType(); 232 final String subType = parser.getSubType(); 233 234 if (type != null && subType != null) { 235 mimeType = (type + "/" + parser.getSubType()).toLowerCase(); 236 237 ArrayList paramNames = parser.getParamNames(); 238 ArrayList paramValues = parser.getParamValues(); 239 240 if (paramNames != null && paramValues != null) { 241 for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) { 242 if (parameters == null) 243 parameters = new HashMap((int)(paramNames.size() * 1.3 + 1)); 244 String paramName = ((String)paramNames.get(i)).toLowerCase(); 245 String paramValue = ((String)paramValues.get(i)); 246 parameters.put(paramName, paramValue); 247 } 248 } 249 } 250 } 251 catch (NullPointerException npe) { 252 } 253 return new ContentTypeField(name, body, raw, mimeType, parameters, parseException); 254 } 255 } 256} 257