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
020package org.apache.james.mime4j.message;
021
022import java.util.HashMap;
023import java.util.Locale;
024import java.util.Map;
025
026import org.apache.james.mime4j.MimeException;
027import org.apache.james.mime4j.codec.DecodeMonitor;
028import org.apache.james.mime4j.dom.FieldParser;
029import org.apache.james.mime4j.dom.field.ContentTypeField;
030import org.apache.james.mime4j.dom.field.FieldName;
031import org.apache.james.mime4j.dom.field.ParsedField;
032import org.apache.james.mime4j.field.DefaultFieldParser;
033import org.apache.james.mime4j.stream.BodyDescriptor;
034import org.apache.james.mime4j.stream.BodyDescriptorBuilder;
035import org.apache.james.mime4j.stream.Field;
036import org.apache.james.mime4j.stream.RawField;
037import org.apache.james.mime4j.util.MimeUtil;
038
039/**
040 * Default {@link BodyDescriptorBuilder} implementation.
041 */
042public class DefaultBodyDescriptorBuilder implements BodyDescriptorBuilder {
043
044    private static final String CONTENT_TYPE = FieldName.CONTENT_TYPE.toLowerCase(Locale.US);
045
046    private static final String US_ASCII = "us-ascii";
047    private static final String SUB_TYPE_EMAIL = "rfc822";
048    private static final String MEDIA_TYPE_TEXT = "text";
049    private static final String MEDIA_TYPE_MESSAGE = "message";
050    private static final String EMAIL_MESSAGE_MIME_TYPE = MEDIA_TYPE_MESSAGE + "/" + SUB_TYPE_EMAIL;
051    private static final String DEFAULT_SUB_TYPE = "plain";
052    private static final String DEFAULT_MEDIA_TYPE = MEDIA_TYPE_TEXT;
053    private static final String DEFAULT_MIME_TYPE = DEFAULT_MEDIA_TYPE + "/" + DEFAULT_SUB_TYPE;
054
055    private final String parentMimeType;
056    private final DecodeMonitor monitor;
057    private final FieldParser<? extends ParsedField> fieldParser;
058    private final Map<String, ParsedField> fields;
059
060    /**
061     * Creates a new root <code>BodyDescriptor</code> instance.
062     */
063    public DefaultBodyDescriptorBuilder() {
064        this(null);
065    }
066
067    public DefaultBodyDescriptorBuilder(final String parentMimeType) {
068        this(parentMimeType, null, null);
069    }
070
071    /**
072     * Creates a new <code>BodyDescriptor</code> instance.
073     */
074    public DefaultBodyDescriptorBuilder(
075            final String parentMimeType,
076            final FieldParser<? extends ParsedField> fieldParser,
077            final DecodeMonitor monitor) {
078        super();
079        this.parentMimeType = parentMimeType;
080        this.fieldParser = fieldParser != null ? fieldParser : DefaultFieldParser.getParser();
081        this.monitor = monitor != null ? monitor : DecodeMonitor.SILENT;
082        this.fields = new HashMap<String, ParsedField>();
083    }
084
085    public void reset() {
086        fields.clear();
087    }
088
089    public Field addField(final RawField rawfield) throws MimeException {
090        ParsedField field = fieldParser.parse(rawfield, monitor);
091        String name = field.getName().toLowerCase(Locale.US);
092        if (!fields.containsKey(name)) {
093            fields.put(name, field);
094        }
095        return field;
096    }
097
098    public BodyDescriptor build() {
099        String actualMimeType = null;
100        String actualMediaType = null;
101        String actualSubType = null;
102        String actualCharset = null;
103        String actualBoundary = null;
104        ContentTypeField contentTypeField = (ContentTypeField) fields.get(CONTENT_TYPE);
105        if (contentTypeField != null) {
106            actualMimeType = contentTypeField.getMimeType();
107            actualMediaType = contentTypeField.getMediaType();
108            actualSubType = contentTypeField.getSubType();
109            actualCharset = contentTypeField.getCharset();
110            actualBoundary = contentTypeField.getBoundary();
111        }
112        if (actualMimeType == null) {
113            if (MimeUtil.isSameMimeType("multipart/digest", parentMimeType)) {
114                actualMimeType = EMAIL_MESSAGE_MIME_TYPE;
115                actualMediaType = MEDIA_TYPE_MESSAGE;
116                actualSubType = SUB_TYPE_EMAIL;
117            } else {
118                actualMimeType = DEFAULT_MIME_TYPE;
119                actualMediaType = DEFAULT_MEDIA_TYPE;
120                actualSubType = DEFAULT_SUB_TYPE;
121            }
122        }
123        if (actualCharset == null && MEDIA_TYPE_TEXT.equals(actualMediaType)) {
124            actualCharset = US_ASCII;
125        }
126        if (!MimeUtil.isMultipart(actualMimeType)) {
127            actualBoundary = null;
128        }
129        return new MaximalBodyDescriptor(
130                actualMimeType, actualMediaType, actualSubType, actualBoundary, actualCharset,
131                fields);
132    }
133
134    public BodyDescriptorBuilder newChild() {
135        String actualMimeType;
136        ContentTypeField contentTypeField = (ContentTypeField) fields.get(CONTENT_TYPE);
137        if (contentTypeField != null) {
138            actualMimeType = contentTypeField.getMimeType();
139        } else {
140            if (MimeUtil.isSameMimeType("multipart/digest", parentMimeType)) {
141                actualMimeType = EMAIL_MESSAGE_MIME_TYPE;
142            } else {
143                actualMimeType = DEFAULT_MIME_TYPE;
144            }
145        }
146        return new DefaultBodyDescriptorBuilder(actualMimeType, fieldParser, monitor);
147    }
148
149}