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