james-mime4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mwiederk...@apache.org
Subject svn commit: r778457 [8/13] - in /james/mime4j/trunk: benchmark/src/main/java/org/apache/james/mime4j/ core/src/main/java/org/apache/james/mime4j/ core/src/main/java/org/apache/james/mime4j/codec/ core/src/main/java/org/apache/james/mime4j/descriptor/ c...
Date Mon, 25 May 2009 17:20:52 GMT
Propchange: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/CharsetUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ContentUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/EmptyByteSequence.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java?rev=778457&r1=778456&r2=778457&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java Mon May 25 17:20:48 2009
@@ -1,545 +1,545 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.mime4j.util;
-
-import java.text.DateFormat;
-import java.text.FieldPosition;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Random;
-import java.util.TimeZone;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A utility class, which provides some MIME related application logic.
- */
-public final class MimeUtil {
-    private static final Log log = LogFactory.getLog(MimeUtil.class);
-    
-    /**
-     * The <code>quoted-printable</code> encoding.
-     */
-    public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";
-    /**
-     * The <code>binary</code> encoding.
-     */
-    public static final String ENC_BINARY = "binary";
-    /**
-     * The <code>base64</code> encoding.
-     */
-    public static final String ENC_BASE64 = "base64";
-    /**
-     * The <code>8bit</code> encoding.
-     */
-    public static final String ENC_8BIT = "8bit";
-    /**
-     * The <code>7bit</code> encoding.
-     */
-    public static final String ENC_7BIT = "7bit";
-
-    /** <code>MIME-Version</code> header name (lowercase) */
-    public static final String MIME_HEADER_MIME_VERSION = "mime-version";
-    /** <code>Content-ID</code> header name (lowercase) */
-    public static final String MIME_HEADER_CONTENT_ID = "content-id";
-    /** <code>Content-Description</code> header name (lowercase) */
-    public static final String MIME_HEADER_CONTENT_DESCRIPTION = "content-description";
-    /** 
-     * <code>Content-Disposition</code> header name (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String MIME_HEADER_CONTENT_DISPOSITION = "content-disposition";
-    /** 
-     * <code>Content-Disposition</code> filename parameter (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String PARAM_FILENAME = "filename";
-    /** 
-     * <code>Content-Disposition</code> modification-date parameter (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String PARAM_MODIFICATION_DATE = "modification-date";
-    /** 
-     * <code>Content-Disposition</code> creation-date parameter (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String PARAM_CREATION_DATE = "creation-date";
-    /** 
-     * <code>Content-Disposition</code> read-date parameter (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String PARAM_READ_DATE = "read-date";
-    /** 
-     * <code>Content-Disposition</code> size parameter (lowercase). 
-     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
-     */
-    public static final String PARAM_SIZE = "size";
-    /**
-     * <code>Content-Langauge</code> header (lower case).
-     * See <a href='http://www.faqs.org/rfcs/rfc4646.html'>RFC4646</a>.
-     */
-    public static final String MIME_HEADER_LANGAUGE = "content-language";
-    /**
-     * <code>Content-Location</code> header (lower case).
-     * See <a href='http://www.faqs.org/rfcs/rfc2557.html'>RFC2557</a>.
-     */
-    public static final String MIME_HEADER_LOCATION = "content-location";
-    /**
-     * <code>Content-MD5</code> header (lower case).
-     * See <a href='http://www.faqs.org/rfcs/rfc1864.html'>RFC1864</a>.
-     */
-    public static final String MIME_HEADER_MD5 = "content-md5";
-
-    // used to create unique ids
-    private static final Random random = new Random();
-    
-    // used to create unique ids
-    private static int counter = 0;
-
-    private MimeUtil() {
-        // this is an utility class to be used statically.
-        // this constructor protect from instantiation.
-    }
-    
-    /**
-     * Returns, whether the given two MIME types are identical.
-     */
-    public static boolean isSameMimeType(String pType1, String pType2) {
-        return pType1 != null  &&  pType2 != null  &&  pType1.equalsIgnoreCase(pType2);
-    }
-
-    /**
-     * Returns true, if the given MIME type is that of a message. 
-     */
-    public static boolean isMessage(String pMimeType) {
-        return pMimeType != null  &&  pMimeType.equalsIgnoreCase("message/rfc822");
-    }
-
-    /**
-     * Return true, if the given MIME type indicates a multipart entity.
-     */
-    public static boolean isMultipart(String pMimeType) {
-        return pMimeType != null  &&  pMimeType.toLowerCase().startsWith("multipart/");
-    }
-
-    /**
-     * Returns, whether the given transfer-encoding is "base64".
-     */
-    public static boolean isBase64Encoding(String pTransferEncoding) {
-        return ENC_BASE64.equalsIgnoreCase(pTransferEncoding);
-    }
-
-    /**
-     * Returns, whether the given transfer-encoding is "quoted-printable".
-     */
-    public static boolean isQuotedPrintableEncoded(String pTransferEncoding) {
-        return ENC_QUOTED_PRINTABLE.equalsIgnoreCase(pTransferEncoding);
-    }
-
-    /**
-     * <p>Parses a complex field value into a map of key/value pairs. You may
-     * use this, for example, to parse a definition like
-     * <pre>
-     *   text/plain; charset=UTF-8; boundary=foobar
-     * </pre>
-     * The above example would return a map with the keys "", "charset",
-     * and "boundary", and the values "text/plain", "UTF-8", and "foobar".
-     * </p><p>
-     * Header value will be unfolded and excess white space trimmed.
-     * </p>
-     * @param pValue The field value to parse.
-     * @return The result map; use the key "" to retrieve the first value.
-     */
-    @SuppressWarnings("fallthrough")
-    public static Map<String, String> getHeaderParams(String pValue) {
-        pValue = pValue.trim();
-
-        pValue = unfold(pValue);
-        
-        Map<String, String> result = new HashMap<String, String>();
-
-        // split main value and parameters
-        String main;
-        String rest;
-        if (pValue.indexOf(";") == -1) {
-            main = pValue;
-            rest = null;
-        } else {
-            main = pValue.substring(0, pValue.indexOf(";"));
-            rest = pValue.substring(main.length() + 1);
-        }
-
-        result.put("", main);
-        if (rest != null) {
-            char[] chars = rest.toCharArray();
-            StringBuilder paramName = new StringBuilder(64);
-            StringBuilder paramValue = new StringBuilder(64);
-
-            final byte READY_FOR_NAME = 0;
-            final byte IN_NAME = 1;
-            final byte READY_FOR_VALUE = 2;
-            final byte IN_VALUE = 3;
-            final byte IN_QUOTED_VALUE = 4;
-            final byte VALUE_DONE = 5;
-            final byte ERROR = 99;
-
-            byte state = READY_FOR_NAME;
-            boolean escaped = false;
-            for (char c : chars) {
-                switch (state) {
-                    case ERROR:
-                        if (c == ';')
-                            state = READY_FOR_NAME;
-                        break;
-
-                    case READY_FOR_NAME:
-                        if (c == '=') {
-                            log.error("Expected header param name, got '='");
-                            state = ERROR;
-                            break;
-                        }
-
-                        paramName.setLength(0);
-                        paramValue.setLength(0);
-
-                        state = IN_NAME;
-                        // fall-through
-
-                    case IN_NAME:
-                        if (c == '=') {
-                            if (paramName.length() == 0)
-                                state = ERROR;
-                            else
-                                state = READY_FOR_VALUE;
-                            break;
-                        }
-
-                        // not '='... just add to name
-                        paramName.append(c);
-                        break;
-
-                    case READY_FOR_VALUE:
-                        boolean fallThrough = false;
-                        switch (c) {
-                            case ' ':
-                            case '\t':
-                                break;  // ignore spaces, especially before '"'
-
-                            case '"':
-                                state = IN_QUOTED_VALUE;
-                                break;
-
-                            default:
-                                state = IN_VALUE;
-                                fallThrough = true;
-                                break;
-                        }
-                        if (!fallThrough)
-                            break;
-
-                        // fall-through
-
-                    case IN_VALUE:
-                        fallThrough = false;
-                        switch (c) {
-                            case ';':
-                            case ' ':
-                            case '\t':
-                                result.put(
-                                   paramName.toString().trim().toLowerCase(),
-                                   paramValue.toString().trim());
-                                state = VALUE_DONE;
-                                fallThrough = true;
-                                break;
-                            default:
-                                paramValue.append(c);
-                                break;
-                        }
-                        if (!fallThrough)
-                            break;
-
-                    case VALUE_DONE:
-                        switch (c) {
-                            case ';':
-                                state = READY_FOR_NAME;
-                                break;
-
-                            case ' ':
-                            case '\t':
-                                break;
-
-                            default:
-                                state = ERROR;
-                                break;
-                        }
-                        break;
-                        
-                    case IN_QUOTED_VALUE:
-                        switch (c) {
-                            case '"':
-                                if (!escaped) {
-                                    // don't trim quoted strings; the spaces could be intentional.
-                                    result.put(
-                                            paramName.toString().trim().toLowerCase(),
-                                            paramValue.toString());
-                                    state = VALUE_DONE;
-                                } else {
-                                    escaped = false;
-                                    paramValue.append(c);                                    
-                                }
-                                break;
-                                
-                            case '\\':
-                                if (escaped) {
-                                    paramValue.append('\\');
-                                }
-                                escaped = !escaped;
-                                break;
-
-                            default:
-                                if (escaped) {
-                                    paramValue.append('\\');
-                                }
-                                escaped = false;
-                                paramValue.append(c);
-                                break;
-                        }
-                        break;
-
-                }
-            }
-
-            // done looping.  check if anything is left over.
-            if (state == IN_VALUE) {
-                result.put(
-                        paramName.toString().trim().toLowerCase(),
-                        paramValue.toString().trim());
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Creates a new unique message boundary string that can be used as boundary
-     * parameter for the Content-Type header field of a message.
-     * 
-     * @return a new unique message boundary string.
-     */
-    public static String createUniqueBoundary() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("-=Part.");
-        sb.append(Integer.toHexString(nextCounterValue()));
-        sb.append('.');
-        sb.append(Long.toHexString(random.nextLong()));
-        sb.append('.');
-        sb.append(Long.toHexString(System.currentTimeMillis()));
-        sb.append('.');
-        sb.append(Long.toHexString(random.nextLong()));
-        sb.append("=-");
-        return sb.toString();
-    }
-
-    /**
-     * Creates a new unique message identifier that can be used in message
-     * header field such as Message-ID or In-Reply-To. If the given host name is
-     * not <code>null</code> it will be used as suffix for the message ID
-     * (following an at sign).
-     * 
-     * The resulting string is enclosed in angle brackets (&lt; and &gt;);
-     * 
-     * @param hostName host name to be included in the message ID or
-     *            <code>null</code> if no host name should be included.
-     * @return a new unique message identifier.
-     */
-    public static String createUniqueMessageId(String hostName) {
-        StringBuilder sb = new StringBuilder("<Mime4j.");
-        sb.append(Integer.toHexString(nextCounterValue()));
-        sb.append('.');
-        sb.append(Long.toHexString(random.nextLong()));
-        sb.append('.');
-        sb.append(Long.toHexString(System.currentTimeMillis()));
-        if (hostName != null) {
-            sb.append('@');
-            sb.append(hostName);
-        }
-        sb.append('>');
-        return sb.toString();
-    }
-
-    /**
-     * Formats the specified date into a RFC 822 date-time string.
-     * 
-     * @param date
-     *            date to be formatted into a string.
-     * @param zone
-     *            the time zone to use or <code>null</code> to use the default
-     *            time zone.
-     * @return the formatted time string.
-     */
-    public static String formatDate(Date date, TimeZone zone) {
-        DateFormat df = RFC822_DATE_FORMAT.get();
-
-        if (zone == null) {
-            df.setTimeZone(TimeZone.getDefault());
-        } else {
-            df.setTimeZone(zone);
-        }
-
-        return df.format(date);
-    }
-
-    /**
-     * Splits the specified string into a multiple-line representation with
-     * lines no longer than 76 characters (because the line might contain
-     * encoded words; see <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC
-     * 2047</a> section 2). If the string contains non-whitespace sequences
-     * longer than 76 characters a line break is inserted at the whitespace
-     * character following the sequence resulting in a line longer than 76
-     * characters.
-     * 
-     * @param s
-     *            string to split.
-     * @param usedCharacters
-     *            number of characters already used up. Usually the number of
-     *            characters for header field name plus colon and one space.
-     * @return a multiple-line representation of the given string.
-     */
-    public static String fold(String s, int usedCharacters) {
-        final int maxCharacters = 76;
-
-        final int length = s.length();
-        if (usedCharacters + length <= maxCharacters)
-            return s;
-
-        StringBuilder sb = new StringBuilder();
-
-        int lastLineBreak = -usedCharacters;
-        int wspIdx = indexOfWsp(s, 0);
-        while (true) {
-            if (wspIdx == length) {
-                sb.append(s.substring(Math.max(0, lastLineBreak)));
-                return sb.toString();
-            }
-
-            int nextWspIdx = indexOfWsp(s, wspIdx + 1);
-
-            if (nextWspIdx - lastLineBreak > maxCharacters) {
-                sb.append(s.substring(Math.max(0, lastLineBreak), wspIdx));
-                sb.append("\r\n");
-                lastLineBreak = wspIdx;
-            }
-
-            wspIdx = nextWspIdx;
-        }
-    }
-
-    /**
-     * Unfold a multiple-line representation into a single line.
-     * 
-     * @param s
-     *            string to unfold.
-     * @return unfolded string.
-     */
-    public static String unfold(String s) {
-        final int length = s.length();
-        for (int idx = 0; idx < length; idx++) {
-            char c = s.charAt(idx);
-            if (c == '\r' || c == '\n') {
-                return unfold0(s, idx);
-            }
-        }
-
-        return s;
-    }
-
-    private static String unfold0(String s, int crlfIdx) {
-        final int length = s.length();
-        StringBuilder sb = new StringBuilder(length);
-
-        if (crlfIdx > 0) {
-            sb.append(s.substring(0, crlfIdx));
-        }
-
-        for (int idx = crlfIdx + 1; idx < length; idx++) {
-            char c = s.charAt(idx);
-            if (c != '\r' && c != '\n') {
-                sb.append(c);
-            }
-        }
-
-        return sb.toString();
-    }
-
-    private static int indexOfWsp(String s, int fromIndex) {
-        final int len = s.length();
-        for (int index = fromIndex; index < len; index++) {
-            char c = s.charAt(index);
-            if (c == ' ' || c == '\t')
-                return index;
-        }
-        return len;
-    }
-
-    private static synchronized int nextCounterValue() {
-        return counter++;
-    }
-
-    private static final ThreadLocal<DateFormat> RFC822_DATE_FORMAT = new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            return new Rfc822DateFormat();
-        }
-    };
-
-    private static final class Rfc822DateFormat extends SimpleDateFormat {
-        private static final long serialVersionUID = 1L;
-
-        public Rfc822DateFormat() {
-            super("EEE, d MMM yyyy HH:mm:ss ", Locale.US);
-        }
-
-        @Override
-        public StringBuffer format(Date date, StringBuffer toAppendTo,
-                FieldPosition pos) {
-            StringBuffer sb = super.format(date, toAppendTo, pos);
-
-            int zoneMillis = calendar.get(GregorianCalendar.ZONE_OFFSET);
-            int dstMillis = calendar.get(GregorianCalendar.DST_OFFSET);
-            int minutes = (zoneMillis + dstMillis) / 1000 / 60;
-
-            if (minutes < 0) {
-                sb.append('-');
-                minutes = -minutes;
-            } else {
-                sb.append('+');
-            }
-
-            sb.append(String.format("%02d%02d", minutes / 60, minutes % 60));
-
-            return sb;
-        }
-    }
-}
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mime4j.util;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.TimeZone;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A utility class, which provides some MIME related application logic.
+ */
+public final class MimeUtil {
+    private static final Log log = LogFactory.getLog(MimeUtil.class);
+    
+    /**
+     * The <code>quoted-printable</code> encoding.
+     */
+    public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";
+    /**
+     * The <code>binary</code> encoding.
+     */
+    public static final String ENC_BINARY = "binary";
+    /**
+     * The <code>base64</code> encoding.
+     */
+    public static final String ENC_BASE64 = "base64";
+    /**
+     * The <code>8bit</code> encoding.
+     */
+    public static final String ENC_8BIT = "8bit";
+    /**
+     * The <code>7bit</code> encoding.
+     */
+    public static final String ENC_7BIT = "7bit";
+
+    /** <code>MIME-Version</code> header name (lowercase) */
+    public static final String MIME_HEADER_MIME_VERSION = "mime-version";
+    /** <code>Content-ID</code> header name (lowercase) */
+    public static final String MIME_HEADER_CONTENT_ID = "content-id";
+    /** <code>Content-Description</code> header name (lowercase) */
+    public static final String MIME_HEADER_CONTENT_DESCRIPTION = "content-description";
+    /** 
+     * <code>Content-Disposition</code> header name (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String MIME_HEADER_CONTENT_DISPOSITION = "content-disposition";
+    /** 
+     * <code>Content-Disposition</code> filename parameter (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String PARAM_FILENAME = "filename";
+    /** 
+     * <code>Content-Disposition</code> modification-date parameter (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String PARAM_MODIFICATION_DATE = "modification-date";
+    /** 
+     * <code>Content-Disposition</code> creation-date parameter (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String PARAM_CREATION_DATE = "creation-date";
+    /** 
+     * <code>Content-Disposition</code> read-date parameter (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String PARAM_READ_DATE = "read-date";
+    /** 
+     * <code>Content-Disposition</code> size parameter (lowercase). 
+     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. 
+     */
+    public static final String PARAM_SIZE = "size";
+    /**
+     * <code>Content-Langauge</code> header (lower case).
+     * See <a href='http://www.faqs.org/rfcs/rfc4646.html'>RFC4646</a>.
+     */
+    public static final String MIME_HEADER_LANGAUGE = "content-language";
+    /**
+     * <code>Content-Location</code> header (lower case).
+     * See <a href='http://www.faqs.org/rfcs/rfc2557.html'>RFC2557</a>.
+     */
+    public static final String MIME_HEADER_LOCATION = "content-location";
+    /**
+     * <code>Content-MD5</code> header (lower case).
+     * See <a href='http://www.faqs.org/rfcs/rfc1864.html'>RFC1864</a>.
+     */
+    public static final String MIME_HEADER_MD5 = "content-md5";
+
+    // used to create unique ids
+    private static final Random random = new Random();
+    
+    // used to create unique ids
+    private static int counter = 0;
+
+    private MimeUtil() {
+        // this is an utility class to be used statically.
+        // this constructor protect from instantiation.
+    }
+    
+    /**
+     * Returns, whether the given two MIME types are identical.
+     */
+    public static boolean isSameMimeType(String pType1, String pType2) {
+        return pType1 != null  &&  pType2 != null  &&  pType1.equalsIgnoreCase(pType2);
+    }
+
+    /**
+     * Returns true, if the given MIME type is that of a message. 
+     */
+    public static boolean isMessage(String pMimeType) {
+        return pMimeType != null  &&  pMimeType.equalsIgnoreCase("message/rfc822");
+    }
+
+    /**
+     * Return true, if the given MIME type indicates a multipart entity.
+     */
+    public static boolean isMultipart(String pMimeType) {
+        return pMimeType != null  &&  pMimeType.toLowerCase().startsWith("multipart/");
+    }
+
+    /**
+     * Returns, whether the given transfer-encoding is "base64".
+     */
+    public static boolean isBase64Encoding(String pTransferEncoding) {
+        return ENC_BASE64.equalsIgnoreCase(pTransferEncoding);
+    }
+
+    /**
+     * Returns, whether the given transfer-encoding is "quoted-printable".
+     */
+    public static boolean isQuotedPrintableEncoded(String pTransferEncoding) {
+        return ENC_QUOTED_PRINTABLE.equalsIgnoreCase(pTransferEncoding);
+    }
+
+    /**
+     * <p>Parses a complex field value into a map of key/value pairs. You may
+     * use this, for example, to parse a definition like
+     * <pre>
+     *   text/plain; charset=UTF-8; boundary=foobar
+     * </pre>
+     * The above example would return a map with the keys "", "charset",
+     * and "boundary", and the values "text/plain", "UTF-8", and "foobar".
+     * </p><p>
+     * Header value will be unfolded and excess white space trimmed.
+     * </p>
+     * @param pValue The field value to parse.
+     * @return The result map; use the key "" to retrieve the first value.
+     */
+    @SuppressWarnings("fallthrough")
+    public static Map<String, String> getHeaderParams(String pValue) {
+        pValue = pValue.trim();
+
+        pValue = unfold(pValue);
+        
+        Map<String, String> result = new HashMap<String, String>();
+
+        // split main value and parameters
+        String main;
+        String rest;
+        if (pValue.indexOf(";") == -1) {
+            main = pValue;
+            rest = null;
+        } else {
+            main = pValue.substring(0, pValue.indexOf(";"));
+            rest = pValue.substring(main.length() + 1);
+        }
+
+        result.put("", main);
+        if (rest != null) {
+            char[] chars = rest.toCharArray();
+            StringBuilder paramName = new StringBuilder(64);
+            StringBuilder paramValue = new StringBuilder(64);
+
+            final byte READY_FOR_NAME = 0;
+            final byte IN_NAME = 1;
+            final byte READY_FOR_VALUE = 2;
+            final byte IN_VALUE = 3;
+            final byte IN_QUOTED_VALUE = 4;
+            final byte VALUE_DONE = 5;
+            final byte ERROR = 99;
+
+            byte state = READY_FOR_NAME;
+            boolean escaped = false;
+            for (char c : chars) {
+                switch (state) {
+                    case ERROR:
+                        if (c == ';')
+                            state = READY_FOR_NAME;
+                        break;
+
+                    case READY_FOR_NAME:
+                        if (c == '=') {
+                            log.error("Expected header param name, got '='");
+                            state = ERROR;
+                            break;
+                        }
+
+                        paramName.setLength(0);
+                        paramValue.setLength(0);
+
+                        state = IN_NAME;
+                        // fall-through
+
+                    case IN_NAME:
+                        if (c == '=') {
+                            if (paramName.length() == 0)
+                                state = ERROR;
+                            else
+                                state = READY_FOR_VALUE;
+                            break;
+                        }
+
+                        // not '='... just add to name
+                        paramName.append(c);
+                        break;
+
+                    case READY_FOR_VALUE:
+                        boolean fallThrough = false;
+                        switch (c) {
+                            case ' ':
+                            case '\t':
+                                break;  // ignore spaces, especially before '"'
+
+                            case '"':
+                                state = IN_QUOTED_VALUE;
+                                break;
+
+                            default:
+                                state = IN_VALUE;
+                                fallThrough = true;
+                                break;
+                        }
+                        if (!fallThrough)
+                            break;
+
+                        // fall-through
+
+                    case IN_VALUE:
+                        fallThrough = false;
+                        switch (c) {
+                            case ';':
+                            case ' ':
+                            case '\t':
+                                result.put(
+                                   paramName.toString().trim().toLowerCase(),
+                                   paramValue.toString().trim());
+                                state = VALUE_DONE;
+                                fallThrough = true;
+                                break;
+                            default:
+                                paramValue.append(c);
+                                break;
+                        }
+                        if (!fallThrough)
+                            break;
+
+                    case VALUE_DONE:
+                        switch (c) {
+                            case ';':
+                                state = READY_FOR_NAME;
+                                break;
+
+                            case ' ':
+                            case '\t':
+                                break;
+
+                            default:
+                                state = ERROR;
+                                break;
+                        }
+                        break;
+                        
+                    case IN_QUOTED_VALUE:
+                        switch (c) {
+                            case '"':
+                                if (!escaped) {
+                                    // don't trim quoted strings; the spaces could be intentional.
+                                    result.put(
+                                            paramName.toString().trim().toLowerCase(),
+                                            paramValue.toString());
+                                    state = VALUE_DONE;
+                                } else {
+                                    escaped = false;
+                                    paramValue.append(c);                                    
+                                }
+                                break;
+                                
+                            case '\\':
+                                if (escaped) {
+                                    paramValue.append('\\');
+                                }
+                                escaped = !escaped;
+                                break;
+
+                            default:
+                                if (escaped) {
+                                    paramValue.append('\\');
+                                }
+                                escaped = false;
+                                paramValue.append(c);
+                                break;
+                        }
+                        break;
+
+                }
+            }
+
+            // done looping.  check if anything is left over.
+            if (state == IN_VALUE) {
+                result.put(
+                        paramName.toString().trim().toLowerCase(),
+                        paramValue.toString().trim());
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Creates a new unique message boundary string that can be used as boundary
+     * parameter for the Content-Type header field of a message.
+     * 
+     * @return a new unique message boundary string.
+     */
+    public static String createUniqueBoundary() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("-=Part.");
+        sb.append(Integer.toHexString(nextCounterValue()));
+        sb.append('.');
+        sb.append(Long.toHexString(random.nextLong()));
+        sb.append('.');
+        sb.append(Long.toHexString(System.currentTimeMillis()));
+        sb.append('.');
+        sb.append(Long.toHexString(random.nextLong()));
+        sb.append("=-");
+        return sb.toString();
+    }
+
+    /**
+     * Creates a new unique message identifier that can be used in message
+     * header field such as Message-ID or In-Reply-To. If the given host name is
+     * not <code>null</code> it will be used as suffix for the message ID
+     * (following an at sign).
+     * 
+     * The resulting string is enclosed in angle brackets (&lt; and &gt;);
+     * 
+     * @param hostName host name to be included in the message ID or
+     *            <code>null</code> if no host name should be included.
+     * @return a new unique message identifier.
+     */
+    public static String createUniqueMessageId(String hostName) {
+        StringBuilder sb = new StringBuilder("<Mime4j.");
+        sb.append(Integer.toHexString(nextCounterValue()));
+        sb.append('.');
+        sb.append(Long.toHexString(random.nextLong()));
+        sb.append('.');
+        sb.append(Long.toHexString(System.currentTimeMillis()));
+        if (hostName != null) {
+            sb.append('@');
+            sb.append(hostName);
+        }
+        sb.append('>');
+        return sb.toString();
+    }
+
+    /**
+     * Formats the specified date into a RFC 822 date-time string.
+     * 
+     * @param date
+     *            date to be formatted into a string.
+     * @param zone
+     *            the time zone to use or <code>null</code> to use the default
+     *            time zone.
+     * @return the formatted time string.
+     */
+    public static String formatDate(Date date, TimeZone zone) {
+        DateFormat df = RFC822_DATE_FORMAT.get();
+
+        if (zone == null) {
+            df.setTimeZone(TimeZone.getDefault());
+        } else {
+            df.setTimeZone(zone);
+        }
+
+        return df.format(date);
+    }
+
+    /**
+     * Splits the specified string into a multiple-line representation with
+     * lines no longer than 76 characters (because the line might contain
+     * encoded words; see <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC
+     * 2047</a> section 2). If the string contains non-whitespace sequences
+     * longer than 76 characters a line break is inserted at the whitespace
+     * character following the sequence resulting in a line longer than 76
+     * characters.
+     * 
+     * @param s
+     *            string to split.
+     * @param usedCharacters
+     *            number of characters already used up. Usually the number of
+     *            characters for header field name plus colon and one space.
+     * @return a multiple-line representation of the given string.
+     */
+    public static String fold(String s, int usedCharacters) {
+        final int maxCharacters = 76;
+
+        final int length = s.length();
+        if (usedCharacters + length <= maxCharacters)
+            return s;
+
+        StringBuilder sb = new StringBuilder();
+
+        int lastLineBreak = -usedCharacters;
+        int wspIdx = indexOfWsp(s, 0);
+        while (true) {
+            if (wspIdx == length) {
+                sb.append(s.substring(Math.max(0, lastLineBreak)));
+                return sb.toString();
+            }
+
+            int nextWspIdx = indexOfWsp(s, wspIdx + 1);
+
+            if (nextWspIdx - lastLineBreak > maxCharacters) {
+                sb.append(s.substring(Math.max(0, lastLineBreak), wspIdx));
+                sb.append("\r\n");
+                lastLineBreak = wspIdx;
+            }
+
+            wspIdx = nextWspIdx;
+        }
+    }
+
+    /**
+     * Unfold a multiple-line representation into a single line.
+     * 
+     * @param s
+     *            string to unfold.
+     * @return unfolded string.
+     */
+    public static String unfold(String s) {
+        final int length = s.length();
+        for (int idx = 0; idx < length; idx++) {
+            char c = s.charAt(idx);
+            if (c == '\r' || c == '\n') {
+                return unfold0(s, idx);
+            }
+        }
+
+        return s;
+    }
+
+    private static String unfold0(String s, int crlfIdx) {
+        final int length = s.length();
+        StringBuilder sb = new StringBuilder(length);
+
+        if (crlfIdx > 0) {
+            sb.append(s.substring(0, crlfIdx));
+        }
+
+        for (int idx = crlfIdx + 1; idx < length; idx++) {
+            char c = s.charAt(idx);
+            if (c != '\r' && c != '\n') {
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static int indexOfWsp(String s, int fromIndex) {
+        final int len = s.length();
+        for (int index = fromIndex; index < len; index++) {
+            char c = s.charAt(index);
+            if (c == ' ' || c == '\t')
+                return index;
+        }
+        return len;
+    }
+
+    private static synchronized int nextCounterValue() {
+        return counter++;
+    }
+
+    private static final ThreadLocal<DateFormat> RFC822_DATE_FORMAT = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new Rfc822DateFormat();
+        }
+    };
+
+    private static final class Rfc822DateFormat extends SimpleDateFormat {
+        private static final long serialVersionUID = 1L;
+
+        public Rfc822DateFormat() {
+            super("EEE, d MMM yyyy HH:mm:ss ", Locale.US);
+        }
+
+        @Override
+        public StringBuffer format(Date date, StringBuffer toAppendTo,
+                FieldPosition pos) {
+            StringBuffer sb = super.format(date, toAppendTo, pos);
+
+            int zoneMillis = calendar.get(GregorianCalendar.ZONE_OFFSET);
+            int dstMillis = calendar.get(GregorianCalendar.DST_OFFSET);
+            int minutes = (zoneMillis + dstMillis) / 1000 / 60;
+
+            if (minutes < 0) {
+                sb.append('-');
+                minutes = -minutes;
+            } else {
+                sb.append('+');
+            }
+
+            sb.append(String.format("%02d%02d", minutes / 60, minutes % 60));
+
+            return sb;
+        }
+    }
+}

Propchange: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/StringArrayMap.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/StringArrayMap.java?rev=778457&r1=778456&r2=778457&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/StringArrayMap.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/StringArrayMap.java Mon May 25 17:20:48 2009
@@ -1,253 +1,253 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.mime4j.util;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import org.apache.james.mime4j.message.Header;
-import org.apache.james.mime4j.parser.ContentHandler;
-import org.apache.james.mime4j.parser.Field;
-
-/**
- * An object, which may be used to implement header, or parameter
- * maps. The maps keys are the header or parameter names. The
- * maps values are strings (single value), lists, or arrays.
- * <p>
- * Note that this class is not directly used anywhere in Mime4j.
- * Instead a user might choose to use it instead of {@link Header}
- * and {@link Field} in a custom {@link ContentHandler} implementation.
- * See also MIME4j-24.
- */
-public class StringArrayMap implements Serializable {
-    private static final long serialVersionUID = -5833051164281786907L;
-    private final Map<String, Object> map = new HashMap<String, Object>();
-
-    /**
-     * <p>Converts the given object into a string. The object may be either of:
-     * <ul>
-     *   <li>a string, which is returned without conversion</li>
-     *   <li>a list of strings, in which case the first element is returned</li>
-     *   <li>an array of strings, in which case the first element is returned</li>
-     * </ul>
-     */
-    public static String asString(Object pValue) {
-        if (pValue == null) {
-            return null;
-        }
-        if (pValue instanceof String) {
-            return (String) pValue;
-        }
-        if (pValue instanceof String[]) {
-            return ((String[]) pValue)[0];
-        }
-        if (pValue instanceof List) {
-            return (String) ((List<?>) pValue).get(0);
-        }
-        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
-    }
-
-    /**
-     * <p>Converts the given object into a string array. The object may be either of:
-     * <ul>
-     *   <li>a string, which is returned as an array with one element</li>
-     *   <li>a list of strings, which is being converted into a string array</li>
-     *   <li>an array of strings, which is returned without conversion</li>
-     * </ul>
-     */
-    public static String[] asStringArray(Object pValue) {
-        if (pValue == null) {
-            return null;
-        }
-        if (pValue instanceof String) {
-            return new String[]{(String) pValue};
-        }
-        if (pValue instanceof String[]) {
-            return (String[]) pValue;
-        }
-        if (pValue instanceof List) {
-            final List<?> l = (List<?>) pValue;
-            return l.toArray(new String[l.size()]);
-        }
-        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
-    }
-
-    /**
-     * <p>Converts the given object into a string enumeration. The object may be either of:
-     * <ul>
-     *   <li>a string, which is returned as an enumeration with one element</li>
-     *   <li>a list of strings, which is being converted into a string enumeration</li>
-     *   <li>an array of strings, which is being converted into a string enumeration</li>
-     * </ul>
-     */
-    public static Enumeration<String> asStringEnum(final Object pValue) {
-        if (pValue == null) {
-            return null;
-        }
-        if (pValue instanceof String) {
-            return new Enumeration<String>(){
-                private Object value = pValue;
-                public boolean hasMoreElements() {
-                    return value != null;
-                }
-                public String nextElement() {
-                    if (value == null) {
-                        throw new NoSuchElementException();
-                    }
-                    final String s = (String) value;
-                    value = null;
-                    return s;
-                }
-            };
-        }
-        if (pValue instanceof String[]) {
-            final String[] values = (String[]) pValue;
-            return new Enumeration<String>() {
-                private int offset;
-                public boolean hasMoreElements() {
-                    return offset < values.length;
-                }
-                public String nextElement() {
-                    if (offset >= values.length) {
-                        throw new NoSuchElementException();
-                    }
-                    return values[offset++];
-                }
-            };
-        }
-        if (pValue instanceof List) {
-            @SuppressWarnings("unchecked")
-            final List<String> stringList = (List<String>) pValue; 
-            return Collections.enumeration(stringList);
-        }
-        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
-    }
-
-    /**
-     * Converts the given map into a string array map: The map values
-     * are string arrays.
-     */
-    public static Map<String, String[]> asMap(final Map<String, Object> pMap) {
-        Map<String, String[]> result = new HashMap<String, String[]>(pMap.size());
-        for (Map.Entry<String, Object> entry : pMap.entrySet()) {
-            final String[] value = asStringArray(entry.getValue());
-            result.put(entry.getKey(), value);
-        }
-        return Collections.unmodifiableMap(result);
-    }
-
-    /**
-     * Adds a value to the given map.
-     */
-    protected void addMapValue(Map<String, Object> pMap, String pName, String pValue) {
-        Object o = pMap.get(pName);
-        if (o == null) {
-            o = pValue;
-        } else if (o instanceof String) {
-            final List<Object> list = new ArrayList<Object>();
-            list.add(o);
-            list.add(pValue);
-            o = list;
-        } else if (o instanceof List) {
-            @SuppressWarnings("unchecked")
-            final List<String> stringList = (List<String>) o; 
-            stringList.add(pValue);
-        } else if (o instanceof String[]) {
-            final List<String> list = new ArrayList<String>();
-            final String[] arr = (String[]) o;
-            for (String str : arr) {
-                list.add(str);
-            }
-            list.add(pValue);
-            o = list;
-        } else {
-            throw new IllegalStateException("Invalid object type: " + o.getClass().getName());
-        }
-        pMap.put(pName, o);
-    }
-
-    /**
-     * Lower cases the given name.
-     */
-    protected String convertName(String pName) {
-        return pName.toLowerCase();
-    }
-
-    /**
-     * Returns the requested value.
-     */
-    public String getValue(String pName) {
-        return asString(map.get(convertName(pName)));
-    }
-
-    /**
-     * Returns the requested values as a string array.
-     */
-    public String[] getValues(String pName) {
-        return asStringArray(map.get(convertName(pName)));
-    }
-
-    /**
-     * Returns the requested values as an enumeration.
-     */
-    public Enumeration<String> getValueEnum(String pName) {
-        return asStringEnum(map.get(convertName(pName)));
-    }
-
-    /**
-     * Returns the set of registered names as an enumeration.
-     * @see #getNameArray()
-     */
-    public Enumeration<String> getNames() {
-        return Collections.enumeration(map.keySet());
-    }
-
-    /**
-     * Returns an unmodifiable map of name/value pairs. The map keys
-     * are the lower cased parameter/header names. The map values are
-     * string arrays.
-     */
-    public Map<String, String[]> getMap() {
-        return asMap(map);
-    }
-
-    /**
-     * Adds a new name/value pair.
-     */
-    public void addValue(String pName, String pValue) {
-        addMapValue(map, convertName(pName), pValue);
-    }
-
-    /**
-     * Returns the set of registered names.
-     * @see #getNames()
-     */
-    public String[] getNameArray() {
-        final Collection<String> c = map.keySet();
-        return c.toArray(new String[c.size()]);
-    }
-}
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mime4j.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.james.mime4j.message.Header;
+import org.apache.james.mime4j.parser.ContentHandler;
+import org.apache.james.mime4j.parser.Field;
+
+/**
+ * An object, which may be used to implement header, or parameter
+ * maps. The maps keys are the header or parameter names. The
+ * maps values are strings (single value), lists, or arrays.
+ * <p>
+ * Note that this class is not directly used anywhere in Mime4j.
+ * Instead a user might choose to use it instead of {@link Header}
+ * and {@link Field} in a custom {@link ContentHandler} implementation.
+ * See also MIME4j-24.
+ */
+public class StringArrayMap implements Serializable {
+    private static final long serialVersionUID = -5833051164281786907L;
+    private final Map<String, Object> map = new HashMap<String, Object>();
+
+    /**
+     * <p>Converts the given object into a string. The object may be either of:
+     * <ul>
+     *   <li>a string, which is returned without conversion</li>
+     *   <li>a list of strings, in which case the first element is returned</li>
+     *   <li>an array of strings, in which case the first element is returned</li>
+     * </ul>
+     */
+    public static String asString(Object pValue) {
+        if (pValue == null) {
+            return null;
+        }
+        if (pValue instanceof String) {
+            return (String) pValue;
+        }
+        if (pValue instanceof String[]) {
+            return ((String[]) pValue)[0];
+        }
+        if (pValue instanceof List) {
+            return (String) ((List<?>) pValue).get(0);
+        }
+        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
+    }
+
+    /**
+     * <p>Converts the given object into a string array. The object may be either of:
+     * <ul>
+     *   <li>a string, which is returned as an array with one element</li>
+     *   <li>a list of strings, which is being converted into a string array</li>
+     *   <li>an array of strings, which is returned without conversion</li>
+     * </ul>
+     */
+    public static String[] asStringArray(Object pValue) {
+        if (pValue == null) {
+            return null;
+        }
+        if (pValue instanceof String) {
+            return new String[]{(String) pValue};
+        }
+        if (pValue instanceof String[]) {
+            return (String[]) pValue;
+        }
+        if (pValue instanceof List) {
+            final List<?> l = (List<?>) pValue;
+            return l.toArray(new String[l.size()]);
+        }
+        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
+    }
+
+    /**
+     * <p>Converts the given object into a string enumeration. The object may be either of:
+     * <ul>
+     *   <li>a string, which is returned as an enumeration with one element</li>
+     *   <li>a list of strings, which is being converted into a string enumeration</li>
+     *   <li>an array of strings, which is being converted into a string enumeration</li>
+     * </ul>
+     */
+    public static Enumeration<String> asStringEnum(final Object pValue) {
+        if (pValue == null) {
+            return null;
+        }
+        if (pValue instanceof String) {
+            return new Enumeration<String>(){
+                private Object value = pValue;
+                public boolean hasMoreElements() {
+                    return value != null;
+                }
+                public String nextElement() {
+                    if (value == null) {
+                        throw new NoSuchElementException();
+                    }
+                    final String s = (String) value;
+                    value = null;
+                    return s;
+                }
+            };
+        }
+        if (pValue instanceof String[]) {
+            final String[] values = (String[]) pValue;
+            return new Enumeration<String>() {
+                private int offset;
+                public boolean hasMoreElements() {
+                    return offset < values.length;
+                }
+                public String nextElement() {
+                    if (offset >= values.length) {
+                        throw new NoSuchElementException();
+                    }
+                    return values[offset++];
+                }
+            };
+        }
+        if (pValue instanceof List) {
+            @SuppressWarnings("unchecked")
+            final List<String> stringList = (List<String>) pValue; 
+            return Collections.enumeration(stringList);
+        }
+        throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName());
+    }
+
+    /**
+     * Converts the given map into a string array map: The map values
+     * are string arrays.
+     */
+    public static Map<String, String[]> asMap(final Map<String, Object> pMap) {
+        Map<String, String[]> result = new HashMap<String, String[]>(pMap.size());
+        for (Map.Entry<String, Object> entry : pMap.entrySet()) {
+            final String[] value = asStringArray(entry.getValue());
+            result.put(entry.getKey(), value);
+        }
+        return Collections.unmodifiableMap(result);
+    }
+
+    /**
+     * Adds a value to the given map.
+     */
+    protected void addMapValue(Map<String, Object> pMap, String pName, String pValue) {
+        Object o = pMap.get(pName);
+        if (o == null) {
+            o = pValue;
+        } else if (o instanceof String) {
+            final List<Object> list = new ArrayList<Object>();
+            list.add(o);
+            list.add(pValue);
+            o = list;
+        } else if (o instanceof List) {
+            @SuppressWarnings("unchecked")
+            final List<String> stringList = (List<String>) o; 
+            stringList.add(pValue);
+        } else if (o instanceof String[]) {
+            final List<String> list = new ArrayList<String>();
+            final String[] arr = (String[]) o;
+            for (String str : arr) {
+                list.add(str);
+            }
+            list.add(pValue);
+            o = list;
+        } else {
+            throw new IllegalStateException("Invalid object type: " + o.getClass().getName());
+        }
+        pMap.put(pName, o);
+    }
+
+    /**
+     * Lower cases the given name.
+     */
+    protected String convertName(String pName) {
+        return pName.toLowerCase();
+    }
+
+    /**
+     * Returns the requested value.
+     */
+    public String getValue(String pName) {
+        return asString(map.get(convertName(pName)));
+    }
+
+    /**
+     * Returns the requested values as a string array.
+     */
+    public String[] getValues(String pName) {
+        return asStringArray(map.get(convertName(pName)));
+    }
+
+    /**
+     * Returns the requested values as an enumeration.
+     */
+    public Enumeration<String> getValueEnum(String pName) {
+        return asStringEnum(map.get(convertName(pName)));
+    }
+
+    /**
+     * Returns the set of registered names as an enumeration.
+     * @see #getNameArray()
+     */
+    public Enumeration<String> getNames() {
+        return Collections.enumeration(map.keySet());
+    }
+
+    /**
+     * Returns an unmodifiable map of name/value pairs. The map keys
+     * are the lower cased parameter/header names. The map values are
+     * string arrays.
+     */
+    public Map<String, String[]> getMap() {
+        return asMap(map);
+    }
+
+    /**
+     * Adds a new name/value pair.
+     */
+    public void addValue(String pName, String pValue) {
+        addMapValue(map, convertName(pName), pValue);
+    }
+
+    /**
+     * Returns the set of registered names.
+     * @see #getNames()
+     */
+    public String[] getNameArray() {
+        final Collection<String> c = map.keySet();
+        return c.toArray(new String[c.size()]);
+    }
+}

Propchange: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/StringArrayMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/EncodeUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/ExampleMail.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/TestUtil.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/TestUtil.java?rev=778457&r1=778456&r2=778457&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/TestUtil.java (original)
+++ james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/TestUtil.java Mon May 25 17:20:48 2009
@@ -1,73 +1,73 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.mime4j;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.commons.io.IOUtils;
-
-public class TestUtil {
-    public static final String[] TEST_MESSAGES = new String[] {
-            "2002_06_12_doublebound",
-            "ak-0696",
-            "bluedot-postcard",
-            "bluedot-simple",
-            "double-bound-with-embedded",
-            "double-bound",
-            "dup-names",
-            "frag",
-            "german",
-            "hdr-fakeout",
-            "multi-2evil",
-            "multi-2gifs",
-            "multi-clen",
-            "multi-digest",
-            "multi-frag",
-            "multi-igor",
-            "multi-igor2",
-            "multi-nested",
-            "multi-nested2",
-            "multi-nested3",
-            "multi-simple",
-            "multi-weirdspace",
-            "re-fwd",
-            "russian",
-            "simple",
-            "uu-junk-target",
-            "uu-junk",
-            "uu-zeegee"
-    };
-    
-    public static String readResource(String resource, String charset) 
-            throws IOException {
-        
-        return IOUtils.toString(readResourceAsStream(resource), charset);
-    }
-
-    public static InputStream readResourceAsStream(String resource) 
-            throws IOException {
-
-        return new BufferedInputStream(
-                TestUtil.class.getResource(resource).openStream());
-    }
-    
-}
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mime4j;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+
+public class TestUtil {
+    public static final String[] TEST_MESSAGES = new String[] {
+            "2002_06_12_doublebound",
+            "ak-0696",
+            "bluedot-postcard",
+            "bluedot-simple",
+            "double-bound-with-embedded",
+            "double-bound",
+            "dup-names",
+            "frag",
+            "german",
+            "hdr-fakeout",
+            "multi-2evil",
+            "multi-2gifs",
+            "multi-clen",
+            "multi-digest",
+            "multi-frag",
+            "multi-igor",
+            "multi-igor2",
+            "multi-nested",
+            "multi-nested2",
+            "multi-nested3",
+            "multi-simple",
+            "multi-weirdspace",
+            "re-fwd",
+            "russian",
+            "simple",
+            "uu-junk-target",
+            "uu-junk",
+            "uu-zeegee"
+    };
+    
+    public static String readResource(String resource, String charset) 
+            throws IOException {
+        
+        return IOUtils.toString(readResourceAsStream(resource), charset);
+    }
+
+    public static InputStream readResourceAsStream(String resource) 
+            throws IOException {
+
+        return new BufferedInputStream(
+                TestUtil.class.getResource(resource).openStream());
+    }
+    
+}

Propchange: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/TestUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message