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.util.regex.Matcher;
23import java.util.regex.Pattern;
24
25/**
26 * The base class of all field classes.
27 *
28 *
29 * @version $Id: Field.java,v 1.6 2004/10/25 07:26:46 ntherning Exp $
30 */
31public abstract class Field {
32    public static final String SENDER = "Sender";
33    public static final String FROM = "From";
34    public static final String TO = "To";
35    public static final String CC = "Cc";
36    public static final String BCC = "Bcc";
37    public static final String REPLY_TO = "Reply-To";
38    public static final String RESENT_SENDER = "Resent-Sender";
39    public static final String RESENT_FROM = "Resent-From";
40    public static final String RESENT_TO = "Resent-To";
41    public static final String RESENT_CC = "Resent-Cc";
42    public static final String RESENT_BCC = "Resent-Bcc";
43
44    public static final String DATE = "Date";
45    public static final String RESENT_DATE = "Resent-Date";
46
47    public static final String SUBJECT = "Subject";
48    public static final String CONTENT_TYPE = "Content-Type";
49    public static final String CONTENT_TRANSFER_ENCODING =
50                                        "Content-Transfer-Encoding";
51
52    private static final String FIELD_NAME_PATTERN =
53        "^([\\x21-\\x39\\x3b-\\x7e]+)[ \t]*:";
54    private static final Pattern fieldNamePattern =
55        Pattern.compile(FIELD_NAME_PATTERN);
56
57    private static final DefaultFieldParser parser = new DefaultFieldParser();
58
59    private final String name;
60    private final String body;
61    private final String raw;
62
63    protected Field(final String name, final String body, final String raw) {
64        this.name = name;
65        this.body = body;
66        this.raw = raw;
67    }
68
69    /**
70     * Parses the given string and returns an instance of the
71     * <code>Field</code> class. The type of the class returned depends on
72     * the field name:
73     * <table>
74     *      <tr>
75     *          <td><em>Field name</em></td><td><em>Class returned</em></td>
76     *          <td>Content-Type</td><td>org.apache.james.mime4j.field.ContentTypeField</td>
77     *          <td>other</td><td>org.apache.james.mime4j.field.UnstructuredField</td>
78     *      </tr>
79     * </table>
80     *
81     * @param s the string to parse.
82     * @return a <code>Field</code> instance.
83     * @throws IllegalArgumentException on parse errors.
84     */
85    public static Field parse(final String raw) {
86
87        /*
88         * Unfold the field.
89         */
90        final String unfolded = raw.replaceAll("\r|\n", "");
91
92        /*
93         * Split into name and value.
94         */
95        final Matcher fieldMatcher = fieldNamePattern.matcher(unfolded);
96        if (!fieldMatcher.find()) {
97            throw new IllegalArgumentException("Invalid field in string");
98        }
99        final String name = fieldMatcher.group(1);
100
101        String body = unfolded.substring(fieldMatcher.end());
102        if (body.length() > 0 && body.charAt(0) == ' ') {
103            body = body.substring(1);
104        }
105
106        return parser.parse(name, body, raw);
107    }
108
109    /**
110     * Gets the default parser used to parse fields.
111     * @return the default field parser
112     */
113    public static DefaultFieldParser getParser() {
114        return parser;
115    }
116
117    /**
118     * Gets the name of the field (<code>Subject</code>,
119     * <code>From</code>, etc).
120     *
121     * @return the field name.
122     */
123    public String getName() {
124        return name;
125    }
126
127    /**
128     * Gets the original raw field string.
129     *
130     * @return the original raw field string.
131     */
132    public String getRaw() {
133        return raw;
134    }
135
136    /**
137     * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field
138     * body string.
139     *
140     * @return the unfolded unparsed field body string.
141     */
142    public String getBody() {
143        return body;
144    }
145
146    /**
147     * Determines if this is a <code>Content-Type</code> field.
148     *
149     * @return <code>true</code> if this is a <code>Content-Type</code> field,
150     *         <code>false</code> otherwise.
151     */
152    public boolean isContentType() {
153        return CONTENT_TYPE.equalsIgnoreCase(name);
154    }
155
156    /**
157     * Determines if this is a <code>Subject</code> field.
158     *
159     * @return <code>true</code> if this is a <code>Subject</code> field,
160     *         <code>false</code> otherwise.
161     */
162    public boolean isSubject() {
163        return SUBJECT.equalsIgnoreCase(name);
164    }
165
166    /**
167     * Determines if this is a <code>From</code> field.
168     *
169     * @return <code>true</code> if this is a <code>From</code> field,
170     *         <code>false</code> otherwise.
171     */
172    public boolean isFrom() {
173        return FROM.equalsIgnoreCase(name);
174    }
175
176    /**
177     * Determines if this is a <code>To</code> field.
178     *
179     * @return <code>true</code> if this is a <code>To</code> field,
180     *         <code>false</code> otherwise.
181     */
182    public boolean isTo() {
183        return TO.equalsIgnoreCase(name);
184    }
185
186    /**
187     * @see #getRaw()
188     */
189    public String toString() {
190        return raw;
191    }
192}
193