View Javadoc

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  
20  package org.apache.james.mime4j.field;
21  
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.james.mime4j.MimeException;
26  import org.apache.james.mime4j.util.ByteSequence;
27  import org.apache.james.mime4j.util.ContentUtil;
28  import org.apache.james.mime4j.util.MimeUtil;
29  
30  /**
31   * The base class of all field classes.
32   */
33  public abstract class AbstractField implements ParsedField {
34  
35      private static final Pattern FIELD_NAME_PATTERN = Pattern
36              .compile("^([\\x21-\\x39\\x3b-\\x7e]+):");
37  
38      private static final DefaultFieldParser parser = new DefaultFieldParser();
39      
40      private final String name;
41      private final String body;
42      private final ByteSequence raw;
43      
44      protected AbstractField(final String name, final String body, final ByteSequence raw) {
45          this.name = name;
46          this.body = body;
47          this.raw = raw;
48      }
49  
50      /**
51       * Parses the given byte sequence and returns an instance of the
52       * <code>Field</code> class. The type of the class returned depends on the
53       * field name; see {@link #parse(String)} for a table of field names and
54       * their corresponding classes.
55       * 
56       * @param raw the bytes to parse.
57       * @return a <code>ParsedField</code> instance.
58       * @throws MimeException if the raw string cannot be split into field name and body.
59       * @see #isValidField()
60       */
61      public static ParsedField parse(final ByteSequence raw) throws MimeException {
62          String rawStr = ContentUtil.decode(raw);
63          return parse(raw, rawStr);
64      }
65  
66      /**
67       * Parses the given string and returns an instance of the 
68       * <code>Field</code> class. The type of the class returned depends on
69       * the field name:
70       * <p>
71       * <table>
72       *   <tr><th>Class returned</th><th>Field names</th></tr>
73       *   <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr>
74       *   <tr><td>{@link ContentTransferEncodingField}</td><td>Content-Transfer-Encoding</td></tr>
75       *   <tr><td>{@link ContentDispositionField}</td><td>Content-Disposition</td></tr>
76       *   <tr><td>{@link DateTimeField}</td><td>Date, Resent-Date</td></tr>
77       *   <tr><td>{@link MailboxField}</td><td>Sender, Resent-Sender</td></tr>
78       *   <tr><td>{@link MailboxListField}</td><td>From, Resent-From</td></tr>
79       *   <tr><td>{@link AddressListField}</td><td>To, Cc, Bcc, Reply-To, Resent-To, Resent-Cc, Resent-Bcc</td></tr>
80       *   <tr><td>{@link UnstructuredField}</td><td>Subject and others</td></tr>
81       * </table>
82       * 
83       * @param rawStr the string to parse.
84       * @return a <code>ParsedField</code> instance.
85       * @throws MimeException if the raw string cannot be split into field name and body.
86       * @see #isValidField()
87       */
88      public static ParsedField parse(final String rawStr) throws MimeException {
89          ByteSequence raw = ContentUtil.encode(rawStr);
90          return parse(raw, rawStr);
91      }
92  
93      /**
94       * Gets the default parser used to parse fields.
95       * 
96       * @return the default field parser
97       */
98      public static DefaultFieldParser getParser() {
99          return parser;
100     }
101     
102     /**
103      * Gets the name of the field (<code>Subject</code>, 
104      * <code>From</code>, etc).
105      * 
106      * @return the field name.
107      */
108     public String getName() {
109         return name;
110     }
111     
112     /**
113      * Gets the original raw field string.
114      * 
115      * @return the original raw field string.
116      */
117     public ByteSequence getRaw() {
118         return raw;
119     }
120     
121     /**
122      * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field 
123      * body string.
124      * 
125      * @return the unfolded unparsed field body string.
126      */
127     public String getBody() {
128         return body;
129     }
130 
131     /**
132      * @see ParsedField#isValidField() 
133      */
134     public boolean isValidField() {
135         return getParseException() == null;
136     }
137 
138     /**
139      * @see ParsedField#getParseException() 
140      */
141     public ParseException getParseException() {
142         return null;
143     }
144 
145     @Override
146     public String toString() {
147         return name + ": " + body;
148     }
149 
150     private static ParsedField parse(final ByteSequence raw, final String rawStr)
151             throws MimeException {
152         /*
153          * Unfold the field.
154          */
155         final String unfolded = MimeUtil.unfold(rawStr);
156 
157         /*
158          * Split into name and value.
159          */
160         final Matcher fieldMatcher = FIELD_NAME_PATTERN.matcher(unfolded);
161         if (!fieldMatcher.find()) {
162             throw new MimeException("Invalid field in string");
163         }
164         final String name = fieldMatcher.group(1);
165 
166         String body = unfolded.substring(fieldMatcher.end());
167         if (body.length() > 0 && body.charAt(0) == ' ') {
168             body = body.substring(1);
169         }
170 
171         return parser.parse(name, body, raw);
172     }
173 
174 }