001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020 package org.apache.james.mime4j.field;
021
022 import java.io.StringReader;
023 import java.util.Collections;
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027
028 import org.apache.james.mime4j.codec.DecodeMonitor;
029 import org.apache.james.mime4j.dom.FieldParser;
030 import org.apache.james.mime4j.dom.field.ContentTypeField;
031 import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;
032 import org.apache.james.mime4j.field.contenttype.parser.ParseException;
033 import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
034 import org.apache.james.mime4j.stream.Field;
035
036 /**
037 * Represents a <code>Content-Type</code> field.
038 */
039 public class ContentTypeFieldImpl extends AbstractField implements ContentTypeField {
040 private boolean parsed = false;
041
042 private String mimeType = null;
043 private String mediaType = null;
044 private String subType = null;
045 private Map<String, String> parameters = new HashMap<String, String>();
046 private ParseException parseException;
047
048 ContentTypeFieldImpl(Field rawField, DecodeMonitor monitor) {
049 super(rawField, monitor);
050 }
051
052 /**
053 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getParseException()
054 */
055 @Override
056 public ParseException getParseException() {
057 if (!parsed)
058 parse();
059
060 return parseException;
061 }
062
063 /**
064 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getMimeType()
065 */
066 public String getMimeType() {
067 if (!parsed)
068 parse();
069
070 return mimeType;
071 }
072
073 /**
074 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getMediaType()
075 */
076 public String getMediaType() {
077 if (!parsed)
078 parse();
079
080 return mediaType;
081 }
082
083 /**
084 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getSubType()
085 */
086 public String getSubType() {
087 if (!parsed)
088 parse();
089
090 return subType;
091 }
092
093 /**
094 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getParameter(java.lang.String)
095 */
096 public String getParameter(String name) {
097 if (!parsed)
098 parse();
099
100 return parameters.get(name.toLowerCase());
101 }
102
103 /**
104 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getParameters()
105 */
106 public Map<String, String> getParameters() {
107 if (!parsed)
108 parse();
109
110 return Collections.unmodifiableMap(parameters);
111 }
112
113 /**
114 * @see org.apache.james.mime4j.dom.field.ContentTypeField#isMimeType(java.lang.String)
115 */
116 public boolean isMimeType(String mimeType) {
117 if (!parsed)
118 parse();
119
120 return this.mimeType != null && this.mimeType.equalsIgnoreCase(mimeType);
121 }
122
123 /**
124 * @see org.apache.james.mime4j.dom.field.ContentTypeField#isMultipart()
125 */
126 public boolean isMultipart() {
127 if (!parsed)
128 parse();
129
130 return this.mimeType != null && mimeType.startsWith(TYPE_MULTIPART_PREFIX);
131 }
132
133 /**
134 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getBoundary()
135 */
136 public String getBoundary() {
137 return getParameter(PARAM_BOUNDARY);
138 }
139
140 /**
141 * @see org.apache.james.mime4j.dom.field.ContentTypeField#getCharset()
142 */
143 public String getCharset() {
144 return getParameter(PARAM_CHARSET);
145 }
146
147 /**
148 * Gets the MIME type defined in the child's Content-Type field or derives a
149 * MIME type from the parent if child is <code>null</code> or hasn't got a
150 * MIME type value set. If child's MIME type is multipart but no boundary
151 * has been set the MIME type of child will be derived from the parent.
152 *
153 * @param child
154 * the child.
155 * @param parent
156 * the parent.
157 * @return the MIME type.
158 */
159 public static String getMimeType(ContentTypeField child,
160 ContentTypeField parent) {
161 if (child == null || child.getMimeType() == null
162 || child.isMultipart() && child.getBoundary() == null) {
163
164 if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {
165 return TYPE_MESSAGE_RFC822;
166 } else {
167 return TYPE_TEXT_PLAIN;
168 }
169 }
170
171 return child.getMimeType();
172 }
173
174 /**
175 * Gets the value of the <code>charset</code> parameter if set for the
176 * given field. Returns the default <code>us-ascii</code> if not set or if
177 * <code>f</code> is <code>null</code>.
178 *
179 * @return the <code>charset</code> parameter value.
180 */
181 public static String getCharset(ContentTypeField f) {
182 if (f != null) {
183 String charset = f.getCharset();
184 if (charset != null && charset.length() > 0) {
185 return charset;
186 }
187 }
188 return "us-ascii";
189 }
190
191 private void parse() {
192 String body = getBody();
193
194 ContentTypeParser parser = new ContentTypeParser(new StringReader(body));
195 try {
196 parser.parseAll();
197 } catch (ParseException e) {
198 parseException = e;
199 } catch (TokenMgrError e) {
200 parseException = new ParseException(e.getMessage());
201 }
202
203 mediaType = parser.getType();
204 subType = parser.getSubType();
205
206 if (mediaType != null && subType != null) {
207 mimeType = (mediaType + "/" + subType).toLowerCase();
208
209 List<String> paramNames = parser.getParamNames();
210 List<String> paramValues = parser.getParamValues();
211
212 if (paramNames != null && paramValues != null) {
213 final int len = Math.min(paramNames.size(), paramValues.size());
214 for (int i = 0; i < len; i++) {
215 String paramName = paramNames.get(i).toLowerCase();
216 String paramValue = paramValues.get(i);
217 parameters.put(paramName, paramValue);
218 }
219 }
220 }
221
222 parsed = true;
223 }
224
225 public static final FieldParser<ContentTypeField> PARSER = new FieldParser<ContentTypeField>() {
226
227 public ContentTypeField parse(final Field rawField, final DecodeMonitor monitor) {
228 return new ContentTypeFieldImpl(rawField, monitor);
229 }
230
231 };
232 }