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    }