am ae1ea825: (-s ours) Import translations. DO NOT MERGE

* commit 'ae1ea825848142ae00b8347bafadcb1a294f48e5':
  Import translations. DO NOT MERGE
diff --git a/Android.mk b/Android.mk
index 3e60c64..0f39dad 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,6 +23,10 @@
 datetimepicker_dir := ../../../frameworks/opt/datetimepicker/res
 res_dirs := res $(chips_dir) $(photo_dir) $(gridlayout_dir) $(datetimepicker_dir)
 
+##################################################
+include $(CLEAR_VARS)
+
+include $(BUILD_MULTI_PREBUILT)
 
 ##################################################
 # Build APK
@@ -39,6 +43,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-gridlayout
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
 LOCAL_STATIC_JAVA_LIBRARIES += android-opt-datetimepicker
+# STOPSHIP: Open source the wearable support jar
+LOCAL_STATIC_JAVA_LIBRARIES += prebuilt-wearable-support-1133727
 
 LOCAL_SDK_VERSION := current
 
@@ -54,7 +60,6 @@
 
 include $(BUILD_PACKAGE)
 
-
 ##################################################
 # Build all sub-directories
 
diff --git a/assets/script.js b/assets/script.js
index e957d2d..b096c4b 100644
--- a/assets/script.js
+++ b/assets/script.js
@@ -576,6 +576,10 @@
 }
 
 function onContentReady(event) {
+    // hack for b/1333356
+    if (RUNNING_KITKAT_OR_LATER) {
+        restoreScrollPosition();
+    }
     window.mail.onContentReady();
 }
 
@@ -658,6 +662,15 @@
 
 function setMessageHeaderSpacerHeight(messageDomId, spacerHeight) {
     var spacer = document.querySelector("#" + messageDomId + " > .mail-message-header");
+    setSpacerHeight(spacer, spacerHeight);
+}
+
+function setMessageFooterSpacerHeight(messageDomId, spacerHeight) {
+    var spacer = document.querySelector("#" + messageDomId + " > .mail-message-footer");
+    setSpacerHeight(spacer, spacerHeight);
+}
+
+function setSpacerHeight(spacer, spacerHeight) {
     if (!spacer) {
         console.log("can't set spacer for message with id: " + messageDomId);
         return;
@@ -779,6 +792,9 @@
 hideAllUnsafeImages();
 normalizeAllMessageWidths();
 //setWideViewport();
-restoreScrollPosition();
+// hack for b/1333356
+if (!RUNNING_KITKAT_OR_LATER) {
+    restoreScrollPosition();
+}
 measurePositions();
 
diff --git a/proguard.flags b/proguard.flags
index 653943c..7267eab 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -6,41 +6,11 @@
 -dontwarn android.support.v13.**
 -dontwarn com.android.ex.photo.**
 
-# keep names that are used by reflection, from javascript or from unit tests
+# keep names that are used by reflection, or from javascript
 
--keepclasseswithmembers class com.android.mail.compose.ComposeActivity {
-  *** registerTestSendOrSaveCallback(...);
-  *** getSubject(...);
-  *** getBodyHtml(...);
-}
-
--keepclasseswithmembers class com.android.mail.providers.protos.mock.MockUiProvider {
-  *** getAccountsUri(...);
-}
-
--keepclasseswithmembers class com.android.mail.providers.UIProvider {
-  *** getAccountsUri(...);
-}
-
--keepclasseswithmembers class com.android.mail.utils.LogUtils {
-  public <methods>;
-}
-
-# Needed for Parcelable/SafeParcelable Creators to not get stripped
+# TODO(skennedy) Remove this when the base file includes it
 -keep class * implements android.os.Parcelable {
-    public static final ** CREATOR;
-}
-
--keepclasseswithmembers class com.google.common.collect.ImmutableMap {
-  *** of(...);
-}
-
--keepclasseswithmembers class com.google.common.collect.ImmutableList {
-  *** of(...);
-}
-
--keepclasseswithmembers class com.google.common.collect.Sets {
-  *** of(...);
+  public static final ** CREATOR;
 }
 
 -keepclasseswithmembers class com.android.mail.ui.LeaveBehindItem {
@@ -51,41 +21,11 @@
   public <methods>;
 }
 
--keepclasseswithmembers class com.android.mail.ui.TwoPaneLayout {
-  *** setFoldersLeft(...);
-  *** setListBitmapLeft(...);
-  *** setListBitmapAlpha(...);
-  *** setListLeft(...);
-  *** setListAlpha(...);
-  *** setConversationLeft(...);
-}
-
 -keepclasseswithmembers class com.android.mail.browse.ConversationItemView {
   *** setAnimatedHeightFraction(...);
 }
 
--keepclasseswithmembers class com.android.mail.ui.MailActivity {
-  *** doNothingClickHandler(...);
-}
-
 -keepclasseswithmembers class * extends com.android.mail.ui.ConversationSpecialItemView {
   *** setAnimatedHeight(...);
 }
 
-# the widget "Tap to set up" intent builder references this class by name...
--keep class com.android.mail.ui.MailboxSelectionActivity
-
-# not yet used in UnifiedEmail, but needed by UnifiedEmailTests
--keep class com.android.mail.ui.MailAsyncTaskLoader
-
--keepclasseswithmembers class com.android.emailcommon.mail.Address {
-  public <methods>;
-}
-
--keepclasseswithmembers class com.android.mail.providers.ConversationInfo {
-  *** addParticipant(...);
-}
-
--keepclasseswithmembers class com.android.mail.providers.ParticipantInfo {
-  public <methods>;
-}
diff --git a/res/layout/above_attachment_bar_list_layout.xml b/res/layout/above_attachment_bar_list_layout.xml
new file mode 100644
index 0000000..a0bd285
--- /dev/null
+++ b/res/layout/above_attachment_bar_list_layout.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed 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.
+-->
+<merge />
\ No newline at end of file
diff --git a/res/layout/conversation_list.xml b/res/layout/conversation_list.xml
index 4d87975..2d2bfa8 100644
--- a/res/layout/conversation_list.xml
+++ b/res/layout/conversation_list.xml
@@ -29,9 +29,9 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <FrameLayout
+            android:id="@+id/conversation_list_parent_frame"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:animateLayoutChanges="true"
             android:clickable="true">
             <!-- Note: intentionally not called "empty" because we call
              setEmptyView programmatically-->
diff --git a/res/layout/conversation_list_empty_view.xml b/res/layout/conversation_list_empty_view.xml
index 93e905a..c5dc31c 100644
--- a/res/layout/conversation_list_empty_view.xml
+++ b/res/layout/conversation_list_empty_view.xml
@@ -22,7 +22,7 @@
         android:layout_height="match_parent"
         android:gravity="center"
         android:orientation="vertical"
-        android:visibility="gone" >
+        android:visibility="invisible" >
 
     <ImageView
             android:id="@+id/empty_icon"
diff --git a/res/layout/conversation_message_footer.xml b/res/layout/conversation_message_footer.xml
index a1eefb8..55f3310 100644
--- a/res/layout/conversation_message_footer.xml
+++ b/res/layout/conversation_message_footer.xml
@@ -53,6 +53,10 @@
         android:visibility="gone"
         style="@style/AttachmentMarginStyle" />
 
+    <include
+        layout="@layout/above_attachment_bar_list_layout"
+        android:id="@+id/above_attachment_bar_list_layout"/>
+
     <LinearLayout
         android:id="@+id/attachment_bar_list"
         android:layout_width="match_parent"
diff --git a/res/layout/conversation_message_upper_header.xml b/res/layout/conversation_message_upper_header.xml
index 34747db..880560c 100644
--- a/res/layout/conversation_message_upper_header.xml
+++ b/res/layout/conversation_message_upper_header.xml
@@ -21,7 +21,7 @@
     android:background="@color/message_header_background_color"
     android:duplicateParentState="true" >
 
-    <QuickContactBadge
+    <com.android.mail.browse.MessageHeaderContactBadge
         android:id="@+id/photo"
         android:layout_width="@dimen/message_header_contact_photo_width"
         android:layout_height="@dimen/message_header_contact_photo_height"
diff --git a/res/layout/drawer_empty_view.xml b/res/layout/drawer_empty_view.xml
index b81d1a5..0e4f797 100644
--- a/res/layout/drawer_empty_view.xml
+++ b/res/layout/drawer_empty_view.xml
@@ -23,6 +23,8 @@
         android:id="@+id/empty_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:paddingTop="10dp"
+        android:paddingBottom="20dp"
         android:gravity="center_horizontal">
 
     <ProgressBar
diff --git a/res/layout/drawer_footer_item.xml b/res/layout/drawer_footer_item.xml
index d890bcf..40ee298 100644
--- a/res/layout/drawer_footer_item.xml
+++ b/res/layout/drawer_footer_item.xml
@@ -19,8 +19,7 @@
 <!-- Item in the drawer that launches the Help or Feedback activities. -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@color/footer_background_color">
+    android:layout_height="wrap_content" >
 
     <LinearLayout
         android:layout_height="wrap_content"
diff --git a/res/layout/conversation_list_loading_view.xml b/res/layout/loading_messages.xml
similarity index 94%
rename from res/layout/conversation_list_loading_view.xml
rename to res/layout/loading_messages.xml
index a33d05e..b466e51 100644
--- a/res/layout/conversation_list_loading_view.xml
+++ b/res/layout/loading_messages.xml
@@ -22,8 +22,7 @@
         android:layout_height="match_parent"
         android:gravity="center"
         android:orientation="vertical"
-        android:visibility="gone"
-        android:background="@color/footer_background_color">
+        android:background="@color/loading_messages_background_color">
 
     <ImageView
             android:id="@+id/loading_icon"
diff --git a/res/layout/swipe_leavebehind_body.xml b/res/layout/swipe_leavebehind_body.xml
index aff4aa3..cfe8858 100644
--- a/res/layout/swipe_leavebehind_body.xml
+++ b/res/layout/swipe_leavebehind_body.xml
@@ -29,9 +29,9 @@
         android:singleLine="true"
         android:textColor="@android:color/white"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:paddingLeft="16dip"
         android:clickable="true"
-        android:gravity="center_vertical"/>
+        android:gravity="center_vertical"
+        style="@style/UndoDescriptionStyle" />
 
     <View
         android:id="@+id/undo_separator"
@@ -45,11 +45,10 @@
         android:id="@+id/undo_icon"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:paddingLeft="12dip"
-        android:paddingRight="8dip"
         android:src="@drawable/ic_menu_revert_holo_dark"
         android:background="?android:attr/selectableItemBackground"
-        android:duplicateParentState="true" />
+        android:duplicateParentState="true"
+        style="@style/UndoIconStyle" />
 
     <TextView
         android:id="@+id/undo_text"
diff --git a/res/layout/wait_for_sync.xml b/res/layout/wait_for_sync.xml
deleted file mode 100644
index f40e8e1..0000000
--- a/res/layout/wait_for_sync.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2012 Google Inc.
-     Licensed to The Android Open Source Project.
-
-     Licensed 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.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@android:color/white"
-    android:padding="@dimen/wait_padding">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/wait_for_sync_title"
-        style="@style/WaitFragmentTitle"
-        android:id="@+id/wait_for_sync_title"
-        android:layout_marginLeft="8dip"
-        android:layout_marginBottom="4dip"/>
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/wait_for_sync_body"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:layout_below="@id/wait_for_sync_title"
-        android:layout_marginLeft="8dip" />
-
- </RelativeLayout>
diff --git a/res/menu/message_footer_overflow_menu.xml b/res/menu/message_footer_overflow_menu.xml
index be64442..707e8c0 100644
--- a/res/menu/message_footer_overflow_menu.xml
+++ b/res/menu/message_footer_overflow_menu.xml
@@ -19,10 +19,10 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/preview_attachment"
           android:title="@string/preview_attachment" />
+    <item android:id="@+id/attachment_extra_option1"
+          android:title="@string/attachment_extra_option1" />
     <item android:id="@+id/save_attachment"
           android:title="@string/save_attachment" />
     <item android:id="@+id/download_again"
           android:title="@string/download_again" />
-    <item android:id="@+id/attachment_extra_option1"
-          android:title="@string/attachment_extra_option1" />
 </menu>
diff --git a/res/menu/photo_view_menu.xml b/res/menu/photo_view_menu.xml
index 48d4b70..3969575 100644
--- a/res/menu/photo_view_menu.xml
+++ b/res/menu/photo_view_menu.xml
@@ -27,6 +27,10 @@
             android:showAsAction="never"
             android:title="@string/menu_photo_save_all"/>
         <item
+            android:id="@+id/attachment_extra_option1"
+            android:showAsAction="never"
+            android:title="@string/attachment_extra_option1" />
+        <item
             android:id="@+id/menu_share"
             android:showAsAction="never"
             android:title="@string/menu_photo_share"/>
@@ -42,10 +46,6 @@
             android:id="@+id/menu_download_again"
             android:showAsAction="never"
             android:title="@string/download_again"/>
-        <item
-            android:id="@+id/attachment_extra_option1"
-            android:showAsAction="never"
-            android:title="@string/attachment_extra_option1" />
     </group>
 
 </menu>
\ No newline at end of file
diff --git a/res/raw/template_conversation_lower.html b/res/raw/template_conversation_lower.html
index 1b78f1c..e235332 100644
--- a/res/raw/template_conversation_lower.html
+++ b/res/raw/template_conversation_lower.html
@@ -11,6 +11,7 @@
   var NORMALIZE_MESSAGE_WIDTHS = %s;
   var ENABLE_MUNGE_TABLES = %s;
   var ENABLE_MUNGE_IMAGES = %s;
+  var RUNNING_KITKAT_OR_LATER = %s;
 </script>
 <script type="text/javascript" src="file:///android_asset/script.js"></script>
 </html>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index cb2bd2c..27f1644 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Verskuif na <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultate"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Search word nie ondersteun op hierdie rekening nie."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Soek tans…"</string>
     <string name="add_label" msgid="6060201715848652940">"Voeg vouer by"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Wys nuwe boodskap van <xliff:g id="SENDER">%s</xliff:g> af."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Benoem vouer se kortpad"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Wag vir sinkronisasie"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Jou e-pos sal binnekort verskyn."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Rekening nie gesinkroniseer nie"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Hierdie rekening is nie opgestel om outomaties te sinkroniseer nie.\nRaak "<b>"Sinkroniseer nou"</b>" om pos een keer te sinkroniseer, of "<b>"Verander sinkroniseer-instellings"</b>" om hierdie rekening op te stel om pos outomaties te sinkroniseer."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sinkroniseer nou"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 04473f0..df9a308 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"ወደ <xliff:g id="FOLDERNAME">%1$s</xliff:g> ተወስዷል"</string>
     <string name="search_results_header" msgid="4669917471897026269">"ውጤቶች"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"ፍለጋ በዚህ መለያ አይደገፍም።"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"በመፈለግ ላይ…"</string>
     <string name="add_label" msgid="6060201715848652940">"አቃፊ አክል"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"ከ<xliff:g id="SENDER">%s</xliff:g> የመጣ አዲስ መልዕክት አሳይ።"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"ለአቃፊ አቋራጭ ስም ስጥ"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"አስምር በመጠበቅ ላይ"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"የእርስዎ ኢሜይል ከትንሽ ጊዜ በኋላ ብቅ ይላል።"</string>
     <string name="not_synced_title" msgid="794279749170198599">"መለያ አልተመሳሰለም"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"ይህ መለያ በራስ-ሰር እንዲመሳሰል አልተዋቀረም።\nመልዕክት አንዴ ለማመሳሰል "<b>"አሁን አመሳስል"</b>"ን ንካ፣ ወይም ደግሞ ይሄ መለያ መልዕክት በራስ-ሰር እንዲያመሳስል ለማዋቀር "<b>"የማመሳሰል ቅንብሮችን ቀይር"</b>"።"</string>
     <string name="manual_sync" msgid="6743983544685622800">"አሁን አመሳስል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 172dcac..27607b8 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"تم النقل إلى <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"النتائج"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"لا يمكن استخدام البحث على هذا الحساب."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"جارٍ البحث…"</string>
     <string name="add_label" msgid="6060201715848652940">"إضافة مجلد"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"عرض رسالة جديدة من <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"اختصار اسم المجلد"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"في انتظار المزامنة"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"سيظهر بريدك الإلكتروني بعد قليل."</string>
     <string name="not_synced_title" msgid="794279749170198599">"لم تتم مزامنة الحساب"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"لم يتم تعيين هذا الحساب على المزامنة تلقائيًا.\nيمكنك لمس "<b>"المزامنة الآن"</b>" لمزامنة البريد مرة واحدة أو "<b>"تغيير إعدادات المزامنة"</b>" لإعداد هذا الحساب على مزامنة البريد تلقائيًا."</string>
     <string name="manual_sync" msgid="6743983544685622800">"مزامنة الآن"</string>
diff --git a/res/values-be-sw600dp/strings.xml b/res/values-be-sw600dp/strings.xml
deleted file mode 100644
index 3380ef4..0000000
--- a/res/values-be-sw600dp/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-     Copyright (C) 2012 Google Inc.
-     Licensed to The Android Open Source Project.
-
-     Licensed 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="add_cc_label" msgid="6156834609108442458">"+ Копія/УК"</string>
-    <string name="add_bcc_label" msgid="7648769906379235693">"+ схаваная копія"</string>
-    <string name="menu_manage_folders" msgid="7574962882483466700">"Кiраванне тэчкамі"</string>
-    <string name="preferences_default_reply_all_summary" msgid="5318418012992612729">"Выкарыстоўваць у якасці адказу на паведамленні па змаўчанні"</string>
-</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
deleted file mode 100644
index 104b554..0000000
--- a/res/values-be/strings.xml
+++ /dev/null
@@ -1,483 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-     Copyright (C) 2011 Google Inc.
-     Licensed to The Android Open Source Project.
-
-     Licensed 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="to" msgid="4392614766835725698">"Каму"</string>
-    <string name="cc" msgid="7017720927911078193">"Копія"</string>
-    <string name="bcc" msgid="3098643138241111579">"Утоеная копія"</string>
-    <string name="subject_hint" msgid="7253964583324677128">"Тэма"</string>
-    <string name="body_hint" msgid="4279864165439577668">"Скласцi ліст"</string>
-    <string name="add_file_attachment" msgid="2203398371671979481">"Далучыць файл"</string>
-    <string name="add_photo_attachment" msgid="3738882100645552858">"Далучыць здымак"</string>
-    <string name="add_video_attachment" msgid="8916625983712820173">"Далучыць відэа"</string>
-    <string name="save_draft" msgid="5555744208032445632">"Захаваць чарнавік"</string>
-    <string name="discard" msgid="1714960202994370243">"Скасаваць"</string>
-    <string name="compose" msgid="1473559896062432878">"Напісаць"</string>
-  <string-array name="compose_modes">
-    <item msgid="8631190144210933782">"Адказаць"</item>
-    <item msgid="4085029907540221568">"Адказаць усім"</item>
-    <item msgid="857480048798815437">"Пераслаць"</item>
-  </string-array>
-    <string name="reply_attribution" msgid="717207316577386788">"<xliff:g id="DATE">%s</xliff:g> карыстальнік <xliff:g id="PERSON">%s</xliff:g> напісаў:"</string>
-    <string name="forward_attribution" msgid="1498936339458535158">"---------- Перанакіроўванае паведамленне ----------&lt;br&gt;Ад: <xliff:g id="FROM">%1$s</xliff:g>&lt;br&gt;Дата: <xliff:g id="DATE">%2$s</xliff:g>&lt;br&gt;Тэма: <xliff:g id="SUBJECT">%3$s</xliff:g>&lt;br&gt;Каму: <xliff:g id="TO">%4$s</xliff:g>&lt;br&gt;"</string>
-    <!-- no translation found for forward_attribution_no_headers (3239825787227110752) -->
-    <skip />
-    <string name="cc_attribution" msgid="374493748526504562">"Копія: <xliff:g id="CC">%1$s</xliff:g>&lt;br&gt;"</string>
-    <string name="select_attachment_type" msgid="4711524151968942082">"Выбраць тып далучэння"</string>
-    <string name="too_large_to_attach_single" msgid="2996733972478212136">"Немагчыма далучыць файл, большы за памер: <xliff:g id="MAXSIZE">%1$s</xliff:g>."</string>
-    <string name="too_large_to_attach_multiple" msgid="5452547998184881210">"Не далучаны адзін або некалькі файлаў. Лiмiт – <xliff:g id="MAXSIZE">%1$s</xliff:g>."</string>
-    <string name="too_large_to_attach_additional" msgid="3366607594339390249">"Файл не далучаны. Дасягнуты лiмiт у <xliff:g id="MAXSIZE">%1$s</xliff:g>."</string>
-    <string name="generic_attachment_problem" msgid="8775959340559458709">"Не атрымлiваецца далучыць файл."</string>
-    <string name="recipient_needed" msgid="999037027358925655">"Дадайце хаця б аднаго атрымальнiка."</string>
-    <string name="recipient_error_dialog_title" msgid="6051694305498669180">"Памылка атрымальнiка"</string>
-    <string name="confirm_send_title" msgid="5663602035452564062">"Адправiць паведамленне?"</string>
-    <string name="confirm_send_message_with_no_subject" msgid="8900379985826988281">"Тэма не запоўнена."</string>
-    <string name="confirm_send_message_with_no_body" msgid="4606328514693368953">"У целе паведамлення няма тэксту."</string>
-    <string name="confirm_send_message" msgid="3221546155385562874">"Адправіць гэта паведамленне?"</string>
-    <string name="message_discarded" msgid="5079975378884765445">"Паведамленне адменена."</string>
-    <string name="signature" msgid="7882698047321710252">\n\n"<xliff:g id="SIGNATURE">%s</xliff:g>"</string>
-    <string name="custom_from_account_label" msgid="5069652141632649722">"Адпр. ліст як:"</string>
-    <string name="send" msgid="8242003661397555986">"Адправіць"</string>
-    <string name="mark_read" msgid="8438349154394313195">"Пазначыць як прачытанае"</string>
-    <string name="mark_unread" msgid="6231373939351125713">"Пазначыць як непрачытанае"</string>
-    <string name="mute" msgid="1995954983688603977">"Выключыць гук"</string>
-    <string name="add_star" msgid="2866003744391266498">"Дадаць пазнаку"</string>
-    <string name="remove_star" msgid="4372327933122106128">"Выдаліць пазнаку"</string>
-    <string name="remove_folder" msgid="4906542720439865413">"Выдаліць з папкi <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
-    <string name="archive" msgid="847250094775910499">"Архіваваць"</string>
-    <string name="report_spam" msgid="6467567747975393907">"Паведаміць пра спам"</string>
-    <string name="mark_not_spam" msgid="694891665407228160">"Паведамiць, што гэта не спам"</string>
-    <string name="report_phishing" msgid="5714205737453138338">"Паведамiць пра фiшынг"</string>
-    <string name="delete" msgid="844871204175957681">"Выдаліць"</string>
-    <string name="discard_drafts" msgid="6862272443470085375">"Выдалiць чарнавiкi"</string>
-    <string name="refresh" msgid="490989798005710951">"Абнавіць"</string>
-    <string name="reply" msgid="2032052598496088561">"Адказаць"</string>
-    <string name="reply_all" msgid="7257248094625487557">"Адказаць усім"</string>
-    <string name="resume_draft" msgid="6450150180834092146">"Рэдагаваць"</string>
-    <string name="forward" msgid="6822914459902983767">"Пераслаць"</string>
-    <string name="menu_compose" msgid="6274193058224230645">"Напісаць"</string>
-    <string name="menu_change_folders" msgid="1542713666608888717">"Змена тэчкі"</string>
-    <string name="menu_move_to" msgid="9138296669516358542">"Перанесці ў"</string>
-    <string name="menu_move_to_inbox" msgid="3201472943201077389">"Перамясціць у папку \"Ўваходныя\""</string>
-    <string name="menu_manage_folders" msgid="6755623004628177492">"Налады тэчкi"</string>
-    <string name="menu_show_original" msgid="4227060689700765828">"Вярнуць аўтаматычнае змяненне памераў"</string>
-    <string name="menu_folder_options" msgid="8897520487430647932">"Налады тэчак"</string>
-    <string name="menu_settings" msgid="6007027046949837282">"Налады"</string>
-    <string name="menu_search" msgid="5297197523031943248">"Пошук"</string>
-    <string name="mark_important" msgid="6904096897104265870">"Пазначыць як важнае"</string>
-    <string name="mark_not_important" msgid="4550967327747977068">"Пазначыць як няважнае"</string>
-    <string name="add_cc_label" msgid="504861315425773180">"Дадаць копію/схаваную копiю"</string>
-    <string name="add_bcc_label" msgid="7432027645493843336">"Дадаць схав. копію"</string>
-    <string name="feedback" msgid="79365993169715049">"Адправiць водгук"</string>
-    <string name="help_and_info" msgid="7873089811705257882">"Даведка"</string>
-    <string name="quoted_text" msgid="7250749398672037355">"Уключыць цытаваны тэкст"</string>
-    <string name="quoted_text_label" msgid="541007446961960617">"Цытаваць тэкст"</string>
-    <string name="respond_inline" msgid="6797117334768201180">"Адказаць у лісце"</string>
-    <string name="bytes" msgid="874874279846496468">"<xliff:g id="COUNT">%s</xliff:g> Б"</string>
-    <string name="kilobytes" msgid="9191949758901349712">"<xliff:g id="COUNT">%s</xliff:g> КБ"</string>
-    <string name="megabytes" msgid="130431176022837380">"<xliff:g id="COUNT">%s</xliff:g> МБ"</string>
-    <string name="attachment_image" msgid="3832985223906528449">"Выява"</string>
-    <string name="attachment_video" msgid="7526134752988603154">"Відэа"</string>
-    <string name="attachment_audio" msgid="135527133955622936">"Гук"</string>
-    <string name="attachment_text" msgid="5544874374971562759">"Тэкст"</string>
-    <string name="attachment_application_msword" msgid="2326909450865637910">"Дакумент"</string>
-    <string name="attachment_application_vnd_ms_powerpoint" msgid="861348289167216897">"Прэзентацыя"</string>
-    <string name="attachment_application_vnd_ms_excel" msgid="5861224590731578901">"Электронныя табліцы"</string>
-    <string name="attachment_application_pdf" msgid="6922990425001131901">"PDF"</string>
-    <string name="attachment_unknown" msgid="6005445955982153931">"<xliff:g id="ATTACHMENTEXTENSION">%s</xliff:g> файла"</string>
-    <string name="preview_attachment" msgid="6934911143603455393">"Папярэдні прагляд"</string>
-    <string name="save_attachment" msgid="5360128849867863132">"Захаваць"</string>
-    <string name="cancel_attachment" msgid="4651201258272223650">"Адмена"</string>
-    <string name="download_again" msgid="7418568915429156518">"Спампаваць яшчэ раз"</string>
-    <string name="more_info_attachment" msgid="717244337981816703">"Інфармацыя"</string>
-    <string name="no_application_found" msgid="1945184804521392093">"Няма прыкладанняў, каб прагледзець гэта далучэнне."</string>
-    <string name="fetching_attachment" msgid="2599763030649526179">"Атрыманне далучаных файлаў"</string>
-    <string name="please_wait" msgid="8797634225088247121">"Чакайце..."</string>
-    <string name="saved" msgid="6660844285730790469">"Захаваныя, <xliff:g id="SIZE">%s</xliff:g>"</string>
-    <string name="download_failed" msgid="442019281547315527">"Не атрымалася загрузіць. Націсніце, каб паўтарыць спробу."</string>
-    <string name="attachments_header" msgid="6459475590011460229">"Далучэнні"</string>
-    <string name="menu_photo_save_all" msgid="4295226099481995348">"Захав. усё"</string>
-    <string name="menu_photo_share" msgid="2214772815165829079">"Адк. дост."</string>
-    <string name="menu_photo_share_all" msgid="4577299400844129078">"Падз. усё"</string>
-    <string name="saving" msgid="925514721138014677">"Захаванне..."</string>
-    <string name="choosertitle_sharevia" msgid="6142681649048665134">"Апублікаваць з дапамогай"</string>
-    <string name="contextmenu_openlink" msgid="2176176524824953775">"Адкрыць у браўзеры"</string>
-    <string name="contextmenu_copy" msgid="4604170699444153037">"Капіраваць"</string>
-    <string name="contextmenu_copylink" msgid="3580131515400610839">"Скапіраваць URL спасылкi"</string>
-    <string name="contextmenu_dial_dot" msgid="9104426720103729453">"Набраць..."</string>
-    <string name="contextmenu_sms_dot" msgid="6481040539501081547">"Адправiць SMS"</string>
-    <string name="contextmenu_add_contact" msgid="8395100913366045250">"Дадаць кантакт"</string>
-    <string name="contextmenu_send_mail" msgid="8301941130009797208">"Адправiць паведамленне электроннай пошты"</string>
-    <string name="contextmenu_map" msgid="7721544518698532954">"Карта"</string>
-    <string name="contextmenu_sharelink" msgid="6213520416250245200">"Падзяліцца спасылкай"</string>
-    <string name="contextmenu_help" msgid="4651718679842414097">"Даведка"</string>
-    <string name="contextmenu_feedback" msgid="4429118420169571473">"Адправiць водгук"</string>
-    <!-- no translation found for num_selected (5723323585692387843) -->
-    <skip />
-  <plurals name="move_conversation">
-    <item quantity="one" msgid="3657821200245019288">"Перамясціць размову"</item>
-    <item quantity="other" msgid="1685896770174368261">"Перамяшчэнне размоў: <xliff:g id="ID_1">%1$d</xliff:g>"</item>
-  </plurals>
-    <string name="content_description" msgid="5191020413686786341">"<xliff:g id="SENDER">%1$s</xliff:g> аб <xliff:g id="SUBJECT">%2$s</xliff:g>, <xliff:g id="SNIPPET">%3$s</xliff:g> у <xliff:g id="DATE">%4$s</xliff:g>, <xliff:g id="READSTATE">%5$s</xliff:g>"</string>
-    <string name="content_description_today" msgid="2303358176694038320">"<xliff:g id="SENDER">%1$s</xliff:g> аб <xliff:g id="SUBJECT">%2$s</xliff:g>, <xliff:g id="SNIPPET">%3$s</xliff:g> у <xliff:g id="TIME">%4$s</xliff:g>, <xliff:g id="READSTATE">%5$s</xliff:g>"</string>
-    <string name="read_string" msgid="219566857950091332">"запіс гутаркі прачытаны"</string>
-    <string name="unread_string" msgid="588180203444398297">"запіс гутаркі не прачытаны"</string>
-    <string name="filtered_tag" msgid="6470627954465675488">"[<xliff:g id="TAG">%1$s</xliff:g>]<xliff:g id="SUBJECT">%2$s</xliff:g>"</string>
-    <string name="subject_and_snippet" msgid="7884124651812798896">"<xliff:g id="ID_1">%s</xliff:g> — <xliff:g id="ID_2">%s</xliff:g>"</string>
-  <plurals name="draft">
-    <item quantity="one" msgid="333591132893648383">"Чарнавік"</item>
-    <item quantity="other" msgid="6961049507579977505">"Чарнавікі"</item>
-  </plurals>
-    <string name="sending" msgid="8214361929125649771">"Адпраўка..."</string>
-    <string name="send_failed" msgid="750908595144529579">"Паведамленне не адпраўлена."</string>
-    <!-- no translation found for me_object_pronun (7668994569594901571) -->
-    <skip />
-    <!-- no translation found for me_subject_pronun (1088657349831581116) -->
-    <skip />
-    <string name="inbox_folders_heading" msgid="3960094746248976565">"Уваходныя"</string>
-  <plurals name="confirm_delete_conversation">
-    <item quantity="one" msgid="3731948757247905508">"Выдаліць размову?"</item>
-    <item quantity="other" msgid="930334208937121234">"Выдаліць ​​гэтыя размовы (<xliff:g id="COUNT">%1$d</xliff:g>)?"</item>
-  </plurals>
-  <plurals name="confirm_archive_conversation">
-    <item quantity="one" msgid="2990537295519552069">"Заархiваваць гэту размову?"</item>
-    <item quantity="other" msgid="4713469868399246772">"Заархiваваць гэтыя размовы (<xliff:g id="COUNT">%1$d</xliff:g>)?"</item>
-  </plurals>
-  <plurals name="confirm_discard_drafts_conversation">
-    <item quantity="one" msgid="5974090449454432874">"Выдалiць чарнавiкi з гэтай размовы?"</item>
-    <item quantity="other" msgid="4173815457177336569">"Выдалiць чарнавiкi з гэтых размоў <xliff:g id="COUNT">%1$d</xliff:g>?"</item>
-  </plurals>
-    <string name="confirm_discard_text" msgid="1149834186404614612">"Скасаваць гэта паведамленне?"</string>
-    <string name="loading_conversations" msgid="2649440958602369555">"Загрузка…"</string>
-    <string name="empty_folder" msgid="35400008890372241">"Няма размоў."</string>
-    <string name="undo" msgid="6070869718469782251">"Адмянiць"</string>
-  <plurals name="conversation_unstarred">
-    <item quantity="one" msgid="8315328190288744398">"Здымаецца пазнака з размоў (<xliff:g id="COUNT">%1$d</xliff:g>)."</item>
-    <item quantity="other" msgid="2222365088907519938">"Здымаюцца пазнакi з размоў: <xliff:g id="COUNT">%1$d</xliff:g>."</item>
-  </plurals>
-  <plurals name="conversation_muted">
-    <item quantity="one" msgid="3197365771487542615">"Праiгнаравана: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-    <item quantity="other" msgid="1941193356069303587">"Праiгнаравана: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-  </plurals>
-  <plurals name="conversation_spammed">
-    <item quantity="one" msgid="3060864972122279220">"Пазначана як спам: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-    <item quantity="other" msgid="5071459475389411227">"Пазначана як спам: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-  </plurals>
-  <plurals name="conversation_not_spam">
-    <item quantity="one" msgid="7361303622131552596">"&lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt; пазначана як не спам."</item>
-    <item quantity="other" msgid="2250720080221922267">"&lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt; пазначана як не спам."</item>
-  </plurals>
-  <plurals name="conversation_not_important">
-    <item quantity="one" msgid="788387259924027833">"Пазначаны як няважныя: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-    <item quantity="other" msgid="6662213740575340397">"Пазначаны як няважныя: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-  </plurals>
-  <plurals name="conversation_phished">
-    <item quantity="one" msgid="5386916574432049235">"&lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt; пазначана як фiшынг."</item>
-    <item quantity="other" msgid="3678624994662639442">"&lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt; пазначана як фiшынг."</item>
-  </plurals>
-  <plurals name="conversation_archived">
-    <item quantity="one" msgid="5457612502965117672">"Заархiвавана: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-    <item quantity="other" msgid="3012054504370916482">"Заархiвавана: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-  </plurals>
-  <plurals name="conversation_deleted">
-    <item quantity="one" msgid="4398693029405479323">"Выдалена: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-    <item quantity="other" msgid="8630099095360065837">"Выдалена: &lt;b&gt;<xliff:g id="COUNT">%1$d</xliff:g>&lt;/b&gt;."</item>
-  </plurals>
-    <string name="deleted" msgid="2757349161107268029">"Выдалена"</string>
-    <string name="archived" msgid="7533995360704366325">"Заархiвавана"</string>
-    <string name="folder_removed" msgid="1047474677580149436">"Выдалена з <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
-  <plurals name="conversation_folder_changed">
-    <item quantity="one" msgid="4930161390461457462">"Змененая тэчка."</item>
-    <item quantity="other" msgid="8918589141287976985">"Змененыя тэчкі."</item>
-  </plurals>
-    <string name="conversation_folder_moved" msgid="297469098857964678">"Перамешчаная ў тэчку <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
-    <string name="search_results_header" msgid="4669917471897026269">"Вынікі"</string>
-    <string name="search_unsupported" msgid="4654227193354052607">"У гэтым уліковым запісе пошук не падтрымліваецца."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Пошук..."</string>
-    <!-- no translation found for add_label (6060201715848652940) -->
-    <skip />
-    <!-- no translation found for new_incoming_messages_one (827152289636155810) -->
-    <skip />
-    <!-- no translation found for new_incoming_messages_many:other (1018949581192786598) -->
-    <string name="expand_recipient_details" msgid="2266544827446346880">"Разгарнуць звесткi пра атрымальніка"</string>
-    <string name="collapse_recipient_details" msgid="3608892454869745927">"Згарнуць дадзеныя пра атрымальніка"</string>
-    <string name="contact_info_string" msgid="6911322458329265644">"Паказаць кантактную інфармацыю карыстальнiка <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="contact_info_string_default" msgid="596531738469809159">"Паказаць кантактную інфармацыю"</string>
-    <!-- no translation found for show_messages_read:other (7439716291751085801) -->
-    <!-- no translation found for from_heading (2454519463223197231) -->
-    <skip />
-    <!-- no translation found for replyto_heading (8563992423173220512) -->
-    <skip />
-    <!-- no translation found for to_heading (1142876841308812954) -->
-    <skip />
-    <!-- no translation found for cc_heading (52678613787360092) -->
-    <skip />
-    <!-- no translation found for bcc_heading (4698055226318660420) -->
-    <skip />
-    <!-- no translation found for date_heading (2086174555097691910) -->
-    <skip />
-    <string name="show_images" msgid="1875700481186331432">"Паказаць выявы"</string>
-    <string name="always_show_images" msgid="5966408990164581801">"Заўсёды паказваць выявы ад гэтага адпраўніка"</string>
-    <string name="always_show_images_toast" msgid="6218061921546973716">"Выявы ад гэтага адпраўніка будуць паказвацца аўтаматычна."</string>
-    <string name="address_display_format" msgid="3581303608028992576">"<xliff:g id="NAME">%1$s</xliff:g> <xliff:g id="EMAIL">%2$s</xliff:g>"</string>
-    <string name="address_display_format_with_via_domain" msgid="2316262813990842490">"<xliff:g id="NAME">%1$s</xliff:g> <xliff:g id="EMAIL">%2$s</xliff:g> праз <xliff:g id="VIA_DOMAIN">%3$s</xliff:g>"</string>
-    <string name="message_saved" msgid="7073947895562277576">"Паведамленне захавана як чарнавік."</string>
-    <string name="sending_message" msgid="1498583163419122128">"Адпраўка паведамлення..."</string>
-    <string name="invalid_recipient" msgid="2161317422860961623">"Адрас <xliff:g id="WRONGEMAIL">%s</xliff:g> несапраўдны."</string>
-    <string name="show_elided" msgid="5255917904110437886">"▶ Паказаць цытуемы тэкст"</string>
-    <string name="hide_elided" msgid="8907305605854960737">"▼ Схаваць цытуемы тэкст"</string>
-    <string name="message_invite_title" msgid="6757287288611722175">"Запрашэнне ў Календары"</string>
-    <string name="message_invite_calendar_view" msgid="1898326841767645665">"Паглядзець у Календары"</string>
-    <string name="message_invite_label_attending" msgid="7378806613557157546">"Збіраецеся?"</string>
-    <string name="message_invite_accept" msgid="869753811259793266">"Так"</string>
-    <string name="message_invite_tentative" msgid="3898050756490962079">"Магчыма"</string>
-    <string name="message_invite_decline" msgid="4524135969656266758">"Не"</string>
-    <string name="enumeration_comma" msgid="5191660524342286026">", "</string>
-    <string name="yes" msgid="7474216525286833848">"Так"</string>
-    <string name="no" msgid="427548507197604096">"Не"</string>
-    <string name="ok" msgid="8339355733816891026">"ОК"</string>
-    <string name="done" msgid="3079981801778027540">"Гатова"</string>
-    <string name="cancel" msgid="8762981157252199115">"Адмена"</string>
-    <string name="clear" msgid="3184687982186479442">"Выдалiць"</string>
-  <string-array name="sync_status">
-    <item msgid="2446076619901049026">"Паспяхова"</item>
-    <item msgid="7109065688039971961">"Няма злучэння."</item>
-    <item msgid="8437496123716232060">"Не атрымалася ўвайсці."</item>
-    <item msgid="1651266301325684887">"Памылка бяспекi."</item>
-    <item msgid="1461520171154288533">"Не атрымалася сінхранізаваць."</item>
-    <item msgid="4779810016424303449">"Унутраная памылка"</item>
-  </string-array>
-    <string name="tap_to_configure" msgid="1181356242354390930">"Націсніце, каб наладзіць"</string>
-    <string name="non_synced_folder_description" msgid="6059194125558004080">"Сінхр. гэту тэчку, каб прагл. разм."</string>
-    <string name="tap_to_configure_folder_sync" msgid="3846898229180591661">"Сінхранізацыя тэчкi"</string>
-    <string name="widget_large_unread_count" msgid="5668393672379294516">"%d+"</string>
-    <!-- no translation found for actionbar_unread_messages:other (6210217540181960613) -->
-    <string name="actionbar_large_unread_count" msgid="2780005218279965391">"Непрачытаных: больш за <xliff:g id="COUNT">%1$d</xliff:g>"</string>
-    <string name="view_more_conversations" msgid="3151067626309674344">"Глядзець iншыя размовы"</string>
-    <string name="loading_conversation" msgid="489160038366067983">"Загрузка..."</string>
-    <string name="activity_mailbox_selection" msgid="6876105223659297603">"Выберыце ўліковы запіс"</string>
-    <string name="activity_folder_selection" msgid="5161845867397316052">"Выберыце тэчку"</string>
-    <!-- no translation found for folder_shortcut_widget_label (6700454062559797794) -->
-    <skip />
-    <string name="change_folders_selection_dialog_title" msgid="8640832109613405534">"Змена папак"</string>
-    <string name="move_to_selection_dialog_title" msgid="1230762892675507608">"Перанесці"</string>
-    <string name="search_hint" msgid="3625734263059221350">"Пошук у пошце"</string>
-    <!-- no translation found for search_results_loaded (7840075360438913721) -->
-    <skip />
-    <string name="network_error" msgid="2125579009654728299">"Няма злучэння"</string>
-    <string name="retry" msgid="4515885444824418022">"Паўтарыць"</string>
-    <string name="load_more" msgid="420892447103014683">"Загрузіць яшчэ"</string>
-    <!-- no translation found for load_attachment (6181745283445986413) -->
-    <skip />
-    <string name="shortcut_name_title" msgid="8220482314700618391">"Назваць цэтлік да тэчкі"</string>
-    <string name="wait_for_sync_title" msgid="2767841242539238015">"Чаканне сінхранізацыі"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Ваш адрас электроннай пошты з\'явіцца ў бліжэйшы час."</string>
-    <string name="not_synced_title" msgid="794279749170198599">"Уліковы запіс не сінхранiзаваны"</string>
-    <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Гэты ўліковы запіс не наладжаны на аўтаматычную сінхранізацыю.\nДакраніцеся да кнопкі "<b>"Сінхранаваць зараз"</b>", каб адзін раз сінхранiзаваць пошту, або да кнопкі "<b>"Змена параметраў сінхранізацыі"</b>", каб наладзіць аўтаматычную сінхранізацыю пошты ў гэтым уліковым запісе."</string>
-    <string name="manual_sync" msgid="6743983544685622800">"Сінхранаваць зараз"</string>
-    <string name="change_sync_settings" msgid="5514293926214426577">"Змена параметраў сінхранізацыі"</string>
-    <string name="photo_load_failed" msgid="8811324070219403896">"Не атрымалася загрузіць малюнак"</string>
-    <string name="cant_move_or_change_labels" msgid="7997792014816166601">"Немагчыма працягваць, таму што выбар змяшчае некалькі ўліковых запісаў."</string>
-    <string name="multiple_new_message_notification_item" msgid="9125239830562919898"><b>"<xliff:g id="SENDER">%1$s</xliff:g>"</b>"   <xliff:g id="SUBJECT">%2$s</xliff:g>"</string>
-    <string name="single_new_message_notification_big_text" msgid="291800374285445488">"<xliff:g id="SUBJECT">%1$s</xliff:g>\n<xliff:g id="SNIPPET">%2$s</xliff:g>"</string>
-    <string name="ignore_spam_warning" msgid="1554651881128904262">"Ігнараваць, я давяраю адпраўнiку"</string>
-    <string name="via_domain" msgid="380109230928079350">"праз дамен <xliff:g id="VIADOMAIN">%1$s</xliff:g>"</string>
-  <string-array name="moveto_folder_sections">
-    <item msgid="6984976603137892898">"Сістэма"</item>
-    <item msgid="6593672292311851204">"Часта выкарыстоўваецца"</item>
-    <item msgid="3584541772344786752">"Усе тэчкi"</item>
-  </string-array>
-    <string name="signin" msgid="8958889809095796177">"Уваход"</string>
-    <string name="info" msgid="6009817562073541204">"Звесткі"</string>
-    <string name="report" msgid="5417082746232614958">"Справаздача"</string>
-    <string name="sync_error" msgid="7368819509040597851">"Не атрымлiваецца сінхранізаваць."</string>
-    <string name="sync_error_message" msgid="5367907194753034375">"На прыладзе не хапае месца для сiнхранiзацыi."</string>
-    <string name="storage" msgid="3262760336059073446">"Сховішча"</string>
-    <string name="senders_split_token" msgid="5895854795157860258">", "</string>
-    <string name="draft_count_format" msgid="4869620346730961822">" (<xliff:g id="COUNT">%1$s</xliff:g>)"</string>
-    <string name="all_folders_heading" msgid="2506843494725730618">"Усе папкi"</string>
-    <string name="recent_folders_heading" msgid="8613621794529777406">"Апошнія папкi"</string>
-    <string name="message_details_title" msgid="2183394728494962332">"Звесткi пра паведамленне"</string>
-    <string name="auto_advance_label" msgid="3192951873988392119">"Аўтаматычна ўперад"</string>
-    <string name="auto_advance_summary" msgid="573927628806250719">"Выберыце экран, якi будзе паказвацца пасля выдалення паведамлення"</string>
-    <string name="auto_advance_help_title" msgid="8091541690538426090">"Усталяваць аўтаматычны выбар пажаданых пазіцый\n(пасля выдалення і г.д.)"</string>
-  <string-array name="prefEntries_autoAdvance">
-    <item msgid="3400401085256526376">"Больш новая"</item>
-    <item msgid="5562093094216840213">"Больш ранняя"</item>
-    <item msgid="3331655314944797957">"Спіс гутарак"</item>
-  </string-array>
-  <string-array name="prefSummaries_autoAdvance">
-    <item msgid="6389534341359835440">"Паказаць больш новую гутарку пасля выдалення"</item>
-    <item msgid="732746454445519134">"Паказаць больш раннюю гутарку пасля выдалення"</item>
-    <item msgid="2189929276292165301">"Паказаць спіс гутарак пасля выдалення"</item>
-  </string-array>
-    <string name="clear_display_images_whitelist_title" msgid="7120575487854245735">"Выдаліць дазволы па выявах"</string>
-    <string name="clear_display_images_whitelist_dialog_title" msgid="3190704164490442683">"Выдаліць дазволы па выявах?"</string>
-    <string name="clear_display_images_whitelist_dialog_message" msgid="1169152185612117654">"Спыненне адлюстравання убудаваных выяў ад адпраўнікоў, якое раней было дазволена."</string>
-    <string name="sender_whitelist_cleared" msgid="917434007919176024">"Выявы не будуць адлюстроўвацца аўтаматычна"</string>
-    <!-- no translation found for preferences_signature_title (888167074398030574) -->
-    <skip />
-    <!-- no translation found for preferences_signature_dialog_title (5112192919923401319) -->
-    <skip />
-    <!-- no translation found for preferences_signature_summary_not_set (2830886980620445857) -->
-    <skip />
-    <string name="notification_action_reply" msgid="6015299134424685297">"Адказаць"</string>
-    <string name="notification_action_reply_all" msgid="20020468410400912">"Адказаць усім"</string>
-    <string name="notification_action_archive" msgid="81978513898440040">"Архіваваць"</string>
-    <string name="notification_action_remove_label" msgid="5852730267411032291">"Выдаліць цэтлік"</string>
-    <string name="notification_action_delete" msgid="327773454111551791">"Выдаліць"</string>
-    <string name="notification_action_undo_archive" msgid="7280710366587361219">"Заархiвавана"</string>
-    <string name="notification_action_undo_remove_label" msgid="1425192858683932457">"Цэтлiк выдалены"</string>
-    <string name="notification_action_undo_delete" msgid="3105611327110905438">"Выдалены"</string>
-    <string name="veiled_address" msgid="1209670815434513191"></string>
-    <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
-    <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
-    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
-    <string name="label_notification_ticker" msgid="1684732605316462621">"<xliff:g id="LABEL">%s</xliff:g>: <xliff:g id="NOTIFICATION">%s</xliff:g>"</string>
-    <string name="new_messages" msgid="4419173946074516420">"Новых паведамленняў: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
-    <string name="single_new_message_notification_title" msgid="4138237430881084155">"<xliff:g id="SENDER">%1$s</xliff:g>: <xliff:g id="SUBJECT">%2$s</xliff:g>"</string>
-    <string name="silent_ringtone" msgid="5856834572357761687">"Ціхі рэжым"</string>
-    <string name="preference_removal_action_title" msgid="6585232628422090835">"Архіў і выдаленыя дзеянні"</string>
-  <string-array name="prefEntries_removal_action">
-    <item msgid="1938422987822134657">"Паказаць толькі архiў"</item>
-    <item msgid="5737880287951287278">"Паказаць толькі выдаленне"</item>
-    <item msgid="4279948809971752573">"Паказаць архіў і выдаленне"</item>
-  </string-array>
-  <string-array name="prefSummaries_removal_action_summary">
-    <item msgid="492039800376391677">"Паказаць толькі архiў"</item>
-    <item msgid="8350973578419775975">"Паказаць толькі выдаленне"</item>
-    <item msgid="2967059853409454648">"Паказаць архіў і выдаленне"</item>
-  </string-array>
-    <string name="prefDialogTitle_removal_action" msgid="6225642737241938453">"Архіў і выдаленыя дзеянні"</string>
-    <string name="preferences_default_reply_all_title" msgid="7088691949152598569">"Адказаць усім"</string>
-    <!-- no translation found for preferences_default_reply_all_summary_impl (9131944420965452592) -->
-    <skip />
-    <string name="preference_swipe_title_archive" msgid="1178014339238995814">"Перайсцi ў архiў"</string>
-    <string name="preference_swipe_title_delete" msgid="8611320022727435597">"Прагарнiце, каб выдалiць"</string>
-    <string name="preference_swipe_description" msgid="5132683510510041028">"У спісе гутарак"</string>
-    <string name="preference_sender_image_title" msgid="2441320893522058724">"Выява адпраўнiка"</string>
-    <string name="preference_sender_image_description" msgid="7126493917536558232">"Паказваць побач з імем у спісе размоў"</string>
-    <string name="swipe_down_to_refresh" msgid="6401908905820786227">"Правядзіце ўніз, каб абнавіць"</string>
-    <string name="checking_for_mail" msgid="2301797903350897130">"Праверка пошты"</string>
-    <string name="empty_trash" msgid="652435297593538512">"Ачысціць кошык"</string>
-    <string name="empty_spam" msgid="6064497180615519589">"Выдаліць спам"</string>
-    <string name="empty_trash_dialog_title" msgid="3248644791068595150">"Ачысціць кошык?"</string>
-    <string name="empty_spam_dialog_title" msgid="8091014690336479280">"Выдаліць спам?"</string>
-  <plurals name="empty_folder_dialog_message">
-    <item quantity="one" msgid="7530191630810420066">"Гэта паведамленне (<xliff:g id="COUNT">%1$d</xliff:g>) будзе выдалена назаўсёды."</item>
-    <item quantity="other" msgid="2182899395748020953">"Гэтыя паведамленні (<xliff:g id="COUNT">%1$d</xliff:g>) будуць выдалены назаўсёды."</item>
-  </plurals>
-    <string name="drawer_open" msgid="6074646853178471940">"Адкрыць скрыню навігацыі"</string>
-    <string name="drawer_close" msgid="2764774620737876943">"Закрыць скрыню навігацыі"</string>
-    <string name="conversation_photo_welcome_text" msgid="4274875219447670662">"Націсніце на малюнак адпраўніка малюнак, каб выбраць размову."</string>
-    <!-- no translation found for long_press_to_select_tip (9205666839837250150) -->
-    <skip />
-    <string name="folder_icon_desc" msgid="1500547397347480618">"Значок тэчкі"</string>
-    <!-- no translation found for add_account (2162808366829269770) -->
-    <skip />
-    <string name="attached_message" msgid="4267189489654642035">"Прымацаванае паведамленне"</string>
-    <!-- no translation found for date_message_received (6302308934790266050) -->
-    <skip />
-    <!-- no translation found for date_message_received_today (7923108495669251362) -->
-    <skip />
-    <!-- no translation found for date_message_received_yesterday (2240669381396206434) -->
-    <skip />
-    <!-- no translation found for dismiss_tip_hover_text (1930345936314578166) -->
-    <skip />
-    <!-- no translation found for auto_sync_off (7185342309138211107) -->
-    <skip />
-    <!-- no translation found for tap_to_enable_sync (3334767826332383085) -->
-    <skip />
-    <!-- no translation found for account_sync_off (5699548271856775808) -->
-    <skip />
-    <!-- no translation found for enable_sync_in_account_settings (7707227409018294927) -->
-    <skip />
-    <!-- no translation found for account_settings_param (5277032997773498476) -->
-    <skip />
-    <!-- no translation found for unsent_messages_in_outbox (8754987959475577698) -->
-    <skip />
-    <!-- no translation found for turn_auto_sync_on_dialog_title (8981949974921704813) -->
-    <skip />
-    <!-- no translation found for turn_auto_sync_on_dialog_body (5070811094706347971) -->
-    <skip />
-    <!-- no translation found for phone (5171715391553213328) -->
-    <skip />
-    <!-- no translation found for tablet (7956969657216748522) -->
-    <skip />
-    <!-- no translation found for turn_auto_sync_on_dialog_confirm_btn (1445158420197688714) -->
-    <skip />
-    <!-- no translation found for show_n_more_folders (337747297817699776) -->
-    <skip />
-    <!-- no translation found for hide_folders (7473552966537131652) -->
-    <skip />
-    <!-- no translation found for print (7905250801319578415) -->
-    <skip />
-    <!-- no translation found for print_all (1789808179145024065) -->
-    <skip />
-    <!-- no translation found for num_messages:one (1997403772739309847) -->
-    <!-- no translation found for num_messages:other (3111597053027796035) -->
-    <!-- no translation found for date_message_received_print (456269555541859826) -->
-    <skip />
-    <!-- no translation found for draft_to_heading (3448702197598500284) -->
-    <skip />
-    <!-- no translation found for draft_heading (4654861166951306093) -->
-    <skip />
-    <!-- no translation found for quoted_text_hidden_print (8572207101897331252) -->
-    <skip />
-    <!-- no translation found for num_attachments:one (3344123914734915029) -->
-    <!-- no translation found for num_attachments:other (3556482119464721042) -->
-    <!-- no translation found for no_subject (7823949839148118599) -->
-    <skip />
-    <!-- no translation found for vacation_responder (145292667520371875) -->
-    <skip />
-    <!-- no translation found for preferences_vacation_responder_title (5775829187605350564) -->
-    <skip />
-    <!-- no translation found for vacation_responder_body_hint_text (5731550117332146705) -->
-    <skip />
-    <!-- no translation found for send_to_contacts_text (2696513899649703420) -->
-    <skip />
-    <!-- no translation found for send_to_domain_text (5055332409482662383) -->
-    <skip />
-    <!-- no translation found for pick_start_date_title (915230000170560933) -->
-    <skip />
-    <!-- no translation found for pick_end_date_title (2068598706186320098) -->
-    <skip />
-    <!-- no translation found for date_not_set (4925325938525330766) -->
-    <skip />
-    <!-- no translation found for set_end_date_dialog_title (72028456826019380) -->
-    <skip />
-    <!-- no translation found for custom_date (7566383722092597487) -->
-    <skip />
-    <!-- no translation found for date_none (1033448683454519235) -->
-    <skip />
-    <!-- no translation found for discard_changes (57677638191085556) -->
-    <skip />
-    <!-- no translation found for vacation_responder_changes_saved (7996997204497945275) -->
-    <skip />
-    <!-- no translation found for vacation_responder_changes_discarded (5699324320463227712) -->
-    <skip />
-    <!-- no translation found for vacation_responder_off (3502054743467746285) -->
-    <skip />
-    <!-- no translation found for vacation_responder_on (5242690599331655563) -->
-    <skip />
-    <!-- no translation found for vacation_responder_on_with_end_date (8198920866703798811) -->
-    <skip />
-    <!-- no translation found for vacation_responder_empty_subject_and_body_warning (2344675863430827845) -->
-    <skip />
-</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index efaa846..a97724c 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Преместено в/ъв „<xliff:g id="FOLDERNAME">%1$s</xliff:g>“"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Резултати"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Търсенето не се поддържа за този профил."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Търси се…"</string>
     <string name="add_label" msgid="6060201715848652940">"Добавяне на папка"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Показване на ново съобщение от <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Наименуване на пряк път към папка"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Изчаква се синхронизиране"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Имейлът ви ще се покаже след малко."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Профилът не е синхронизиран"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Този профил не е настроен за автоматично синхронизиране.\nДокоснете "<b>"Синхрон сега"</b>", за да синхронизирате еднократно пощата, или "<b>"Настройки за синхрон: Промяна"</b>", за да го настроите за автоматично синхронизиране."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Синхрон сега"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 528949b..5227afe 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"S\'ha mogut a <xliff:g id="FOLDERNAME">%1$s</xliff:g>."</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultats"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"La cerca no és compatible en aquest compte."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"S\'està cercant..."</string>
     <string name="add_label" msgid="6060201715848652940">"Afegeix una carpeta"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostra el missatge nou de <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Tecla de drecera per posar nom a la carpeta"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"S\'està esperant per sincronitzar"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"El correu electrònic apareixerà aviat."</string>
     <string name="not_synced_title" msgid="794279749170198599">"El compte no s\'ha sincronitzat"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Aquest compte no està configurat per sincronitzar automàticament.\nToca "<b>"Sincronitza ara"</b>" per sincronitzar el correu una vegada o bé "<b>"Canvia la configuració de sincronització"</b>" per configurar aquest compte per sincronitzar el correu de manera automàtica."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincronitza ara"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 409223b..ad83fc8 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Přesunuto do složky <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Výsledky"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Vyhledávání není v tomto účtu podporováno."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Vyhledávání..."</string>
     <string name="add_label" msgid="6060201715848652940">"Přidat složku"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Zobrazit novou zprávu od odesílatele <xliff:g id="SENDER">%s</xliff:g>"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Zadejte název zkratky složky"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Čekání na synchronizaci"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Vaše e-maily se zobrazí za chvíli."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Účet není synchronizován"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Tento účet není nastaven na automatickou synchronizaci.\nChcete-li poštu jednou synchronizovat, dotkněte se položky "<b>"Synchronizovat"</b>". Chcete-li v účtu nastavit automatickou synchronizaci pošty, dotkněte se položky "<b>"Změnit nastavení synchronizace"</b>"."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchronizovat"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7db4de9..5e5f6e4 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Flyttet til <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultater"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Søgningen understøttes ikke på denne konto."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Søger..."</string>
     <string name="add_label" msgid="6060201715848652940">"Tilføj mappe"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Vis ny meddelelse fra <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Genvej til navngivning af mappe"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Venter på synkronisering"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Din e-mail vises om et øjeblik."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Kontoen er ikke synkroniseret"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Denne konto er ikke konfigureret til automatisk synkronisering.\nTryk på "<b>"Synkroniser nu"</b>" for at synkronisere e-mails én gang, eller tryk på "<b>"Skift synkroniseringsindstillinger"</b>" for at konfigurere denne konto til at synkronisere e-mail automatisk."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synkroniser nu"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index fa02cf9..98ea0db 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Verschoben nach <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Ergebnisse"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Die Suche wird für dieses Konto nicht unterstützt."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Wird gesucht..."</string>
     <string name="add_label" msgid="6060201715848652940">"Ordner hinzufügen"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Neue Nachricht von <xliff:g id="SENDER">%s</xliff:g> anzeigen"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Ordnerverknüpfung benennen"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Warten auf Synchronisierung"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Ihre E-Mails werden gleich angezeigt."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Konto nicht synchronisiert"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Für dieses Konto ist keine automatische Synchronisierung eingerichtet.\nZur einmaligen Synchronisierung der E-Mails tippen Sie auf "<b>"Synchronisieren"</b>". Zum Einrichten der automatischen Synchronisierung für dieses Konto tippen Sie auf "<b>"Synchronisierungseinstellungen ändern"</b>"."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchronisieren"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 0b7e5a2..ffb81a8 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Μεταφέρθηκε στο φάκελο <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Αποτελέσματα"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Η αναζήτηση δεν υποστηρίζεται σε αυτόν τον λογαριασμό."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Αναζήτηση…"</string>
     <string name="add_label" msgid="6060201715848652940">"Προσθήκη φακέλου"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Εμφάνιση νέου μηνύματος από το χρήστη <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Όνομα συντόμευσης φακέλου"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Αναμονή για συγχρονισμό"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Η αλληλογραφία θα εμφανιστεί σύντομα."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Ο λογαριασμός δεν συγχρονίστηκε"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Ο λογαριασμός αυτός δεν έχει ρυθμιστεί για αυτόματο συγχρονισμό.\nΑγγίξτε "<b>"Συγχρ. τώρα"</b>" για συγχρονισμό της αλληλογραφίας μία φορά ή "<b>"Αλλαγή ρυθμίσεων συγχρονισμού"</b>" για να ρυθμίσετε το λογαριασμό για αυτόματο συγχρονισμό της αλληλογραφίας."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Συγχρ. τώρα"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 309c03b..33ab9bf 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Moved to <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Results"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Search is not supported on this account."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Searching…"</string>
     <string name="add_label" msgid="6060201715848652940">"Add folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Show new message from <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Name folder shortcut"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Waiting for sync"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Your email will appear shortly."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Account not synced"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"This account isn\'t set up to sync automatically.\nTouch "<b>"Sync Now"</b>" to sync mail once, or "<b>"Change Sync Settings"</b>" to set up this account to sync mail automatically."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sync now"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 309c03b..33ab9bf 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Moved to <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Results"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Search is not supported on this account."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Searching…"</string>
     <string name="add_label" msgid="6060201715848652940">"Add folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Show new message from <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Name folder shortcut"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Waiting for sync"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Your email will appear shortly."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Account not synced"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"This account isn\'t set up to sync automatically.\nTouch "<b>"Sync Now"</b>" to sync mail once, or "<b>"Change Sync Settings"</b>" to set up this account to sync mail automatically."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sync now"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 001b023..aa057b7 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Enviada a <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultados"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Esta cuenta no admite la función de búsqueda."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Buscando…"</string>
     <string name="add_label" msgid="6060201715848652940">"Agregar carpeta"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostrar mensaje nuevo de <xliff:g id="SENDER">%s</xliff:g>"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Definir el nombre del acceso directo a la carpeta"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Esperando para sincronizar"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Tu correo aparecerá en breve."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Cuenta no sincronizada"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Esta cuenta no está configurada para sincronizar el correo automáticamente.\nToca "<b>"Sincronizar ahora"</b>" para sincronizar el correo una vez o "<b>"Cambiar la configuración de sincronización"</b>" para que esta cuenta sincronice el correo automáticamente."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincr. ahora"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index b7d9d2c..b433b3f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Movida a <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultados"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Esta cuenta no admite la función de búsqueda."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Buscando..."</string>
     <string name="add_label" msgid="6060201715848652940">"Añadir carpeta"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostrar mensaje nuevo de <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Asignar nombre a acceso directo de carpeta"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Esperando para sincronizar..."</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Tu correo electrónico aparecerá en breve."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Cuenta no sincronizada"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Esta cuenta no está configurada para sincronizarse de forma automática.\nToca "<b>"Sincronizar ahora"</b>" para sincronizar el correo una vez o selecciona "<b>"Cambiar ajustes de sincronización"</b>" para configurar la sincronización automática del correo en esta cuenta."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincronizar ahora"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 77b5e2a..17276dd 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Teisaldati kausta <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Tulemused"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Sellel kontol ei toetata otsingut."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Otsimine ..."</string>
     <string name="add_label" msgid="6060201715848652940">"Lisa kaust"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Näita uut sõnumit saatjalt <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Kausta otseteele nime andmine"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Sünkroonimise ootel"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Teie meil ilmub peagi."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Konto ei ole sünkroonitud"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"See konto ei ole seadistatud automaatseks sünkroonimiseks.\nPuudutage käsku "<b>"Sünkrooni kohe"</b>", et meile üks kord sünkroonida, või käsku "<b>"Muuda sünkroonimisseadeid"</b>", et seadistada see konto meilide automaatseks sünkroonimiseks."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sünkr. kohe"</string>
diff --git a/res/values-et-sw600dp/strings.xml b/res/values-et-sw600dp/strings.xml
deleted file mode 100644
index 6877308..0000000
--- a/res/values-et-sw600dp/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-     Copyright (C) 2012 Google Inc.
-     Licensed to The Android Open Source Project.
-
-     Licensed 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="add_cc_label" msgid="6156834609108442458">"+ koopia/pimekoopia"</string>
-    <string name="add_bcc_label" msgid="7648769906379235693">"+ pimekoopia"</string>
-    <string name="menu_manage_folders" msgid="7574962882483466700">"Halda kaustu"</string>
-    <string name="preferences_default_reply_all_summary" msgid="5318418012992612729">"Kasuta vaikeseadena sõnumi märguannetele vastamisel"</string>
-</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index b69b97a..aef7203 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"به <xliff:g id="FOLDERNAME">%1$s</xliff:g> انتقال یافت"</string>
     <string name="search_results_header" msgid="4669917471897026269">"نتایج"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"جستجو در این حساب پشتیبانی نمی‌شود."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"در حال جستجو..."</string>
     <string name="add_label" msgid="6060201715848652940">"افزودن پوشه"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"نمایش پیام جدید از <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"میانبر نامگذاری پوشه"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"در انتظار همگام‌سازی"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"ایمیل شما به زودی نمایش داده می‌شود."</string>
     <string name="not_synced_title" msgid="794279749170198599">"حساب همگام نشد"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"این حساب برای همگام‌سازی به طور خودکار تنظیم نشده است.\nبرای یک بار همگام‌سازی نامه، "<b>"اکنون همگام‌سازی شود"</b>" را لمس کنید، یا برای تنظیم این حساب برای همگام‌سازی نامه به طور خودکار، "<b>"تنظیمات همگام‌سازی را تغییر دهید"</b>"."</string>
     <string name="manual_sync" msgid="6743983544685622800">"اکنون همگام‌سازی شود"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 1f2d3f8..562c3c9 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Siirretty kansioon <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Tulokset"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Hakua ei voi käyttää tällä tilillä."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Haetaan.."</string>
     <string name="add_label" msgid="6060201715848652940">"Lisää kansio"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Näytä uusi viesti lähettäjältä <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nimeä kansion pikakuvake"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Odotetaan synkronointia"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Sähköpostit näytetään pian."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Tiliä ei ole synkronoitu"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Tätä tiliä ei ole määritetty synkronoitavaksi automaattisesti.\nKosketa "<b>"Synkronoi"</b>" synkronoidaksesi sähköpostit kerran tai "<b>"Muuta synkronointiasetuksia"</b>" asettaaksesi tilin synkronoimaan sähköpostit automaattisesti."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synkronoi"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 1b33dc9..092d305 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Déplacée vers <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Résultats"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"La fonctionnalité de recherche n\'est pas compatible avec ce compte."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Recherche en cours…"</string>
     <string name="add_label" msgid="6060201715848652940">"Ajouter un dossier"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Afficher le nouveau message envoyé par <xliff:g id="SENDER">%s</xliff:g>"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nom du raccourci du dossier"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"En attente de synchronisation"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Votre courriel va bientôt s\'afficher."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Compte non synchronisé"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"La synchronisation automatique n\'est pas configurée pour ce compte.\nAppuyez sur "<b>"Synchroniser"</b>" pour une synchronisation ponctuelle de la messagerie ou sur "<b>"Modifier les paramètres"</b>" pour activer la synchronisation automatique."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchroniser"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index efb9123..411a911 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Déplacée vers <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Résultats"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"La fonctionnalité de recherche n\'est pas compatible avec ce compte."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Recherche en cours…"</string>
     <string name="add_label" msgid="6060201715848652940">"Ajouter un dossier"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Afficher le nouveau message envoyé par <xliff:g id="SENDER">%s</xliff:g>"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nom du raccourci du dossier"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"En attente de synchronisation"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Votre e-mail va bientôt s\'afficher."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Compte non synchronisé"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"La synchronisation automatique n\'est pas configurée pour ce compte.\nAppuyez sur "<b>"Synchroniser"</b>" pour une synchronisation ponctuelle de la messagerie ou sur "<b>"Modifier les paramètres"</b>" pour activer la synchronisation automatique."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchroniser"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 9adffc8..6fcd274 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"<xliff:g id="FOLDERNAME">%1$s</xliff:g> में ले जाया गया"</string>
     <string name="search_results_header" msgid="4669917471897026269">"परिणाम"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"इस खाते पर खोज समर्थित नहीं है."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"खोज रहा है…"</string>
     <string name="add_label" msgid="6060201715848652940">"फ़ोल्‍डर जोड़ें"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g> के नए संदेश दिखाएं."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"नाम फ़ोल्‍डर शॉर्टकट"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"समन्‍वयन की प्रतीक्षा कर रहा है"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"आपका ईमेल शीघ्र ही दिखाई देगा."</string>
     <string name="not_synced_title" msgid="794279749170198599">"खाता समन्वयित नहीं है"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"यह खाता अपने आप समन्वयित करने के लिए सेट नहीं है. \nएक बार मेल समन्‍वयित करने के लिए, "<b>"अभी समन्वयित करें"</b>" स्‍पर्श करें, या इस खाते को मेल अपने आप समन्‍वयित करने के लिए सेट करने हेतु "<b>"समन्वयन सेटिंग बदलें"</b>" स्‍पर्श करें."</string>
     <string name="manual_sync" msgid="6743983544685622800">"अभी समन्वयित करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index e744d1d..e89ce6c 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Premješteno u mapu <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Rezultati"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Pretraživanje nije podržano na ovom računu."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Traženje..."</string>
     <string name="add_label" msgid="6060201715848652940">"Dodaj mapu"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Pokaži nove poruke pošiljatelja <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Naziv prečaca mape"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Čekanje na sinkronizaciju"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Vaša će se e-pošta uskoro pojaviti."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Račun nije sinkroniziran"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Ovaj račun nije postavljen za automatsku sinkronizaciju.\nDodirnite "<b>"Sinkroniziraj sada"</b>" da biste poštu sinkronizirali jednom ili "<b>"Promjena sinkronizacije"</b>" da biste ovaj račun postavili za automatsku sinkronizaciju pošte."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sink. sada"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index ff09998..995955d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Áthelyezve ide: <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Találatok"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"A keresés nem támogatott ebben a fiókban."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Keresés..."</string>
     <string name="add_label" msgid="6060201715848652940">"Mappa hozzáadása"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Új üzenetek megjelenítése tőle: <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Mappaparancsikon elnevezése"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Várakozás a szinkronizálásra"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Az e-mail hamarosan megjelenik."</string>
     <string name="not_synced_title" msgid="794279749170198599">"A fiók nincs szinkronizálva"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Ez a fiók nincs beállítva automatikus szinkronizáláshoz.\nÉrintse meg a "<b>"Szinkr. most"</b>" elemet a levelek egyszeri szinkronizálásához, vagy érintse meg a "<b>"Szinkr. beállítások módosítása"</b>" lehetőséget a fiók leveleinek automatikusan szinkronizálásához."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Szinkr. most"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index d95c5cf..4d1661a 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Տեղափոխվել է <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Արդյունքներ"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Որոնումը չի աջակցվում այս հաշվի համար:"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Որոնում..."</string>
     <string name="add_label" msgid="6060201715848652940">"Ավելացնել թղթապանակ"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Ցույց տալ նոր հաղորդագրությունը <xliff:g id="SENDER">%s</xliff:g>-ից:"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Անվանեք դյուրանցման թղթապանակը"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Սպասում է համաժամեցմանը"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Ձեր էլփոստը շուտով կհայտնվի:"</string>
     <string name="not_synced_title" msgid="794279749170198599">"Հաշիվը համաժամեցված չէ"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Այս հաշիվը չի կարգավորվել ավտոմատ համաժամեցման համար:\nՀպեք "<b>"Համաժամեցնել հիմա՝"</b>" էլփոստը միանվագ համաժամեցնելու համար կամ "<b>"Փոխել համաժամեցման կարգավորումները`"</b>" այս հաշվի էլփոստը ինքնաբերաբար համաժամեցնելով կարգավորելու համար:"</string>
     <string name="manual_sync" msgid="6743983544685622800">"Համաժամեցնել հիմա"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 59731c5..ab44f9b 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Dipindahkan ke <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Hasil"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Penelusuran tidak didukung pada akun ini."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Menelusuri…"</string>
     <string name="add_label" msgid="6060201715848652940">"Tambahkan folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Tampilkan pesan baru dari <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Beri nama pintasan folder"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Menunggu sinkronisasi"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Email Anda akan segera muncul."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Akun tidak disinkronkan"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Akun ini tidak disiapkan untuk menyinkronkan secara otomatis.\nSentuh "<b>"Sinkronkan Sekarang"</b>" untuk menyinkronkan surat, atau "<b>"Ubah Setelan Sinkronisasi"</b>" untuk menyiapkan akun ini untuk sinkronisasi secara otomatis."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sinkron skrg"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 0b566b0..7e0468e 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Spostata in <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Risultati"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"La ricerca non è supportata per l\'account in uso."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Ricerca…"</string>
     <string name="add_label" msgid="6060201715848652940">"Aggiungi cartella"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostra nuovo messaggio di <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nome scorciatoia cartella"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"In attesa di sincronizzazione"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Le email verranno visualizzate tra poco."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Account non sincronizzato"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Questo account non è configurato per la sincronizzazione automatica.\nTocca "<b>"Sincronizza ora"</b>" per sincronizzare una volta i messaggi, oppure "<b>"Modifica impostazioni sincronizzazione"</b>" per configurare la sincronizzazione automatica dei messaggi per questo account."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincronizza"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index b92b8f4..9aa42ea 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"הועברה אל <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"תוצאות"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"חיפוש אינו נתמך בחשבון זה."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"מחפש…"</string>
     <string name="add_label" msgid="6060201715848652940">"הוסף תיקיה"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"הצג הודעות חדשות מ-<xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"תן שם לקיצור הדרך של התיקיה"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"ממתין לסנכרון"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"הדוא\"ל שלך יופיע בעוד זמן קצר."</string>
     <string name="not_synced_title" msgid="794279749170198599">"החשבון אינו מסונכרן"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"חשבון זה אינו מוגדר לסנכרון אוטומטי.\nגע באפשרות "<b>"סנכרן כעת"</b>" כדי לסנכרן את הדואר פעם אחת, או באפשרות "<b>"שנה הגדרות סנכרון"</b>" כדי להגדיר חשבון זה לסנכרון דואר באופן אוטומטי."</string>
     <string name="manual_sync" msgid="6743983544685622800">"סנכרן עכשיו"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 1bc682c..dbeae40 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"<xliff:g id="FOLDERNAME">%1$s</xliff:g>に移動しました"</string>
     <string name="search_results_header" msgid="4669917471897026269">"検索結果"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"このアカウントでは検索をご利用いただけません。"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"検索しています…"</string>
     <string name="add_label" msgid="6060201715848652940">"フォルダを追加"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g>さんからの新着メールを表示"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"フォルダのショートカットへの名前の指定"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"同期を待機中"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"まもなくメールが表示されます。"</string>
     <string name="not_synced_title" msgid="794279749170198599">"アカウントが同期していません"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"このアカウントには自動同期が設定されていません。\nメールを一度だけ同期させるには["<b>"今すぐ同期"</b>"]をタップします。このアカウントでメールが自動的に同期されるように設定するには、["<b>"同期設定の変更"</b>"]をタップします。"</string>
     <string name="manual_sync" msgid="6743983544685622800">"今すぐ同期"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 6b1b0d2..f89bf80 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"გადატანილია <xliff:g id="FOLDERNAME">%1$s</xliff:g>-ში"</string>
     <string name="search_results_header" msgid="4669917471897026269">"შედეგები"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"ძიება ამ ანგარიშში არ არის მხარდაჭერილი."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"ძიება…"</string>
     <string name="add_label" msgid="6060201715848652940">"საქაღალდის დამატება"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g>-დან მიღებული ახალი შეტყობინების ჩვენება."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"საქათალდის მალმძახისთვის სახელის დარქმევა"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"სინქრონიზაციის მოლოდინი"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"თქვენი ელფოსტა გამოჩნდება ცოტა ხანში."</string>
     <string name="not_synced_title" msgid="794279749170198599">"ანგარიში არ დასინქრონებულა"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"ამ ანგარიშს არ აქვს დაყენებული ავტომატური სინქრონიზაცია.\nშეეხეთ "<b>"სინქრონიზაციას"</b>" ელფოსტის ერთხელ სინქრონიზაციისთვის, ან "<b>"შეცვალეთ სინქრონიზაციის პარამეტრები"</b>" რათა დააყენოთ ელფოსტის ავტომატური სინქრონიზაცია."</string>
     <string name="manual_sync" msgid="6743983544685622800">"სინქრონიზაცია"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index c7cbab6..34c88e4 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"បាន​ផ្លាស់​ទី​ទៅ <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"លទ្ធផល"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"មិន​គាំទ្រ​ការ​ស្វែងរក​​នៅ​លើ​គណនី​នេះ​។"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"កំពុង​​ស្វែងរក..."</string>
     <string name="add_label" msgid="6060201715848652940">"បន្ថែម​ថត"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"បង្ហាញ​សារ​ថ្មី​ពី <xliff:g id="SENDER">%s</xliff:g> ។"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"ដាក់​ឈ្មោះ​ផ្លូវកាត់​ថត"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"រង់ចាំ​ធ្វើ​សម​កាល​កម្ម"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"អ៊ីមែល​របស់​អ្នក​នឹង​​លេចឡើង​​បន្តិច​ទៀត។"</string>
     <string name="not_synced_title" msgid="794279749170198599">"មិន​បាន​ធ្វើ​សម​កាល​កម្ម​គណនី"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"គណនី​នេះ​មិន​ត្រូវ​បាន​រៀបចំ ​​ដើម្បី​ធ្វើ​​សម​កាល​កម្ម​​ស្វ័យ​ប្រវត្តិ​។ \n ប៉ះ "<b>" ដើម្បី​ធ្វើ​សម​កាល​កម្ម​ឥឡូវ​នេះ "</b>" ​សំបុត្រ ​ឬ "<b>" ប្ដូរ​ការ​កំណត់​ការ​ធ្វើ​សម​កាល​កម្ម "</b>" ដើម្បី​រៀបចំ​គណនី​​នេះ​ត្រូវ​ធ្វើ​​សម​កាល​កម្ម​សំបុត្រ​​ស្វ័យ​ប្រវត្តិ​។"</string>
     <string name="manual_sync" msgid="6743983544685622800">"ធ្វើ​សម​កាល​កម្ម​ឥឡូវ​នេះ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 59081ad..7fdcd2b 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"<xliff:g id="FOLDERNAME">%1$s</xliff:g>(으)로 이동함"</string>
     <string name="search_results_header" msgid="4669917471897026269">"검색결과"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"이 계정에서는 검색이 지원되지 않습니다."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"검색 중…"</string>
     <string name="add_label" msgid="6060201715848652940">"폴더 추가"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g>님이 보낸 새 메일 보기"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"폴더 바로가기 이름 지정"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"동기화 대기 중"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"이메일이 곧 표시됩니다."</string>
     <string name="not_synced_title" msgid="794279749170198599">"계정이 동기화되지 않음"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"이 계정은 자동으로 동기화되도록 설정되지 않았습니다.\n"<b>"지금 동기화"</b>"를 터치하여 메일을 한 번 동기화하거나 "<b>"동기화 설정 변경"</b>"을 터치하여 이 계정이 메일을 자동으로 동기화하도록 설정하세요."</string>
     <string name="manual_sync" msgid="6743983544685622800">"지금 동기화"</string>
diff --git a/res/values-ldrtl/styles-ldrtl.xml b/res/values-ldrtl/styles-ldrtl.xml
index 0e2d0da..70a0afe 100644
--- a/res/values-ldrtl/styles-ldrtl.xml
+++ b/res/values-ldrtl/styles-ldrtl.xml
@@ -378,4 +378,13 @@
         <item name="android:layout_marginStart">@dimen/teaser_arrow_margin_start</item>
         <item name="android:layout_marginEnd">@dimen/teaser_arrow_margin_end</item>
     </style>
+
+    <style name="UndoDescriptionStyle">
+        <item name="android:paddingStart">@dimen/undo_description_padding</item>
+    </style>
+
+    <style name="UndoIconStyle">
+        <item name="android:paddingStart">@dimen/undo_icon_padding_start</item>
+        <item name="android:paddingEnd">@dimen/undo_icon_padding_end</item>
+    </style>
 </resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index bfd25d0..3230021 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"ຍ້າຍໄປ <xliff:g id="FOLDERNAME">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="search_results_header" msgid="4669917471897026269">"ຜົນໄດ້ຮັບ"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"ການຊອກຫາບໍ່ຮອງຮັບສຳລັບບັນຊີນີ້."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"ກຳລັງຊອກຫາ..."</string>
     <string name="add_label" msgid="6060201715848652940">"ເພີ່ມໂຟນເດີ"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"ສະ​ແດງ​ຂໍ້​ຄວາມ​ໃຫມ່​ຈາກ <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"ຊື່ໂຟນເດີທາງລັດ"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"ກຳລັງລໍຖ້າເພື່ອຊິ້ງຂໍ້ມູນ"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"ອີເມວຂອງທ່ານຈະໄປປາກົດໄວໆນີ້."</string>
     <string name="not_synced_title" msgid="794279749170198599">"ບັນຊີບໍ່ໄດ້ຊິ້ງຂໍ້ມູນ"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"ບັນຊີນີ້ບໍ່ໄດ້ຖືກຕັ້ງໃຫ້ຊິ້ງຂໍ້ມູນອັດຕະໂນມັດ.\nແຕະ "<b>"ຊິ້ງຂໍ້ມູນດຽວນີ້"</b>" ເພື່ອຊິ້ງຂໍ້ມູນເມວ ຫຼື "<b>"ປ່ຽນການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນ"</b>" ເພື່ອຕັ້ງໃຫ້ບັນຊີນີ້ຊິ້ງຂໍ້ມູນອີເມວອັດຕະໂນມັດ."</string>
     <string name="manual_sync" msgid="6743983544685622800">"ຊິ້ງຂໍ້ມູນດຽວນີ້"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 1eaa93b..226273b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Perkelta į <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Rezultatai"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Šioje paskyroje paieška nepalaikoma."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Ieškoma…"</string>
     <string name="add_label" msgid="6060201715848652940">"Pridėti aplanką"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Rodyti naują pranešimą nuo <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nurodyti spartųjį aplanko klavišą"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Laukiama sinchronizavimo"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"El. paštas netrukus bus rodomas."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Paskyra nesinchronizuota"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Ši paskyra nenustatyta automatiškai sinchronizuoti.\nJei norite vieną kartą sinchronizuoti paštą, palieskite "<b>"Sinch. dabar"</b>" arba palieskite "<b>"Keisti sinchronizavimo nustat."</b>", kad nustatytumėte šią paskyrą automatiškai sinchronizuoti paštą."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sinch. dabar"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index a56ca92..514c1b6 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Pārvietota uz: <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Rezultāti"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Šajā kontā netiek atbalstīta meklēšana."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Notiek meklēšana..."</string>
     <string name="add_label" msgid="6060201715848652940">"Pievienot mapi"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Rādīt jaunu ziņojumu no: <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nosaukuma piešķiršana mapes saīsnei"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Gaida sinhronizāciju"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-pasta saturs drīz tiks parādīts."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Konts nav sinhronizēts"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Šajā kontā nav iestatīta automātiskā sinhronizācija.\nPieskarieties vienumam "<b>"Sinhronizēt tūlīt"</b>", lai vienreiz sinhronizētu pastu, vai vienumam "<b>"Mainīt sinhronizācijas iestatījumus"</b>", lai šajā kontā iestatītu automātisku pasta sinhronizāciju."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sinhr. tūlīt"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 055f19d..7fce862 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"<xliff:g id="FOLDERNAME">%1$s</xliff:g> руу зөөсөн"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Илэрцүүд"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Энэ акаунтад хайлт дэмжигддэггүй."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Хайж байна..."</string>
     <string name="add_label" msgid="6060201715848652940">"Фолдер нэмэх"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g>-с ирсэн шинэ зурвасыг харуулах."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Нэрийн фолдерын товчилбор"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Синк хийгдэхийг хүлээж байна"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Таны имэйл удахгүй гарч ирэх болно."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Акаунт синк хийгдээгүй"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Энэ акаунтыг автоматаар синк хийхээр тохируулаагүй.\nИмэйлийг нэг удаа синк хийхийн тулд "<b>"Одоо синк хийх"</b>" гэсэнд хүрэх буюу энэ акаунтыг автоматаар синк хийхээр тохируулахын тулд "<b>"Синкийн Тохиргоог Өөрчлөх"</b>" гэсэнд хүрнэ үү."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Одоо синк хийх"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 76f0282..4c87353 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Dialihkan ke <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Hasil"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Carian tidak disokong pada akaun ini."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Mencari..."</string>
     <string name="add_label" msgid="6060201715848652940">"Tambah folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Tunjukkan mesej baharu daripada <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Namakan pintasan folder"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Menunggu penyegerakan"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-mel anda akan muncul tidak lama lagi."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Akaun tidak tersegerak"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Akaun ini tidak ditetapkan untuk disegerakkan secara automatik.\nSentuh "<b>"Segerakkan Sekarang"</b>" untuk menyegerakkan e-mel sekali, atau "<b>"Tukar Tetapan Penyegerakan"</b>" untuk menyediakan akaun ini agar menyegerakkan e-mel secara automatik."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Segerakkan sekarang"</string>
diff --git a/res/values-ms-sw600dp/strings.xml b/res/values-ms-sw600dp/strings.xml
deleted file mode 100644
index 85051e8..0000000
--- a/res/values-ms-sw600dp/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-     Copyright (C) 2012 Google Inc.
-     Licensed to The Android Open Source Project.
-
-     Licensed 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="add_cc_label" msgid="6156834609108442458">"+ Sk/Skt"</string>
-    <string name="add_bcc_label" msgid="7648769906379235693">"+ Skt"</string>
-    <string name="menu_manage_folders" msgid="7574962882483466700">"Urus folder"</string>
-    <string name="preferences_default_reply_all_summary" msgid="5318418012992612729">"Gunakan sebagai tetapan lalai untuk balasan kepada pemberitahuan mesej"</string>
-</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e295dbc..92501a5 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Flyttet til <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultater"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Søk støttes ikke på denne kontoen."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Søker …"</string>
     <string name="add_label" msgid="6060201715848652940">"Legg til mappe"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Vis ny e-post fra <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Navngi mappesnarvei"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Venter på synkronisering"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-posten din vises snart."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Kontoen ble ikke synkronisert"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Denne kontoen er ikke konfigurert til å synkroniseres automatisk.\nTrykk på "<b>"Synkroniser nå"</b>" for å synkronisere e-post én gang, eller trykk på "<b>"Endre synkroniseringsinnstillinger"</b>" for å konfigurere denne kontoen til å synkronisere e-post automatisk."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synkroniser nå"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index b027a5d..22c00e0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Verplaatst naar <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultaten"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Zoeken wordt niet ondersteund in dit account."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Zoeken…"</string>
     <string name="add_label" msgid="6060201715848652940">"Map toevoegen"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Nieuw bericht van <xliff:g id="SENDER">%s</xliff:g> weergeven."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Snelkoppeling naar map een naam geven"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Wachten op synchronisatie"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Uw e-mail wordt zo weergegeven."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Account niet gesynchroniseerd"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Automatische synchronisatie is niet ingesteld voor dit account.\nRaak"<b>"Nu synchroniseren"</b>" aan om e-mail één keer te synchroniseren of raak "<b>"Synchronisatie-instellingen wijzigen"</b>" aan om automatische synchronisatie in te stellen voor dit account."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Nu synchroniseren"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 239b1ef..8233761 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Przeniesiono do: <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Wyniki"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Wyszukiwanie nie jest obsługiwane na tym koncie."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Wyszukiwanie…"</string>
     <string name="add_label" msgid="6060201715848652940">"Dodaj folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Pokaż nową wiadomość od: <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Nazwij skrót folderu"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Czekam na synchronizację"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-maile pojawią się za chwilę."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Konto nie jest zsynchronizowane"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Konto nie jest skonfigurowane do automatycznej synchronizacji.\nKliknij "<b>"Synchronizuj"</b>", by zsynchronizować pocztę jeden raz, lub "<b>"Zmień ustawienia synchronizacji"</b>", by skonfigurować konto do automatycznej synchronizacji wiadomości."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchronizuj"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 3d3ca1c..fb355c0 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Movida para <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultados"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"A pesquisa não é suportada nesta conta."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"A pesquisar..."</string>
     <string name="add_label" msgid="6060201715848652940">"Adicionar pasta"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostrar nova mensagem de <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Atalho para pasta de nome"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"A aguardar sincronização"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"O seu email aparecerá em breve."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Conta não sincronizada"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"A conta não está configurada para sincronização automática.\nToque em "<b>"Sincronizar Agora"</b>" para sincronizar correio uma vez ou em "<b>"Alterar Definições de Sincronização"</b>" para configurar esta conta para sincronização automática de correio."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincronizar agora"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 78f6d80..22378a3 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Movida para <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultados"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Não há suporte para pesquisa nesta conta."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Pesquisando..."</string>
     <string name="add_label" msgid="6060201715848652940">"Adicionar pasta"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Mostrar nova mensagem de <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Atalho da pasta Nome"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Aguardando sincronização"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Seu e-mail será exibido em breve."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Conta não sincronizada"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Esta conta não está configurada para sincronizar automaticamente.\nToque em "<b>"Sincronizar agora"</b>" para sincronizar os e-mails uma vez ou em "<b>"Alterar configurações de sincronização"</b>" para configurar a sincronização automática de e-mails desta conta."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincr. agora"</string>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 59af2d4..c55cc96 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -317,8 +317,6 @@
     <skip />
     <!-- no translation found for search_unsupported (4654227193354052607) -->
     <skip />
-    <!-- no translation found for search_results_searching_header (3821361153909466819) -->
-    <skip />
     <!-- no translation found for add_label (6060201715848652940) -->
     <skip />
     <!-- no translation found for new_incoming_messages_one (827152289636155810) -->
@@ -441,8 +439,6 @@
     <skip />
     <!-- no translation found for wait_for_sync_title (2767841242539238015) -->
     <skip />
-    <!-- no translation found for wait_for_sync_body (7718316938173643688) -->
-    <skip />
     <!-- no translation found for not_synced_title (794279749170198599) -->
     <skip />
     <!-- no translation found for wait_for_manual_sync_body (2003192935254244968) -->
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1bdc80c..6dee716 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Mutată în <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Rezultate"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Căutarea nu este acceptată pentru acest cont."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Se caută..."</string>
     <string name="add_label" msgid="6060201715848652940">"Adăugați un dosar"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Afișați mesajul nou de la <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Denumiţi o comandă rapidă pentru dosar"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Se aşteaptă sincronizarea"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-mailul dvs. va apărea în curând."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Cont nesincronizat"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Acest cont nu este configurat să se sincronizeze în mod automat.\nAtingeţi "<b>"Sincronizaţi acum"</b>", pentru a sincroniza mesajele de e-mail o dată sau "<b>"Modificaţi setările de sincronizare"</b>" ca să configuraţi acest cont pentru a sincroniza mesajele de e-mail în mod automat."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sincron.acum"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 543d5aa..98dbb09 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Перемещено в папку \"<xliff:g id="FOLDERNAME">%1$s</xliff:g>\""</string>
     <string name="search_results_header" msgid="4669917471897026269">"Результаты"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"В этом аккаунте не поддерживается поиск."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Поиск…"</string>
     <string name="add_label" msgid="6060201715848652940">"Добавить папку"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Показать новое сообщение (автор: <xliff:g id="SENDER">%s</xliff:g>)"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Ярлык папки"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Синхронизация…"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Ваши сообщения загружаются."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Аккаунт не синхронизирован"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"В этом аккаунте не настроена автоматическая синхронизация.\nВыберите "<b>"Настройки синхронизации"</b>", чтобы включить ее, или "<b>"Синхронизировать сейчас"</b>", чтобы синхронизировать почту один раз."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Синхр."</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 3802ca6..6fa3ff1 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Presunuté do priečinka <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Výsledky"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Hľadanie nie je v tomto účte podporované."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Prebieha vyhľadávanie..."</string>
     <string name="add_label" msgid="6060201715848652940">"Pridať priečinok"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Zobraziť novú správu od odosielateľa <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Pomenovať skratku priečinka"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Čaká sa na synchronizáciu…"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Vaša pošta sa čoskoro zobrazí."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Účet nie je synchronizovaný"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Tento účet nie je nastavený na automatickú synchronizáciu.\nAk chcete synchronizovať e-maily jednorazovo, dotknite sa položky "<b>"Synchronizovať"</b>". Ak chcete nastaviť, aby účet synchronizoval e-maily automaticky, dotknite sa položky "<b>"Zmeniť nastavenia synchronizácie"</b>"."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synchroniz."</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 4f7a350..536ed7d 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Premaknjeno v: <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Rezultati"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Iskanje ni podprto za ta račun."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Iskanje ..."</string>
     <string name="add_label" msgid="6060201715848652940">"Dodaj mapo"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Prikaži novo sporočilo pošiljatelja <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Ime bližnjice do mape"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Čakanje na sinhronizacijo"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Vaša e-pošta se bo kmalu pojavila."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Račun ni sinhroniziran"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Ta račun se ne sinhronizira samodejno.\nDotaknite se "<b>"Sinhroniziraj"</b>", če želite enkrat sinhronizirati pošto, ali "<b>"Spremeni nastavitve sinhronizacije"</b>", če želite spremeniti nastavitve računa tako, da se bo pošta samodejno sinhronizirala."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sinhroniz."</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 1b0cbcb..bf84397 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Премештена је у <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Резултати"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Претрага није подржана на овом налогу."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Претраживање..."</string>
     <string name="add_label" msgid="6060201715848652940">"Додај директоријум"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Прикажи нову поруку пошиљаоца <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Именовање пречице директоријума"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Чека се синхронизација"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Имејл адреса ће се ускоро појавити."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Налог није синхронизован"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Овај налог није подешен за аутоматску синхронизацију.\nДодирните "<b>"Синхронизуј одмах"</b>" да бисте синхронизовали пошту једном, односно "<b>"Промени подешавања синхронизације"</b>" да бисте подесили овај налог да аутоматски синхронизује пошту."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Синхр. одмах"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b08bfb3..fe8fd1f 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Flyttades till <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Resultat"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Det går inte att söka i det här kontot."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Söker …"</string>
     <string name="add_label" msgid="6060201715848652940">"Lägg till mapp"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Visa nytt meddelande från <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Kortkommando för att namnge mapp"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Väntar på synk"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Ditt e-postmeddelande visas snart."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Kontot är inte synkat"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Det här kontot har inte konfigurerats så att det synkroniseras automatiskt.\nTryck på "<b>"Synka nu"</b>" om du vill synkronisera e-post en gång eller "<b>"Ändra synkroniseringsinställn."</b>" om du vill konfigurera kontot så att e-post synkroniseras automatiskt."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Synka nu"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 9905f8b..cd6e613 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Imehamishwa kwenda <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Matokeo"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Utafutaji hauauniwi kwenye akaunti hii."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Inatafuta…"</string>
     <string name="add_label" msgid="6060201715848652940">"Ongeza folda"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Onyesha barua pepe mpya kutoka kwa <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Ipe jina njia ya mkato ya folda"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Inasubiri kusawazishwa"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Barua pepe zako zitaonekana baada ya muda mfupi."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Akaunti haijasawazishwa"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Akaunti hii haijawekwa kujisawazisha kiotomatiki.\nGusa "<b>"Sawazisha Sasa"</b>" ili kusawazisha barua pepe mara moja, au "<b>"Badilisha Mipangilio ya Usawazishaji"</b>" ili kuweka akaunti hii isawazishe barua pepe kiotomatiki."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Sawazisha sasa"</string>
diff --git a/res/values-sw600dp/constants.xml b/res/values-sw600dp/constants.xml
index 10c02f4..91e2051 100644
--- a/res/values-sw600dp/constants.xml
+++ b/res/values-sw600dp/constants.xml
@@ -18,7 +18,6 @@
 <resources>
     <!-- Boolean value indicating whether the table UI should be used. -->
     <bool name="use_tablet_ui">true</bool>
-    <bool name="use_expansive_tablet_ui">false</bool>
     <integer name="conversation_header_mode">1</integer>
     <integer name="conversation_desired_font_size_px">16</integer>
 
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 9e6553e..efd3cfa 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"ย้ายไปยัง <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"ผลการค้นหา"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"บัญชีนี้ไม่สนับสนุนการค้นหา"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"กำลังค้นหา..."</string>
     <string name="add_label" msgid="6060201715848652940">"เพิ่มโฟลเดอร์"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"แสดงข้อความใหม่จาก <xliff:g id="SENDER">%s</xliff:g>"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"ตั้งชื่อทางลัดของโฟลเดอร์"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"กำลังรอการซิงค์"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"อีเมลของคุณจะปรากฏในไม่ช้า"</string>
     <string name="not_synced_title" msgid="794279749170198599">"บัญชีไม่ได้ซิงค์"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"บัญชีนี้ไม่ได้รับการตั้งค่าให้ซิงค์อัตโนมัติ\nแตะ "<b>"ซิงค์เลย"</b>" เพื่อซิงค์เมลหนึ่งครั้ง หรือแตะ "<b>"เปลี่ยนการตั้งค่าการซิงค์"</b>" เพื่อตั้งค่าบัญชีนี้ให้ซิงค์เมลอัตโนมัติ"</string>
     <string name="manual_sync" msgid="6743983544685622800">"ซิงค์เลย"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index b2e0b34..66b1382 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Inilipat sa <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Mga resulta"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Hindi sinusuportahan ang paghahanap sa account na ito."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Naghahanap..."</string>
     <string name="add_label" msgid="6060201715848652940">"Magdagdag ng folder"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Ipakita mga bagong mensahe mula kay <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Shortcut sa pangalan ng folder"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Naghihintay ng pag-sync"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Lilitaw na ang iyong email."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Hindi na-sync ang account"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Hindi naka-set up ang account na ito upang awtomatikong mag-sync.\nPindutin ang "<b>"I-sync Ngayon"</b>" upang i-sync ang mail nang isang beses, o "<b>"Baguhin ang Mga Setting ng Pag-sync"</b>" upang i-set up ang account na ito upang awtomatikong i-sync ang mail."</string>
     <string name="manual_sync" msgid="6743983544685622800">"I-sync na"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index e1a0312..ecbf706 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"<xliff:g id="FOLDERNAME">%1$s</xliff:g> klasörüne taşındı"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Sonuçlar"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Bu hesapta arama desteklenmiyor"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Aranıyor…"</string>
     <string name="add_label" msgid="6060201715848652940">"Klasör ekle"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"<xliff:g id="SENDER">%s</xliff:g> adlı kişiden gelen yeni mesajı göster."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Ad klasörü kısayolu"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Senkronize etmek için bekleniyor"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"E-postanız birazdan görüntülenecektir."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Hesap senkronize değil"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Bu hesap otomatik olarak senkronize edilecek şekilde yapılandırılmadı.\nPostayı bir kez senkronize etmek için "<b>"Şimdi Senk Et"</b>"\'e veya bu hesabı postayı otomatik olarak senkronize edilecek şekilde ayarlamak için "<b>"Senk Ayarlarını Değiştir"</b>"\'e dokunun."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Şimdi senk et"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index dfa484d..be11cf9 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Переміщено в папку <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Результати"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Функція пошуку в цьому обліковому записі не підтримується."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Пошук..."</string>
     <string name="add_label" msgid="6060201715848652940">"Додати папку"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Показати нове повідомлення (автор <xliff:g id="SENDER">%s</xliff:g>)."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Назвати ярлик папки"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Очікування на синхронізацію"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Електронна пошта з’явиться незабаром."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Обліковий запис не синхронізовано"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Цей обліковий запис не налаштовано для автоматичної синхронізації.\nВиберіть "<b>"Синхронізувати зараз"</b>", щоб синхронізувати пошту один раз, або "<b>"Змінити налаштування синхронізації"</b>", щоб налаштувати цей обліковий запис для автоматичної синхронізації пошти."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Синхр. зараз"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 45cb8de..4b494e3 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Đã chuyển vào <xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Kết quả"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Không hỗ trợ tính năng tìm kiếm trên tài khoản này."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Đang tìm kiếm…"</string>
     <string name="add_label" msgid="6060201715848652940">"Thêm thư mục"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Hiển thị tin nhắn mới từ <xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Lối tắt thư mục tên"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Đang đợi đồng bộ hóa"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"Email của bạn sẽ xuất hiện trong chốc lát."</string>
     <string name="not_synced_title" msgid="794279749170198599">"Tài khoản chưa được đồng bộ hóa"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Tài khoản này không được thiết lập để tự động đồng bộ hóa.\nChạm vào "<b>"Đồng bộ hóa ngay bây giờ"</b>" để đồng bộ hóa thư một lần hoặc "<b>"Thay đổi cài đặt đồng bộ hóa"</b>" để thiết lập tài khoản này tự động đồng bộ hóa thư."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Đồng bộ hóa ngay bây giờ"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3cfaa37..6e5ea1c 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"已移至“<xliff:g id="FOLDERNAME">%1$s</xliff:g>”"</string>
     <string name="search_results_header" msgid="4669917471897026269">"结果"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"此帐户不支持搜索。"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"正在搜索..."</string>
     <string name="add_label" msgid="6060201715848652940">"添加文件夹"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"显示来自<xliff:g id="SENDER">%s</xliff:g>的新邮件。"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"为文件夹快捷方式命名"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"正在等待同步"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"您的电子邮件很快就会显示。"</string>
     <string name="not_synced_title" msgid="794279749170198599">"帐户未同步"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"此帐户未设置为自动同步。您可以\n触摸"<b>"立即同步"</b>"同步邮件一次,或者触摸"<b>"更改同步设置"</b>",将此帐户设置为自动同步邮件。"</string>
     <string name="manual_sync" msgid="6743983544685622800">"立即同步"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c969464..0c60506 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"已移至「<xliff:g id="FOLDERNAME">%1$s</xliff:g>」"</string>
     <string name="search_results_header" msgid="4669917471897026269">"結果"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"這個帳戶不支援搜尋功能。"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"正在搜尋..."</string>
     <string name="add_label" msgid="6060201715848652940">"新增資料夾"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"顯示來自<xliff:g id="SENDER">%s</xliff:g>的新郵件。"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"為資料夾捷徑命名"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"正在等待同步處理"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"您的電子郵件即將顯示。"</string>
     <string name="not_synced_title" msgid="794279749170198599">"帳戶未同步"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"這個帳戶尚未設定為自動保持同步。\n請輕觸 [立即同步]"<b></b>",將您的郵件進行同步處理一次,或者輕觸 [更改同步設定]"<b></b>",將帳戶設定為自動保持郵件同步。"</string>
     <string name="manual_sync" msgid="6743983544685622800">"立即同步"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index fed79f3..4e5285f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"已移至「<xliff:g id="FOLDERNAME">%1$s</xliff:g>」"</string>
     <string name="search_results_header" msgid="4669917471897026269">"搜尋結果"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"這個帳戶不支援搜尋功能。"</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"搜尋中…"</string>
     <string name="add_label" msgid="6060201715848652940">"新增資料夾"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"顯示來自<xliff:g id="SENDER">%s</xliff:g>的新郵件。"</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"名稱資料夾捷徑"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"正在等待同步處理"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"即將顯示您的電子郵件。"</string>
     <string name="not_synced_title" msgid="794279749170198599">"帳戶尚未同步處理"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"這個帳戶尚未設成自動同步處理。\n請輕觸 [立即同步處理]"<b></b>",將您的郵件同步處理一次,或者輕觸 [變更同步處理設定]"<b></b>",將帳戶設定成自動同步處理郵件。"</string>
     <string name="manual_sync" msgid="6743983544685622800">"立即同步處理"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index a9b6740..86c0772 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -214,7 +214,6 @@
     <string name="conversation_folder_moved" msgid="297469098857964678">"Kuhanjiswe ku-<xliff:g id="FOLDERNAME">%1$s</xliff:g>"</string>
     <string name="search_results_header" msgid="4669917471897026269">"Imiphumela"</string>
     <string name="search_unsupported" msgid="4654227193354052607">"Usesho alusekelwe kule akhawunti."</string>
-    <string name="search_results_searching_header" msgid="3821361153909466819">"Iyasesha..."</string>
     <string name="add_label" msgid="6060201715848652940">"Engeza ifolda"</string>
     <string name="new_incoming_messages_one" msgid="827152289636155810">"Bonisa umlayezo omusha osuka ku-<xliff:g id="SENDER">%s</xliff:g>."</string>
   <plurals name="new_incoming_messages_many">
@@ -291,7 +290,6 @@
     <skip />
     <string name="shortcut_name_title" msgid="8220482314700618391">"Isinqamuleli sefolda yegama"</string>
     <string name="wait_for_sync_title" msgid="2767841242539238015">"Ilinde ukuvumelanisa"</string>
-    <string name="wait_for_sync_body" msgid="7718316938173643688">"I-imeyli yakho izovela maduze."</string>
     <string name="not_synced_title" msgid="794279749170198599">"I-akhawunti ayivumelanisiwe"</string>
     <string name="wait_for_manual_sync_body" msgid="2003192935254244968">"Le akhawunti ayisethiwe kufika ekuvumelaniseni kokuzenzakalela.\nThinta u-"<b>"Vumelanisa manje"</b>" ukuze uvumelanise imeyila kanye, noma "<b>"shintsha izilungiselelo zokuvumelanisa"</b>" ukuze usethe le akhawunti ukuze ivumelanise imeyili ngokuzenzakalela."</string>
     <string name="manual_sync" msgid="6743983544685622800">"Vumelanisa manje"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0ee3d0a..d75fbfb 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -45,8 +45,7 @@
     <!-- Folder List/Drawer colors -->
     <color name="account_item_selected_text_color">@color/mail_app_blue</color>
     <color name="folder_list_heading_text_color">@color/dark_gray_text_color</color>
-    <color name="list_background_color">@android:color/white</color>
-    <color name="footer_background_color">#eeeeee</color>
+    <color name="list_background_color">#f5f5f5</color>
     <color name="separator_color">#bebebe</color>
 
     <!-- Compose colors -->
@@ -138,4 +137,6 @@
     <color name="swipe_refresh_color4">#fff4b400</color>
 
     <color name="badge_background_color">#edb802</color>
+
+    <color name="loading_messages_background_color">#eeeeee</color>
 </resources>
diff --git a/res/values/constants.xml b/res/values/constants.xml
index c982a77..6e955ca 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -18,6 +18,7 @@
 <resources>
     <!-- Boolean value indicating whether the tablet UI should be used. -->
     <bool name="use_tablet_ui">false</bool>
+    <bool name="use_expansive_tablet_ui">false</bool>
     <integer name="conversation_header_mode">1</integer>
     <integer name="conversation_list_search_header_mode">1</integer>
 
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index 891bb9a..c59c8f5 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -214,4 +214,8 @@
 
     <dimen name="avatar_border_width">.5dp</dimen>
     <dimen name="progress_bar_height">12dp</dimen>
+
+    <dimen name="undo_description_padding">16dip</dimen>
+    <dimen name="undo_icon_padding_start">12dip</dimen>
+    <dimen name="undo_icon_padding_end">8dip</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 646bcd5..8c75234 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -445,9 +445,6 @@
     <!-- Toast shown when the user taps the search hard key when viewing an account that does not support search [CHAR LIMIT=100] -->
     <string name="search_unsupported">Search is not supported on this account.</string>
 
-    <!-- Search Results: Text for header that is shown above search results, while waiting for search results from the server [CHAR LIMIT=30] -->
-    <string name="search_results_searching_header">Searching\u2026</string>
-
     <!-- Conversation view -->
     <!--  Text anchor for control to add / change labels on a conversation when
           viewing it. [CHAR LIMIT=40] -->
@@ -617,8 +614,6 @@
     <!-- Wait fragment strings -->
     <!-- Displayed in title bar during the initial sync  [CHAR LIMIT=40] -->
     <string name="wait_for_sync_title">Waiting for sync</string>
-    <!-- Displayed in the middle of the screen during the initial sync [CHAR LIMIT=40] -->
-    <string name="wait_for_sync_body">Your email will appear shortly.</string>
     <!-- Title for the screen displayed during the initial sync [CHAR LIMIT=100]  -->
     <string name="not_synced_title">Account not synced</string>
     <!-- Displayed in the middle of the screen during the initial sync [CHAR LIMIT=300]-->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c208e35..c4d762e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -919,4 +919,13 @@
         <item name="android:layout_marginLeft">@dimen/teaser_arrow_margin_start</item>
         <item name="android:layout_marginRight">@dimen/teaser_arrow_margin_end</item>
     </style>
+
+    <style name="UndoDescriptionStyle">
+        <item name="android:paddingLeft">@dimen/undo_description_padding</item>
+    </style>
+
+    <style name="UndoIconStyle">
+        <item name="android:paddingLeft">@dimen/undo_icon_padding_start</item>
+        <item name="android:paddingRight">@dimen/undo_icon_padding_end</item>
+    </style>
 </resources>
diff --git a/src/com/android/emailcommon/internet/MimeHeader.java b/src/com/android/emailcommon/internet/MimeHeader.java
index 45ae318..c578bad 100644
--- a/src/com/android/emailcommon/internet/MimeHeader.java
+++ b/src/com/android/emailcommon/internet/MimeHeader.java
@@ -33,10 +33,6 @@
      * into the MIME data by LocalStore.fetch.
      */
     public static final String HEADER_ANDROID_ATTACHMENT_STORE_DATA = "X-Android-Attachment-StoreData";
-    /**
-     * Application specific header that is used to tag body parts for quoted/forwarded messages.
-     */
-    public static final String HEADER_ANDROID_BODY_QUOTED_PART = "X-Android-Body-Quoted-Part";
 
     public static final String HEADER_CONTENT_TYPE = "Content-Type";
     public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
diff --git a/src/com/android/emailcommon/internet/MimeMessage.java b/src/com/android/emailcommon/internet/MimeMessage.java
index aa275c5..9300f6d 100644
--- a/src/com/android/emailcommon/internet/MimeMessage.java
+++ b/src/com/android/emailcommon/internet/MimeMessage.java
@@ -96,12 +96,12 @@
      * @return a long, locally-generated message-ID value
      */
     private static String generateMessageId() {
-        StringBuffer sb = new StringBuffer();
+        final StringBuilder sb = new StringBuilder();
         sb.append("<");
         for (int i = 0; i < 24; i++) {
             // We'll use a 5-bit range (0..31)
-            int value = sRandom.nextInt() & 31;
-            char c = "0123456789abcdefghijklmnopqrstuv".charAt(value);
+            final int value = sRandom.nextInt() & 31;
+            final char c = "0123456789abcdefghijklmnopqrstuv".charAt(value);
             sb.append(c);
         }
         sb.append(".");
@@ -113,7 +113,7 @@
     /**
      * Parse the given InputStream using Apache Mime4J to build a MimeMessage.
      *
-     * @param in
+     * @param in InputStream providing message content
      * @throws IOException
      * @throws MessagingException
      */
@@ -134,20 +134,20 @@
         mSentDate = null;
         mBody = null;
 
-        MimeStreamParser parser = new MimeStreamParser();
+        final MimeStreamParser parser = new MimeStreamParser();
         parser.setContentHandler(new MimeMessageBuilder());
         return parser;
     }
 
     protected void parse(InputStream in) throws IOException, MessagingException {
-        MimeStreamParser parser = init();
+        final MimeStreamParser parser = init();
         parser.parse(new EOLConvertingInputStream(in));
         mComplete = !parser.getPrematureEof();
     }
 
     public void parse(InputStream in, EOLConvertingInputStream.Callback callback)
             throws IOException, MessagingException {
-        MimeStreamParser parser = init();
+        final MimeStreamParser parser = init();
         parser.parse(new EOLConvertingInputStream(in, getSize(), callback));
         mComplete = !parser.getPrematureEof();
     }
@@ -178,7 +178,7 @@
                 // TODO: We should make it more clear what exceptions can be thrown here,
                 // and whether they reflect a normal or error condition.
             } catch (Exception e) {
-
+                LogUtils.v(LogUtils.TAG, "Message missing Date header");
             }
         }
         if (mSentDate == null) {
@@ -190,7 +190,7 @@
                 // TODO: We should make it more clear what exceptions can be thrown here,
                 // and whether they reflect a normal or error condition.
             } catch (Exception e) {
-
+                LogUtils.v(LogUtils.TAG, "Message also missing Delivery-Date header");
             }
         }
         return mSentDate;
@@ -204,7 +204,7 @@
 
     @Override
     public String getContentType() throws MessagingException {
-        String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
+        final String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
         if (contentType == null) {
             return "text/plain";
         } else {
@@ -214,17 +214,12 @@
 
     @Override
     public String getDisposition() throws MessagingException {
-        String contentDisposition = getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
-        if (contentDisposition == null) {
-            return null;
-        } else {
-            return contentDisposition;
-        }
+        return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
     }
 
     @Override
     public String getContentId() throws MessagingException {
-        String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
+        final String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
         if (contentId == null) {
             return null;
         } else {
@@ -406,7 +401,7 @@
     public void setBody(Body body) throws MessagingException {
         this.mBody = body;
         if (body instanceof Multipart) {
-            Multipart multipart = ((Multipart)body);
+            final Multipart multipart = ((Multipart)body);
             multipart.setParent(this);
             setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
             setHeader("MIME-Version", "1.0");
@@ -494,8 +489,8 @@
             mExtendedHeader = null;
         } else {
             mExtendedHeader = new MimeHeader();
-            for (String header : END_OF_LINE.split(headers)) {
-                String[] tokens = header.split(":", 2);
+            for (final String header : END_OF_LINE.split(headers)) {
+                final String[] tokens = header.split(":", 2);
                 if (tokens.length != 2) {
                     throw new MessagingException("Illegal extended headers: " + headers);
                 }
@@ -523,7 +518,7 @@
      */
     @Override
     public void writeTo(OutputStream out) throws IOException, MessagingException {
-        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
+        final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
         // Force creation of local message-id
         getMessageId();
         getMimeHeaders().writeTo(out);
@@ -561,7 +556,7 @@
             } else {
                 expect(Part.class);
                 try {
-                    MimeMessage m = new MimeMessage();
+                    final MimeMessage m = new MimeMessage();
                     ((Part)stack.peek()).setBody(m);
                     stack.push(m);
                 } catch (MessagingException me) {
@@ -585,7 +580,7 @@
         public void field(String fieldData) {
             expect(Part.class);
             try {
-                String[] tokens = fieldData.split(":", 2);
+                final String[] tokens = fieldData.split(":", 2);
                 ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim());
             } catch (MessagingException me) {
                 throw new Error(me);
@@ -601,9 +596,9 @@
         public void startMultipart(BodyDescriptor bd) {
             expect(Part.class);
 
-            Part e = (Part)stack.peek();
+            final Part e = (Part)stack.peek();
             try {
-                MimeMultipart multiPart = new MimeMultipart(e.getContentType());
+                final MimeMultipart multiPart = new MimeMultipart(e.getContentType());
                 e.setBody(multiPart);
                 stack.push(multiPart);
             } catch (MessagingException me) {
@@ -614,7 +609,7 @@
         @Override
         public void body(BodyDescriptor bd, InputStream in) throws IOException {
             expect(Part.class);
-            Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
+            final Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
             try {
                 ((Part)stack.peek()).setBody(body);
             } catch (MessagingException me) {
@@ -632,7 +627,7 @@
             expect(MimeMultipart.class);
 
             try {
-                MimeBodyPart bodyPart = new MimeBodyPart();
+                final MimeBodyPart bodyPart = new MimeBodyPart();
                 ((MimeMultipart)stack.peek()).addBodyPart(bodyPart);
                 stack.push(bodyPart);
             } catch (MessagingException me) {
@@ -649,18 +644,19 @@
         @Override
         public void epilogue(InputStream is) throws IOException {
             expect(MimeMultipart.class);
-            StringBuffer sb = new StringBuffer();
+            final StringBuilder sb = new StringBuilder();
             int b;
             while ((b = is.read()) != -1) {
                 sb.append((char)b);
             }
+            // TODO: why is this commented out?
             // ((Multipart) stack.peek()).setEpilogue(sb.toString());
         }
 
         @Override
         public void preamble(InputStream is) throws IOException {
             expect(MimeMultipart.class);
-            StringBuffer sb = new StringBuffer();
+            final StringBuilder sb = new StringBuilder();
             int b;
             while ((b = is.read()) != -1) {
                 sb.append((char)b);
diff --git a/src/com/android/emailcommon/mail/Address.java b/src/com/android/emailcommon/mail/Address.java
index 307c1a0..d2bc09f 100644
--- a/src/com/android/emailcommon/mail/Address.java
+++ b/src/com/android/emailcommon/mail/Address.java
@@ -91,6 +91,7 @@
 
     private static final String LOG_TAG = LogTag.getLogTag();
 
+    @VisibleForTesting
     public Address(String address) {
         setAddress(address);
     }
@@ -196,6 +197,7 @@
      * This method is used to check that all the addresses that the user
      * entered in a list (e.g. To:) are valid, so that none is dropped.
      */
+    @VisibleForTesting
     public static boolean isAllValid(String addressList) {
         // This code mimics the parse() method below.
         // I don't know how to better avoid the code-duplication.
@@ -321,6 +323,7 @@
      * @param addresses Address array
      * @return Human readable comma-delimited address string.
      */
+    @VisibleForTesting
     public static String toString(Address[] addresses) {
         return toString(addresses, ADDRESS_DELIMETER);
     }
@@ -391,6 +394,7 @@
      * @return the personal part of this Address, or the address part if the
      * personal part is not available
      */
+    @VisibleForTesting
     public String toFriendly() {
         if (mPersonal != null && mPersonal.length() > 0) {
             return mPersonal;
@@ -407,6 +411,7 @@
      * @return A comma-delimited string listing all of the addresses supplied.  Null if source
      * was null or empty.
      */
+    @VisibleForTesting
     public static String toFriendly(Address[] addresses) {
         if (addresses == null || addresses.length == 0) {
             return null;
@@ -425,6 +430,7 @@
     /**
      * Returns exactly the same result as Address.toString(Address.fromHeader(addressList)).
      */
+    @VisibleForTesting
     public static String fromHeaderToString(String addressList) {
         return toString(fromHeader(addressList));
     }
@@ -432,6 +438,7 @@
     /**
      * Returns exactly the same result as Address.toHeader(Address.parse(addressList)).
      */
+    @VisibleForTesting
     public static String parseToHeader(String addressList) {
         return Address.toHeader(Address.parse(addressList));
     }
@@ -441,6 +448,7 @@
      * The same as Address.fromHeader(addressList)[0] for non-empty list.
      * This is an utility method that offers some performance optimization opportunities.
      */
+    @VisibleForTesting
     public static Address firstAddress(String addressList) {
         Address[] array = fromHeader(addressList);
         return array.length > 0 ? array[0] : null;
@@ -462,6 +470,7 @@
      * @param addressList a CSV of RFC822 addresses or the deprecated legacy string format
      * @return array of addresses parsed from <code>addressList</code>
      */
+    @VisibleForTesting
     public static Address[] fromHeader(String addressList) {
         if (addressList == null || addressList.length() == 0) {
             return EMPTY_ADDRESS_ARRAY;
diff --git a/src/com/android/emailcommon/utility/ConversionUtilities.java b/src/com/android/emailcommon/utility/ConversionUtilities.java
index 0dbb520..7bfe7d9 100644
--- a/src/com/android/emailcommon/utility/ConversionUtilities.java
+++ b/src/com/android/emailcommon/utility/ConversionUtilities.java
@@ -27,13 +27,6 @@
 
 public class ConversionUtilities {
     /**
-     * Values for HEADER_ANDROID_BODY_QUOTED_PART to tag body parts
-     */
-    public static final String BODY_QUOTED_PART_REPLY = "quoted-reply";
-    public static final String BODY_QUOTED_PART_FORWARD = "quoted-forward";
-    public static final String BODY_QUOTED_PART_INTRO = "quoted-intro";
-
-    /**
      * Helper function to append text to a StringBuffer, creating it if necessary.
      * Optimization:  The majority of the time we are *not* appending - we should have a path
      * that deals with single strings.
@@ -60,9 +53,6 @@
     public static class BodyFieldData {
         public String textContent;
         public String htmlContent;
-        public String textReply;
-        public String htmlReply;
-        public String introText;
         public String snippet;
         public boolean isQuotedReply;
         public boolean isQuotedForward;
@@ -76,39 +66,12 @@
         final BodyFieldData data = new BodyFieldData();
         StringBuffer sbHtml = null;
         StringBuffer sbText = null;
-        StringBuffer sbHtmlReply = null;
-        StringBuffer sbTextReply = null;
-        StringBuffer sbIntroText = null;
 
         for (Part viewable : viewables) {
             String text = MimeUtility.getTextFromPart(viewable);
-            String[] replyTags = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART);
-            String replyTag = null;
-            if (replyTags != null && replyTags.length > 0) {
-                replyTag = replyTags[0];
-            }
             // Deploy text as marked by the various tags
             boolean isHtml = "text/html".equalsIgnoreCase(viewable.getMimeType());
 
-            if (replyTag != null) {
-                data.isQuotedReply = BODY_QUOTED_PART_REPLY.equalsIgnoreCase(replyTag);
-                data.isQuotedForward = BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(replyTag);
-                boolean isQuotedIntro = BODY_QUOTED_PART_INTRO.equalsIgnoreCase(replyTag);
-
-                if (data.isQuotedReply || data.isQuotedForward) {
-                    if (isHtml) {
-                        sbHtmlReply = appendTextPart(sbHtmlReply, text);
-                    } else {
-                        sbTextReply = appendTextPart(sbTextReply, text);
-                    }
-                    continue;
-                }
-                if (isQuotedIntro) {
-                    sbIntroText = appendTextPart(sbIntroText, text);
-                    continue;
-                }
-            }
-
             // Most of the time, just process regular body parts
             if (isHtml) {
                 sbHtml = appendTextPart(sbHtml, text);
@@ -130,15 +93,6 @@
                 data.snippet = TextUtilities.makeSnippetFromHtmlText(text);
             }
         }
-        if (sbHtmlReply != null && sbHtmlReply.length() != 0) {
-            data.htmlReply = sbHtmlReply.toString();
-        }
-        if (sbTextReply != null && sbTextReply.length() != 0) {
-            data.textReply = sbTextReply.toString();
-        }
-        if (sbIntroText != null && sbIntroText.length() != 0) {
-            data.introText = sbIntroText.toString();
-        }
         return data;
     }
 }
diff --git a/src/com/android/mail/SenderInfoLoader.java b/src/com/android/mail/SenderInfoLoader.java
index e7f153c..a8e6f37 100644
--- a/src/com/android/mail/SenderInfoLoader.java
+++ b/src/com/android/mail/SenderInfoLoader.java
@@ -17,7 +17,7 @@
 
 package com.android.mail;
 
-import com.android.bitmap.Trace;
+import com.android.oldbitmap.Trace;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 
@@ -45,7 +45,6 @@
 /**
  * A {@link Loader} to look up presence, contact URI, and photo data for a set of email
  * addresses.
- *
  */
 public class SenderInfoLoader extends AsyncTaskLoader<ImmutableMap<String, ContactInfo>> {
 
diff --git a/src/com/android/mail/adapter/DrawerItem.java b/src/com/android/mail/adapter/DrawerItem.java
index 7c5771e..87bb9f1 100644
--- a/src/com/android/mail/adapter/DrawerItem.java
+++ b/src/com/android/mail/adapter/DrawerItem.java
@@ -22,7 +22,7 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import com.android.bitmap.BitmapCache;
+import com.android.oldbitmap.BitmapCache;
 import com.android.mail.R;
 import com.android.mail.bitmap.ContactResolver;
 import com.android.mail.providers.Account;
diff --git a/src/com/android/mail/analytics/Analytics.java b/src/com/android/mail/analytics/Analytics.java
index 551f468..778f3e0 100644
--- a/src/com/android/mail/analytics/Analytics.java
+++ b/src/com/android/mail/analytics/Analytics.java
@@ -85,6 +85,9 @@
         public void sendEvent(String category, String action, String label, long value) {}
 
         @Override
+        public void sendTiming(String category, long millis, String name, String label) {}
+
+        @Override
         public void sendMenuItemEvent(String category, int itemResId, String label, long value) {}
 
         @Override
diff --git a/src/com/android/mail/analytics/AnalyticsTimer.java b/src/com/android/mail/analytics/AnalyticsTimer.java
new file mode 100644
index 0000000..1392a38
--- /dev/null
+++ b/src/com/android/mail/analytics/AnalyticsTimer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed 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 com.android.mail.analytics;
+
+import android.os.SystemClock;
+
+import com.google.common.collect.Maps;
+
+import java.util.Map;
+
+/**
+ * Generic static singleton timer that keeps track of start time of various events. It  logs the
+ * event's duration into Analytics using the provided naming information.
+ * This timer class supports multiple data points per event ("lapping").
+ *
+ * This class also holds some defaults constant IDs that we log. This provides an easy way to check
+ * what data we are logging as well as ensuring that the IDs are consistent when accessed by
+ * different classes.
+ */
+public class AnalyticsTimer {
+    public static final String OPEN_CONV_VIEW_FROM_LIST = "open_conv_from_list";
+    public static final String COLD_START_LAUNCHER = "cold_start_to_list";
+    public static final String SEARCH_TO_LIST = "search_to_list";
+
+    private final Map<String, Long> mStartTimes = Maps.newConcurrentMap();
+
+    // Static singleton class to ensure that you can access the timer from anywhere in the code
+    private static final AnalyticsTimer mInstance = new AnalyticsTimer();
+
+    private AnalyticsTimer() {}
+
+    public static AnalyticsTimer getInstance() {
+        return mInstance;
+    }
+
+    /**
+     * Record the current time as the start time of the provided id. If the id has a previously
+     * recorded start time, that time is overwritten.
+     * @param id
+     */
+    public void trackStart(String id) {
+        mStartTimes.put(id, SystemClock.uptimeMillis());
+    }
+
+    /**
+     * Logs the duration of the event with the provided category, name, and label.
+     * This method can be destructive, meaning that any additional calls without calling
+     * {@link AnalyticsTimer#trackStart(String)} will do nothing
+     * We allow the method to be destructive to prevent the following cases from happening:
+     *   - recurring methods that call this with irrelevant mapped start times.
+     *   - multiple entry ways to the method that calls this, thus misusing the
+     *     start time.
+     * With destructive read, we ensure that we only log the event that we care about and only once.
+     * @param id id of the event
+     * @param isDestructive if you are done with this tag (used for multiple data points per tag)
+     * @param category category for analytics logging
+     * @param name name for analytics logging
+     * @param label label for analytics logging
+     */
+    public void logDuration(String id, boolean isDestructive, String category, String name,
+            String label) {
+        Long value = isDestructive ? mStartTimes.remove(id) : mStartTimes.get(id);
+        if (value != null) {
+            Analytics.getInstance().sendTiming(category, SystemClock.uptimeMillis() - value, name,
+                    label);
+        }
+    }
+}
diff --git a/src/com/android/mail/analytics/Tracker.java b/src/com/android/mail/analytics/Tracker.java
index a96677c..6a9e710 100644
--- a/src/com/android/mail/analytics/Tracker.java
+++ b/src/com/android/mail/analytics/Tracker.java
@@ -27,6 +27,7 @@
     void activityStart(Activity a);
     void activityStop(Activity a);
     void sendEvent(String category, String action, String label, long value);
+    void sendTiming(String category, long millis, String name, String label);
     void sendMenuItemEvent(String category, int itemResId, String label, long value);
     void sendView(String view);
     void setCustomDimension(int index, String value);
diff --git a/src/com/android/mail/bitmap/AccountAvatarDrawable.java b/src/com/android/mail/bitmap/AccountAvatarDrawable.java
index ad658b3..4c75b12 100644
--- a/src/com/android/mail/bitmap/AccountAvatarDrawable.java
+++ b/src/com/android/mail/bitmap/AccountAvatarDrawable.java
@@ -14,9 +14,9 @@
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.Drawable;
 
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.ReusableBitmap;
-import com.android.bitmap.DecodeTask.Request;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.ReusableBitmap;
+import com.android.oldbitmap.DecodeTask.Request;
 import com.android.mail.R;
 import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface;
 
diff --git a/src/com/android/mail/bitmap/AttachmentDrawable.java b/src/com/android/mail/bitmap/AttachmentDrawable.java
index 49e4008..3ed5d4e 100644
--- a/src/com/android/mail/bitmap/AttachmentDrawable.java
+++ b/src/com/android/mail/bitmap/AttachmentDrawable.java
@@ -16,13 +16,13 @@
 import android.util.DisplayMetrics;
 import android.view.animation.LinearInterpolator;
 
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.BitmapUtils;
-import com.android.bitmap.DecodeAggregator;
-import com.android.bitmap.DecodeTask;
-import com.android.bitmap.DecodeTask.Request;
-import com.android.bitmap.ReusableBitmap;
-import com.android.bitmap.Trace;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.BitmapUtils;
+import com.android.oldbitmap.DecodeAggregator;
+import com.android.oldbitmap.DecodeTask;
+import com.android.oldbitmap.DecodeTask.Request;
+import com.android.oldbitmap.ReusableBitmap;
+import com.android.oldbitmap.Trace;
 import com.android.mail.R;
 import com.android.mail.browse.ConversationItemViewCoordinates;
 import com.android.mail.ui.SwipeableListView;
diff --git a/src/com/android/mail/bitmap/AttachmentGridDrawable.java b/src/com/android/mail/bitmap/AttachmentGridDrawable.java
index c1e5789..eb85bb7 100644
--- a/src/com/android/mail/bitmap/AttachmentGridDrawable.java
+++ b/src/com/android/mail/bitmap/AttachmentGridDrawable.java
@@ -8,8 +8,8 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.DecodeAggregator;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.DecodeAggregator;
 import com.android.mail.R;
 import com.android.mail.browse.ConversationItemViewCoordinates;
 
diff --git a/src/com/android/mail/bitmap/CompositeDrawable.java b/src/com/android/mail/bitmap/CompositeDrawable.java
index e372bb5..05d681e 100644
--- a/src/com/android/mail/bitmap/CompositeDrawable.java
+++ b/src/com/android/mail/bitmap/CompositeDrawable.java
@@ -6,7 +6,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
-import com.android.bitmap.Trace;
+import com.android.oldbitmap.Trace;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/mail/bitmap/ContactCheckableGridDrawable.java b/src/com/android/mail/bitmap/ContactCheckableGridDrawable.java
index 134439d..6a4481c 100644
--- a/src/com/android/mail/bitmap/ContactCheckableGridDrawable.java
+++ b/src/com/android/mail/bitmap/ContactCheckableGridDrawable.java
@@ -28,7 +28,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
-import com.android.bitmap.BitmapCache;
+import com.android.oldbitmap.BitmapCache;
 import com.android.mail.R.color;
 import com.android.mail.R.drawable;
 
diff --git a/src/com/android/mail/bitmap/ContactDrawable.java b/src/com/android/mail/bitmap/ContactDrawable.java
index 93c8e86..11d885a 100644
--- a/src/com/android/mail/bitmap/ContactDrawable.java
+++ b/src/com/android/mail/bitmap/ContactDrawable.java
@@ -27,9 +27,9 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.DecodeTask.Request;
-import com.android.bitmap.ReusableBitmap;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.DecodeTask.Request;
+import com.android.oldbitmap.ReusableBitmap;
 import com.android.mail.R;
 import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface;
 
diff --git a/src/com/android/mail/bitmap/ContactGridDrawable.java b/src/com/android/mail/bitmap/ContactGridDrawable.java
index 590f806..ed03ff8 100644
--- a/src/com/android/mail/bitmap/ContactGridDrawable.java
+++ b/src/com/android/mail/bitmap/ContactGridDrawable.java
@@ -21,7 +21,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 
-import com.android.bitmap.BitmapCache;
+import com.android.oldbitmap.BitmapCache;
 import com.android.mail.R;
 
 /**
diff --git a/src/com/android/mail/bitmap/ContactRequest.java b/src/com/android/mail/bitmap/ContactRequest.java
index ebb41de..f33719a 100644
--- a/src/com/android/mail/bitmap/ContactRequest.java
+++ b/src/com/android/mail/bitmap/ContactRequest.java
@@ -18,7 +18,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.text.TextUtils;
 
-import com.android.bitmap.DecodeTask;
+import com.android.oldbitmap.DecodeTask;
 import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface;
 
 import java.io.ByteArrayInputStream;
diff --git a/src/com/android/mail/bitmap/ContactResolver.java b/src/com/android/mail/bitmap/ContactResolver.java
index 228e9bb..0c5bf8c 100644
--- a/src/com/android/mail/bitmap/ContactResolver.java
+++ b/src/com/android/mail/bitmap/ContactResolver.java
@@ -22,10 +22,10 @@
 import android.os.AsyncTask.Status;
 import android.os.Handler;
 
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.DecodeTask;
-import com.android.bitmap.ReusableBitmap;
-import com.android.bitmap.DecodeTask.Request;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.DecodeTask;
+import com.android.oldbitmap.ReusableBitmap;
+import com.android.oldbitmap.DecodeTask.Request;
 import com.android.ex.photo.util.Trace;
 import com.android.mail.ContactInfo;
 import com.android.mail.SenderInfoLoader;
diff --git a/src/com/android/mail/bitmap/ImageAttachmentRequest.java b/src/com/android/mail/bitmap/ImageAttachmentRequest.java
index b680db5..4d93ed4 100644
--- a/src/com/android/mail/bitmap/ImageAttachmentRequest.java
+++ b/src/com/android/mail/bitmap/ImageAttachmentRequest.java
@@ -6,7 +6,7 @@
 import android.net.Uri;
 import android.text.TextUtils;
 
-import com.android.bitmap.DecodeTask;
+import com.android.oldbitmap.DecodeTask;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.UIProvider;
 
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
index 8a72533..33f612e 100644
--- a/src/com/android/mail/browse/AttachmentActionHandler.java
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Parcelable;
+import android.view.View;
 
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.Message;
@@ -265,6 +266,37 @@
         sOptionHandler.handleOption1(mContext, mAccount, mMessage, mAttachment, mFragmentManager);
     }
 
+    public static boolean shouldShowAboveBarAttachmentLayout(Context context) {
+        return (sOptionHandler != null) &&
+                sOptionHandler.shouldShowAboveBarAttachmentLayout(context);
+    }
+
+    public static void setupAboveBarAttachmentLayout(View view) {
+        if (sOptionHandler != null) {
+            sOptionHandler.setupAboveBarAttachmentLayout(view);
+        }
+    }
+
+    public static void onOverflowOpened(Context context) {
+        if(sOptionHandler != null) {
+            sOptionHandler.onOverflowOpened(context);
+        }
+    }
+
+    public static void registerDismissListener(Uri conversationUri,
+            AboveAttachmentLayoutDismissedListener listener) {
+        if (sOptionHandler != null) {
+            sOptionHandler.registerDismissListener(conversationUri, listener);
+        }
+    }
+
+    public static void unregisterDismissListeners(Uri conversationUri) {
+        if(sOptionHandler != null) {
+            sOptionHandler.unregisterDismissListeners(conversationUri);
+        }
+    }
+
+
     /**
      * A default, no-op option class. Override this and set it globally with
      * {@link AttachmentActionHandler#setOptionHandler(OptionHandler)}.<br>
@@ -278,12 +310,31 @@
             return false;
         }
 
-        @SuppressWarnings("unused")
         public void handleOption1(Context context, String account, Message message,
                 Attachment attachment, FragmentManager fm) {
             // no-op
         }
 
+        public boolean shouldShowAboveBarAttachmentLayout(Context context) {
+            return false;
+        }
+
+        public void setupAboveBarAttachmentLayout(View view) { /* no-op */ }
+
+        public void onOverflowOpened(Context context) { /* no-op */ }
+
+        public void registerDismissListener(Uri conversationUri,
+                AboveAttachmentLayoutDismissedListener listener) {
+            // no-op
+        }
+
+        public void unregisterDismissListeners(Uri conversationUri) {
+            // no-op
+        }
+    }
+
+    public interface AboveAttachmentLayoutDismissedListener {
+        void onOtherLayoutDismissed();
     }
 
 }
diff --git a/src/com/android/mail/browse/ConversationContainer.java b/src/com/android/mail/browse/ConversationContainer.java
index 0fa9d03..eff4430 100644
--- a/src/com/android/mail/browse/ConversationContainer.java
+++ b/src/com/android/mail/browse/ConversationContainer.java
@@ -197,7 +197,6 @@
     /**
      * Child views of this container should implement this interface to be notified when they are
      * being detached.
-     *
      */
     public interface DetachListener {
         /**
diff --git a/src/com/android/mail/browse/ConversationCursor.java b/src/com/android/mail/browse/ConversationCursor.java
index 7a3ca0f..a5dbb5d 100644
--- a/src/com/android/mail/browse/ConversationCursor.java
+++ b/src/com/android/mail/browse/ConversationCursor.java
@@ -130,7 +130,7 @@
     /** Set when we've sent refreshRequired() to listeners */
     private boolean mRefreshRequired = false;
     /** Whether our first query on this cursor should include a limit */
-    private boolean mInitialConversationLimit = false;
+    private boolean mUseInitialConversationLimit = false;
     /** A list of mostly-dead items */
     private final List<Conversation> mMostlyDead = Lists.newArrayList();
     /** A list of items pending removal from a notification action. These may be undone later.
@@ -187,9 +187,9 @@
         handleNotificationActions();
     }
 
-    public ConversationCursor(Activity activity, Uri uri, boolean initialConversationLimit,
+    public ConversationCursor(Activity activity, Uri uri, boolean useInitialConversationLimit,
             String name) {
-        mInitialConversationLimit = initialConversationLimit;
+        mUseInitialConversationLimit = useInitialConversationLimit;
         mResolver = activity.getApplicationContext().getContentResolver();
         qUri = uri;
         mName = name;
@@ -208,11 +208,11 @@
             try {
                 // Create new ConversationCursor
                 LogUtils.d(LOG_TAG, "Create: initial creation");
-                setCursor(doQuery(mInitialConversationLimit));
+                setCursor(doQuery(mUseInitialConversationLimit));
             } finally {
                 // If we used a limit, queue up a query without limit
-                if (mInitialConversationLimit) {
-                    mInitialConversationLimit = false;
+                if (mUseInitialConversationLimit) {
+                    mUseInitialConversationLimit = false;
                     // We want to notify about this change to allow the UI to requery.  We don't
                     // want to directly call refresh() here as this will start an AyncTask which
                     // is normally only run after the cursor is in the "refresh required"
@@ -688,9 +688,11 @@
                             // cache entry
                             mDeletedCount--;
                             removed = true;
-                            LogUtils.d(LOG_TAG,
+                            LogUtils.i(LOG_TAG,
                                     "IN resetCursor, sDeletedCount decremented to: %d by %s",
-                                    mDeletedCount, key);
+                                    mDeletedCount,
+                                    (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) ? key
+                                            : "[redacted]");
                         }
                     }
                 } else {
@@ -1518,20 +1520,23 @@
 
         private int mUndoSequence = 0;
         private ArrayList<Uri> mUndoDeleteUris = new ArrayList<Uri>();
+        private UndoCallback mUndoCallback = null;
 
-        void addToUndoSequence(Uri uri) {
+        void addToUndoSequence(Uri uri, UndoCallback undoCallback) {
             if (sSequence != mUndoSequence) {
                 mUndoSequence = sSequence;
                 mUndoDeleteUris.clear();
+                mUndoCallback = undoCallback;
             }
             mUndoDeleteUris.add(uri);
         }
 
         @VisibleForTesting
-        void deleteLocal(Uri uri, ConversationCursor conversationCursor) {
+        void deleteLocal(Uri uri, ConversationCursor conversationCursor,
+                UndoCallback undoCallback) {
             String uriString = uriStringFromCachingUri(uri);
             conversationCursor.cacheValue(uriString, DELETED_COLUMN, true);
-            addToUndoSequence(uri);
+            addToUndoSequence(uri, undoCallback);
         }
 
         @VisibleForTesting
@@ -1540,11 +1545,12 @@
             conversationCursor.cacheValue(uriString, DELETED_COLUMN, false);
         }
 
-        void setMostlyDead(Conversation conv, ConversationCursor conversationCursor) {
+        void setMostlyDead(Conversation conv, ConversationCursor conversationCursor,
+                           UndoCallback undoCallback) {
             Uri uri = conv.uri;
             String uriString = uriStringFromCachingUri(uri);
             conversationCursor.setMostlyDead(uriString, conv);
-            addToUndoSequence(uri);
+            addToUndoSequence(uri, undoCallback);
         }
 
         void commitMostlyDead(Conversation conv, ConversationCursor conversationCursor) {
@@ -1571,6 +1577,11 @@
             // Notify listeners that there was a change to the underlying
             // cursor to add back in some items.
             conversationCursor.notifyDataChanged();
+
+            // If the caller specified an undo callback, call it here
+            if (mUndoCallback != null) {
+                mUndoCallback.performUndoCallback();
+            }
         }
 
         @VisibleForTesting
@@ -1708,6 +1719,9 @@
         private final Uri mUri;
         private final Conversation mConversation;
         private final ContentValues mValues;
+        // Callback handler for when this operation is undone
+        private final UndoCallback mUndoCallback;
+
         // True if an updated item should be removed locally (from ConversationCursor)
         // This would be the case for a folder change in which the conversation is no longer
         // in the folder represented by the ConversationCursor
@@ -1718,15 +1732,17 @@
         // Whether this item is already mostly dead
         private final boolean mMostlyDead;
 
-        public ConversationOperation(int type, Conversation conv) {
-            this(type, conv, null);
+        public ConversationOperation(int type, Conversation conv, UndoCallback undoCallback) {
+            this(type, conv, null, undoCallback);
         }
 
-        public ConversationOperation(int type, Conversation conv, ContentValues values) {
+        public ConversationOperation(int type, Conversation conv, ContentValues values,
+                UndoCallback undoCallback) {
             mType = type;
             mUri = conv.uri;
             mConversation = conv;
             mValues = values;
+            mUndoCallback = undoCallback;
             mLocalDeleteOnUpdate = conv.localDeleteOnUpdate;
             mMostlyDead = conv.isMostlyDead();
         }
@@ -1740,7 +1756,7 @@
             switch(mType) {
                 case UPDATE:
                     if (mLocalDeleteOnUpdate) {
-                        sProvider.deleteLocal(mUri, ConversationCursor.this);
+                        sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
                     } else {
                         sProvider.updateLocal(mUri, mValues, ConversationCursor.this);
                         mRecalibrateRequired = false;
@@ -1754,7 +1770,7 @@
                     }
                     break;
                 case MOSTLY_DESTRUCTIVE_UPDATE:
-                    sProvider.setMostlyDead(mConversation, ConversationCursor.this);
+                    sProvider.setMostlyDead(mConversation, ConversationCursor.this, mUndoCallback);
                     op = ContentProviderOperation.newUpdate(uri).withValues(mValues).build();
                     break;
                 case INSERT:
@@ -1766,7 +1782,7 @@
                 // "Mostly" operations are reflected globally, but not locally, except to set
                 // FLAG_MOSTLY_DEAD in the conversation itself
                 case DELETE:
-                    sProvider.deleteLocal(mUri, ConversationCursor.this);
+                    sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
                     if (!mMostlyDead) {
                         op = ContentProviderOperation.newDelete(uri).build();
                     } else {
@@ -1774,11 +1790,11 @@
                     }
                     break;
                 case MOSTLY_DELETE:
-                    sProvider.setMostlyDead(mConversation,ConversationCursor.this);
+                    sProvider.setMostlyDead(mConversation,ConversationCursor.this, mUndoCallback);
                     op = ContentProviderOperation.newDelete(uri).build();
                     break;
                 case ARCHIVE:
-                    sProvider.deleteLocal(mUri, ConversationCursor.this);
+                    sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
                     if (!mMostlyDead) {
                         // Create an update operation that represents archive
                         op = ContentProviderOperation.newUpdate(uri).withValue(
@@ -1790,7 +1806,7 @@
                     }
                     break;
                 case MOSTLY_ARCHIVE:
-                    sProvider.setMostlyDead(mConversation, ConversationCursor.this);
+                    sProvider.setMostlyDead(mConversation, ConversationCursor.this, mUndoCallback);
                     // Create an update operation that represents archive
                     op = ContentProviderOperation.newUpdate(uri).withValue(
                             ConversationOperations.OPERATION_KEY, ConversationOperations.ARCHIVE)
@@ -1798,7 +1814,7 @@
                     break;
                 case MUTE:
                     if (mLocalDeleteOnUpdate) {
-                        sProvider.deleteLocal(mUri, ConversationCursor.this);
+                        sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
                     }
 
                     // Create an update operation that represents mute
@@ -1808,7 +1824,7 @@
                     break;
                 case REPORT_SPAM:
                 case REPORT_NOT_SPAM:
-                    sProvider.deleteLocal(mUri, ConversationCursor.this);
+                    sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
 
                     final String operation = mType == REPORT_SPAM ?
                             ConversationOperations.REPORT_SPAM :
@@ -1819,7 +1835,7 @@
                             ConversationOperations.OPERATION_KEY, operation).build();
                     break;
                 case REPORT_PHISHING:
-                    sProvider.deleteLocal(mUri, ConversationCursor.this);
+                    sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
 
                     // Create an update operation that represents report phishing
                     op = ContentProviderOperation.newUpdate(uri).withValue(
@@ -1827,7 +1843,7 @@
                             ConversationOperations.REPORT_PHISHING).build();
                     break;
                 case DISCARD_DRAFTS:
-                    sProvider.deleteLocal(mUri, ConversationCursor.this);
+                    sProvider.deleteLocal(mUri, ConversationCursor.this, mUndoCallback);
 
                     // Create an update operation that represents discarding drafts
                     op = ContentProviderOperation.newUpdate(uri).withValue(
@@ -2052,14 +2068,20 @@
      * @return the sequence number of the operation (for undo)
      */
     public int updateValues(Collection<Conversation> conversations, ContentValues values) {
+        return updateValues(conversations, values, null);
+    }
+
+    public int updateValues(Collection<Conversation> conversations, ContentValues values,
+            UndoCallback undoCallback) {
         return apply(
-                getOperationsForConversations(conversations, ConversationOperation.UPDATE, values));
+                getOperationsForConversations(conversations, ConversationOperation.UPDATE, values,
+                        undoCallback));
     }
 
     /**
      * Apply many operations in a single batch transaction.
      * @param op the collection of operations obtained through successive calls to
-     * {@link #getOperationForConversation(Conversation, int, ContentValues)}.
+     * {@link #getOperationForConversation(Conversation, int, ContentValues, UndoCallback)}.
      * @return the sequence number of the operation (for undo)
      */
     public int updateBulkValues(Collection<ConversationOperation> op) {
@@ -2067,17 +2089,23 @@
     }
 
     private ArrayList<ConversationOperation> getOperationsForConversations(
-            Collection<Conversation> conversations, int type, ContentValues values) {
+            Collection<Conversation> conversations, int type, ContentValues values,
+            UndoCallback undoCallback) {
         final ArrayList<ConversationOperation> ops = Lists.newArrayList();
         for (Conversation conv: conversations) {
-            ops.add(getOperationForConversation(conv, type, values));
+            ops.add(getOperationForConversation(conv, type, values, undoCallback));
         }
         return ops;
     }
 
     public ConversationOperation getOperationForConversation(Conversation conv, int type,
             ContentValues values) {
-        return new ConversationOperation(type, conv, values);
+        return getOperationForConversation(conv, type, values, null);
+    }
+
+    public ConversationOperation getOperationForConversation(Conversation conv, int type,
+            ContentValues values, UndoCallback undoCallback) {
+        return new ConversationOperation(type, conv, values, undoCallback);
     }
 
     public static void addFolderUpdates(ArrayList<Uri> folderUris, ArrayList<Boolean> add,
@@ -2096,16 +2124,29 @@
 
     public ConversationOperation getConversationFolderOperation(Conversation conv,
             ArrayList<Uri> folderUris, ArrayList<Boolean> add, Collection<Folder> targetFolders) {
-        return getConversationFolderOperation(conv, folderUris, add, targetFolders,
-                new ContentValues());
+        return getConversationFolderOperation(conv, folderUris, add, targetFolders, null, null);
     }
 
     public ConversationOperation getConversationFolderOperation(Conversation conv,
             ArrayList<Uri> folderUris, ArrayList<Boolean> add, Collection<Folder> targetFolders,
             ContentValues values) {
+        return getConversationFolderOperation(conv, folderUris, add, targetFolders, values, null);
+    }
+
+    public ConversationOperation getConversationFolderOperation(Conversation conv,
+            ArrayList<Uri> folderUris, ArrayList<Boolean> add, Collection<Folder> targetFolders,
+            UndoCallback undoCallback) {
+        return getConversationFolderOperation(conv, folderUris, add, targetFolders,
+                new ContentValues(), undoCallback);
+    }
+
+    public ConversationOperation getConversationFolderOperation(Conversation conv,
+            ArrayList<Uri> folderUris, ArrayList<Boolean> add, Collection<Folder> targetFolders,
+            ContentValues values, UndoCallback undoCallback) {
         addFolderUpdates(folderUris, add, values);
         addTargetFolders(targetFolders, values);
-        return getOperationForConversation(conv, ConversationOperation.UPDATE, values);
+        return getOperationForConversation(conv, ConversationOperation.UPDATE, values,
+                undoCallback);
     }
 
     // Convenience methods
@@ -2136,63 +2177,99 @@
      * underlying provider. See applyAction for argument descriptions
      */
     public int delete(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.DELETE);
+        return delete(conversations, null);
+    }
+
+    public int delete(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.DELETE, undoCallback);
     }
 
     /**
      * As above, for archive
      */
     public int archive(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.ARCHIVE);
+        return archive(conversations, null);
+    }
+
+    public int archive(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.ARCHIVE, undoCallback);
     }
 
     /**
      * As above, for mute
      */
     public int mute(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.MUTE);
+        return mute(conversations, null);
+    }
+
+    public int mute(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.MUTE, undoCallback);
     }
 
     /**
      * As above, for report spam
      */
     public int reportSpam(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.REPORT_SPAM);
+        return reportSpam(conversations, null);
+    }
+
+    public int reportSpam(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.REPORT_SPAM, undoCallback);
     }
 
     /**
      * As above, for report not spam
      */
     public int reportNotSpam(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.REPORT_NOT_SPAM);
+        return reportNotSpam(conversations, null);
+    }
+
+    public int reportNotSpam(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.REPORT_NOT_SPAM, undoCallback);
     }
 
     /**
      * As above, for report phishing
      */
     public int reportPhishing(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.REPORT_PHISHING);
+        return reportPhishing(conversations, null);
+    }
+
+    public int reportPhishing(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.REPORT_PHISHING, undoCallback);
     }
 
     /**
      * Discard the drafts in the specified conversations
      */
     public int discardDrafts(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.DISCARD_DRAFTS);
+        return discardDrafts(conversations, null);
+    }
+
+    public int discardDrafts(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.DISCARD_DRAFTS, undoCallback);
     }
 
     /**
      * As above, for mostly archive
      */
     public int mostlyArchive(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.MOSTLY_ARCHIVE);
+        return mostlyArchive(conversations, null);
+    }
+
+    public int mostlyArchive(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.MOSTLY_ARCHIVE, undoCallback);
     }
 
     /**
      * As above, for mostly delete
      */
     public int mostlyDelete(Collection<Conversation> conversations) {
-        return applyAction(conversations, ConversationOperation.MOSTLY_DELETE);
+        return mostlyDelete(conversations, null);
+    }
+
+    public int mostlyDelete(Collection<Conversation> conversations, UndoCallback undoCallback) {
+        return applyAction(conversations, ConversationOperation.MOSTLY_DELETE, undoCallback);
     }
 
     /**
@@ -2200,22 +2277,29 @@
      */
     public int mostlyDestructiveUpdate(Collection<Conversation> conversations,
             ContentValues values) {
+        return mostlyDestructiveUpdate(conversations, values, null);
+    }
+
+    public int mostlyDestructiveUpdate(Collection<Conversation> conversations,
+            ContentValues values, UndoCallback undoCallback) {
         return apply(
                 getOperationsForConversations(conversations,
-                        ConversationOperation.MOSTLY_DESTRUCTIVE_UPDATE, values));
+                        ConversationOperation.MOSTLY_DESTRUCTIVE_UPDATE, values, undoCallback));
     }
 
     /**
      * Convenience method for performing an operation on a group of conversations
      * @param conversations the conversations to be affected
      * @param opAction the action to take
+     * @param undoCallback the undo callback handler
      * @return the sequence number of the operation applied in CC
      */
-    private int applyAction(Collection<Conversation> conversations, int opAction) {
+    private int applyAction(Collection<Conversation> conversations, int opAction,
+            UndoCallback undoCallback) {
         ArrayList<ConversationOperation> ops = Lists.newArrayList();
         for (Conversation conv: conversations) {
             ConversationOperation op =
-                    new ConversationOperation(opAction, conv);
+                    new ConversationOperation(opAction, conv, undoCallback);
             ops.add(op);
         }
         return apply(ops);
@@ -2257,6 +2341,10 @@
         sb.append(mDeletedCount);
         sb.append(" mUnderlying=");
         sb.append(mUnderlyingCursor);
+        if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+            sb.append(" mCacheMap=");
+            sb.append(mCacheMap);
+        }
         sb.append("}");
         return sb.toString();
     }
@@ -2324,7 +2412,8 @@
                             undoConversations.add(conversation);
 
                             if (!mNotificationTempDeleted.contains(conversation)) {
-                                sProvider.deleteLocal(conversation.uri, ConversationCursor.this);
+                                sProvider.deleteLocal(conversation.uri, ConversationCursor.this,
+                                        null);
                                 mNotificationTempDeleted.add(conversation);
 
                                 changed = true;
diff --git a/src/com/android/mail/browse/ConversationItemView.java b/src/com/android/mail/browse/ConversationItemView.java
index e0613fa..a0bcf85 100644
--- a/src/com/android/mail/browse/ConversationItemView.java
+++ b/src/com/android/mail/browse/ConversationItemView.java
@@ -1785,7 +1785,7 @@
     public void dismiss() {
         SwipeableListView listView = getListView();
         if (listView != null) {
-            getListView().dismissChild(this);
+            listView.dismissChild(this);
         }
     }
 
diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java
index 08f5cf5..1cc729b 100644
--- a/src/com/android/mail/browse/ConversationViewAdapter.java
+++ b/src/com/android/mail/browse/ConversationViewAdapter.java
@@ -32,6 +32,7 @@
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
 import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
+import com.android.mail.browse.MessageFooterView.MessageFooterCallbacks;
 import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
 import com.android.mail.browse.SuperCollapsedBlock.OnClickListener;
 import com.android.mail.providers.Conversation;
@@ -64,6 +65,7 @@
     private final LoaderManager mLoaderManager;
     private final FragmentManager mFragmentManager;
     private final MessageHeaderViewCallbacks mMessageCallbacks;
+    private final MessageFooterCallbacks mFooterCallbacks;
     private final ContactInfoSource mContactInfoSource;
     private final ConversationViewHeaderCallbacks mConversationCallbacks;
     private final OnClickListener mSuperCollapsedListener;
@@ -266,15 +268,19 @@
         }
     }
 
-    public class MessageFooterItem extends ConversationOverlayItem {
+    public static class MessageFooterItem extends ConversationOverlayItem implements
+            AttachmentActionHandler.AboveAttachmentLayoutDismissedListener {
+        private final ConversationViewAdapter mAdapter;
+
         /**
          * A footer can only exist if there is a matching header. Requiring a header allows a
          * footer to stay in sync with the expanded state of the header.
          */
-        private final MessageHeaderItem mHeaderitem;
+        private final MessageHeaderItem mHeaderItem;
 
-        private MessageFooterItem(MessageHeaderItem item) {
-            mHeaderitem = item;
+        private MessageFooterItem(ConversationViewAdapter adapter, MessageHeaderItem item) {
+            mAdapter = adapter;
+            mHeaderItem = item;
         }
 
         @Override
@@ -286,14 +292,15 @@
         public View createView(Context context, LayoutInflater inflater, ViewGroup parent) {
             final MessageFooterView v = (MessageFooterView) inflater.inflate(
                     R.layout.conversation_message_footer, parent, false);
-            v.initialize(mLoaderManager, mFragmentManager, mAccountController);
+            v.initialize(mAdapter.mLoaderManager, mAdapter.mFragmentManager,
+                    mAdapter.mAccountController, mAdapter.mFooterCallbacks);
             return v;
         }
 
         @Override
         public void bindView(View v, boolean measureOnly) {
             final MessageFooterView attachmentsView = (MessageFooterView) v;
-            attachmentsView.bind(mHeaderitem, measureOnly);
+            attachmentsView.bind(mHeaderItem, this, measureOnly);
         }
 
         @Override
@@ -303,7 +310,7 @@
 
         @Override
         public boolean isExpanded() {
-            return mHeaderitem.isExpanded();
+            return mHeaderItem.isExpanded();
         }
 
         @Override
@@ -317,11 +324,32 @@
         public int getHeight() {
             // a footer may change height while its view does not exist because it is offscreen
             // (but the header is onscreen and thus collapsible)
-            if (!mHeaderitem.isExpanded()) {
+            if (!mHeaderItem.isExpanded()) {
                 return 0;
             }
             return super.getHeight();
         }
+
+        public MessageHeaderItem getHeaderItem() {
+            return mHeaderItem;
+        }
+
+        @Override
+        public void onOtherLayoutDismissed() {
+            final MessageFooterView view = mAdapter.mFooterCallbacks.getViewForItem(this);
+
+            // the item has a view, use the normal path
+            if (view != null) {
+                view.collapseAboveBarAttachmentsView();
+                return;
+            }
+
+            // the item is offscreen or otherwise doesn't have a view
+            // just update the HTML
+            final int newHeight = mAdapter.mFooterCallbacks.getUpdatedHeight(this);
+            setHeight(newHeight);
+            mAdapter.mFooterCallbacks.setMessageSpacerHeight(this, newHeight);
+        }
     }
 
     public class SuperCollapsedBlockItem extends ConversationOverlayItem {
@@ -453,6 +481,7 @@
             ConversationAccountController accountController,
             LoaderManager loaderManager,
             MessageHeaderViewCallbacks messageCallbacks,
+            MessageFooterCallbacks footerCallbacks,
             ContactInfoSource contactInfoSource,
             ConversationViewHeaderCallbacks convCallbacks,
             SuperCollapsedBlock.OnClickListener scbListener, Map<String, Address> addressCache,
@@ -463,6 +492,7 @@
         mLoaderManager = loaderManager;
         mFragmentManager = controllableActivity.getFragmentManager();
         mMessageCallbacks = messageCallbacks;
+        mFooterCallbacks = footerCallbacks;
         mContactInfoSource = contactInfoSource;
         mConversationCallbacks = convCallbacks;
         mSuperCollapsedListener = scbListener;
@@ -548,7 +578,7 @@
     }
 
     public int addMessageFooter(MessageHeaderItem headerItem) {
-        return addItem(new MessageFooterItem(headerItem));
+        return addItem(new MessageFooterItem(this, headerItem));
     }
 
     public static MessageHeaderItem newMessageHeaderItem(ConversationViewAdapter adapter,
@@ -557,8 +587,9 @@
         return new MessageHeaderItem(adapter, dateBuilder, message, expanded, showImages);
     }
 
-    public MessageFooterItem newMessageFooterItem(MessageHeaderItem headerItem) {
-        return new MessageFooterItem(headerItem);
+    public static MessageFooterItem newMessageFooterItem(
+            ConversationViewAdapter adapter, MessageHeaderItem headerItem) {
+        return new MessageFooterItem(adapter, headerItem);
     }
 
     public int addSuperCollapsedBlock(int start, int end) {
diff --git a/src/com/android/mail/browse/EmlMessageViewFragment.java b/src/com/android/mail/browse/EmlMessageViewFragment.java
index 8da2606..3ab9885 100644
--- a/src/com/android/mail/browse/EmlMessageViewFragment.java
+++ b/src/com/android/mail/browse/EmlMessageViewFragment.java
@@ -33,6 +33,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 import android.widget.Toast;
 
@@ -99,6 +100,18 @@
         }
 
         @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+            // try to load the url assuming it is a cid url
+            final Uri uri = Uri.parse(url);
+            final WebResourceResponse response = loadCIDUri(uri, mViewController.getMessage());
+            if (response != null) {
+                return response;
+            }
+
+            return super.shouldInterceptRequest(view, url);
+        }
+
+        @Override
         public void onPageFinished(WebView view, String url) {
             // Ignore unsafe calls made after a fragment is detached from an activity.
             // This method needs to, for example, get at the loader manager, which needs
@@ -174,6 +187,12 @@
     }
 
     @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mViewController.onDestroyView();
+    }
+
+    @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         if (Utils.isRunningKitkatOrLater()) {
             inflater.inflate(R.menu.eml_fragment_menu, menu);
diff --git a/src/com/android/mail/browse/EmlViewerActivity.java b/src/com/android/mail/browse/EmlViewerActivity.java
index 1812f13..ab3fd60 100644
--- a/src/com/android/mail/browse/EmlViewerActivity.java
+++ b/src/com/android/mail/browse/EmlViewerActivity.java
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 
 import com.android.mail.R;
+import com.android.mail.analytics.Analytics;
 import com.android.mail.ui.AccountFeedbackActivity;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
@@ -47,6 +48,7 @@
                 transaction.add(R.id.root, EmlMessageViewFragment.newInstance(
                         intent.getData(), mAccountUri), FRAGMENT_TAG);
                 transaction.commit();
+                Analytics.getInstance().sendEvent("eml_viewer", null, null, 0);
             } else {
                 LogUtils.wtf(LOG_TAG,
                         "Entered EmlViewerActivity with wrong intent action or type: %s, %s",
diff --git a/src/com/android/mail/browse/FullMessageContract.java b/src/com/android/mail/browse/FullMessageContract.java
index 2f5f983..20bceac 100644
--- a/src/com/android/mail/browse/FullMessageContract.java
+++ b/src/com/android/mail/browse/FullMessageContract.java
@@ -33,4 +33,8 @@
      * String extra for the account to which the message belongs.
      */
     public static final String EXTRA_ACCOUNT_NAME = "account-name";
+    /**
+     * (Optional) String extra for the server message id of the message.
+     */
+    public static final String EXTRA_SERVER_MESSAGE_ID = "server-message-id";
 }
diff --git a/src/com/android/mail/browse/InlineAttachmentViewIntentBuilder.java b/src/com/android/mail/browse/InlineAttachmentViewIntentBuilder.java
index ac169b9..1722239 100644
--- a/src/com/android/mail/browse/InlineAttachmentViewIntentBuilder.java
+++ b/src/com/android/mail/browse/InlineAttachmentViewIntentBuilder.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.content.Intent;
 
-import com.android.mail.providers.Message;
-
 /**
  * Builds an intent to be used when the user long presses an
  * inline image and selects "View image".
@@ -39,5 +37,6 @@
      * inline image and selects "View Image" or {@code null} if there should not
      * be a "View image" option for this url.
      */
-    Intent createInlineAttachmentViewIntent(Context context, String url, Message message);
+    Intent createInlineAttachmentViewIntent(
+            Context context, String url, ConversationMessage message);
 }
diff --git a/src/com/android/mail/browse/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
index 93e7ca3..bb5e1e9 100644
--- a/src/com/android/mail/browse/MessageAttachmentBar.java
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -83,6 +83,13 @@
 
     private static final String LOG_TAG = LogTag.getLogTag();
 
+    /**
+     * Boolean used to tell whether extra option 1 should always be hidden.
+     * Currently makes sure that there is no conversation because that state
+     * means that we're in the EML viewer.
+     */
+    private boolean mHideExtraOptionOne;
+
 
     public MessageAttachmentBar(Context context) {
         this(context, null);
@@ -116,8 +123,12 @@
 
         final Attachment prevAttachment = mAttachment;
         mAttachment = attachment;
+        if (mAccount != null) {
+            mActionHandler.setAccount(mAccount.getEmailAddress());
+        }
         mActionHandler.setMessage(message);
         mActionHandler.setAttachment(mAttachment);
+        mHideExtraOptionOne = message.getConversation() == null;
 
         // reset mSaveClicked if we are not currently downloading
         // So if the download fails or the download completes, we stop
@@ -224,6 +235,8 @@
                 menu.findItem(R.id.attachment_extra_option1).setVisible(shouldShowExtraOption1());
 
                 mPopup.show();
+
+                AttachmentActionHandler.onOverflowOpened(getContext());
             }
         } else {
             // Handles clicking the attachment
@@ -301,7 +314,7 @@
     }
 
     private boolean shouldShowExtraOption1() {
-        return mActionHandler.shouldShowExtraOption1();
+        return !mHideExtraOptionOne && mActionHandler.shouldShowExtraOption1();
     }
 
     private boolean shouldShowOverflow() {
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
index 9c081d0..5fe7439 100644
--- a/src/com/android/mail/browse/MessageFooterView.java
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -30,12 +30,15 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.mail.R;
+import com.android.mail.analytics.Analytics;
 import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
 import com.android.mail.browse.ConversationContainer.DetachListener;
+import com.android.mail.browse.ConversationViewAdapter.MessageFooterItem;
 import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Attachment;
@@ -46,6 +49,7 @@
 import com.android.mail.ui.AttachmentTileGrid;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
 
@@ -55,6 +59,7 @@
 public class MessageFooterView extends LinearLayout implements DetachListener,
         LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
 
+    private MessageFooterItem mMessageFooterItem;
     private MessageHeaderItem mMessageHeaderItem;
     private LoaderManager mLoaderManager;
     private FragmentManager mFragmentManager;
@@ -63,6 +68,7 @@
     private TextView mTitleText;
     private AttachmentTileGrid mAttachmentGrid;
     private LinearLayout mAttachmentBarList;
+    private View mAboveAttachmentBarListLayout;
 
     private final LayoutInflater mInflater;
 
@@ -72,6 +78,27 @@
 
     private BidiFormatter mBidiFormatter;
 
+    private MessageFooterCallbacks mCallbacks;
+
+    private Integer mOldAttachmentLoaderId;
+
+    /**
+     * Callbacks for the MessageFooterView to enable resizing the height.
+     */
+    public interface MessageFooterCallbacks {
+        void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight);
+
+        MessageFooterView getViewForItem(MessageFooterItem item);
+
+        int getUpdatedHeight(MessageFooterItem item);
+
+        /**
+         * @return <tt>true</tt> if this footer is contained within a SecureConversationViewFragment
+         * and cannot assume the content is <strong>not</strong> malicious
+         */
+        boolean isSecure();
+    }
+
     public MessageFooterView(Context context) {
         this(context, null);
     }
@@ -90,47 +117,44 @@
         mTitleText = (TextView) findViewById(R.id.attachments_header_text);
         mAttachmentGrid = (AttachmentTileGrid) findViewById(R.id.attachment_tile_grid);
         mAttachmentBarList = (LinearLayout) findViewById(R.id.attachment_bar_list);
+        mAboveAttachmentBarListLayout = findViewById(R.id.above_attachment_bar_list_layout);
 
         mViewEntireMessagePrompt.setOnClickListener(this);
     }
 
     public void initialize(LoaderManager loaderManager, FragmentManager fragmentManager,
-            ConversationAccountController accountController) {
+            ConversationAccountController accountController, MessageFooterCallbacks callbacks) {
         mLoaderManager = loaderManager;
         mFragmentManager = fragmentManager;
         mAccountController = accountController;
+        mCallbacks = callbacks;
     }
 
-    public void bind(MessageHeaderItem headerItem, boolean measureOnly) {
-        // Resets the footer view. This step is only done if the
-        // attachmentsListUri changes so that we don't
-        // repeat the work of layout and measure when
-        // we're only updating the attachments.
-        if (mMessageHeaderItem != null &&
-                mMessageHeaderItem.getMessage() != null &&
-                mMessageHeaderItem.getMessage().attachmentListUri != null &&
-                !mMessageHeaderItem.getMessage().attachmentListUri.equals(
-                headerItem.getMessage().attachmentListUri)) {
+    public void bind(
+            MessageHeaderItem headerItem, MessageFooterItem footerItem, boolean measureOnly) {
+        mMessageFooterItem = footerItem;
+        mMessageHeaderItem = headerItem;
+
+        final Integer attachmentLoaderId = getAttachmentLoaderId();
+
+        // Destroy the loader if we are attempting to load a different attachment
+        if (mOldAttachmentLoaderId != null &&
+                !Objects.equal(mOldAttachmentLoaderId, attachmentLoaderId)) {
+            mLoaderManager.destroyLoader(mOldAttachmentLoaderId);
+
+            // Resets the footer view. This step is only done if the
+            // attachmentsListUri changes so that we don't
+            // repeat the work of layout and measure when
+            // we're only updating the attachments.
             mAttachmentGrid.removeAllViewsInLayout();
             mAttachmentBarList.removeAllViewsInLayout();
             mViewEntireMessagePrompt.setVisibility(View.GONE);
             mTitleText.setVisibility(View.GONE);
             mAttachmentGrid.setVisibility(View.GONE);
             mAttachmentBarList.setVisibility(View.GONE);
+            hideAboveAttachmentBarListLayout();
         }
-
-        // If this MessageFooterView is being bound to a new attachment, we need to unbind with the
-        // old loader
-        final Integer oldAttachmentLoaderId = getAttachmentLoaderId();
-
-        mMessageHeaderItem = headerItem;
-
-        final Integer attachmentLoaderId = getAttachmentLoaderId();
-        // Destroy the loader if we are attempting to load a different attachment
-        if (oldAttachmentLoaderId != null &&
-                !Objects.equal(oldAttachmentLoaderId, attachmentLoaderId)) {
-            mLoaderManager.destroyLoader(oldAttachmentLoaderId);
-        }
+        mOldAttachmentLoaderId = attachmentLoaderId;
 
         // kick off load of Attachment objects in background thread
         // but don't do any Loader work if we're only measuring
@@ -146,11 +170,31 @@
             renderAttachments(false);
         }
 
+        final ConversationMessage message = mMessageHeaderItem.getMessage();
         mViewEntireMessagePrompt.setVisibility(
-                mMessageHeaderItem.getMessage().clipped ? VISIBLE : GONE);
+                message.clipped && !TextUtils.isEmpty(message.permalink) ? VISIBLE : GONE);
         setVisibility(mMessageHeaderItem.isExpanded() ? VISIBLE : GONE);
     }
 
+    private void hideAboveAttachmentBarListLayout() {
+        if (mAboveAttachmentBarListLayout != null) {
+            mAboveAttachmentBarListLayout.setVisibility(GONE);
+        }
+    }
+
+    private void showAboveAttachmentBarListLayout() {
+        if (mAboveAttachmentBarListLayout != null) {
+            final Conversation conversation = mMessageHeaderItem.getMessage().getConversation();
+            if (conversation == null) {
+                hideAboveAttachmentBarListLayout();
+                return;
+            }
+            AttachmentActionHandler.registerDismissListener(conversation.uri, mMessageFooterItem);
+            mAboveAttachmentBarListLayout.setVisibility(VISIBLE);
+            AttachmentActionHandler.setupAboveBarAttachmentLayout(mAboveAttachmentBarListLayout);
+        }
+    }
+
     private void renderAttachments(boolean loaderResult) {
         final List<Attachment> attachments;
         if (mAttachmentsCursor != null && !mAttachmentsCursor.isClosed()) {
@@ -178,13 +222,15 @@
         final List<Attachment> barAttachments = new ArrayList<Attachment>(maxSize);
 
         for (Attachment attachment : attachments) {
-            if (attachment.isInlineAttachment()) {
-                // skip non-standard (aka inline) attachments
-                continue;
-            } else if (AttachmentTile.isTiledAttachment(attachment)) {
-                tiledAttachments.add(attachment);
-            } else {
-                barAttachments.add(attachment);
+            // attachments in secure views are displayed in the footer so the user may interact with
+            // them; for normal views there is no need to show inline attachments in the footer
+            // since users can interact with them in place
+            if (!attachment.isInlineAttachment() || mCallbacks.isSecure()) {
+                if (AttachmentTile.isTiledAttachment(attachment)) {
+                    tiledAttachments.add(attachment);
+                } else {
+                    barAttachments.add(attachment);
+                }
             }
         }
 
@@ -197,8 +243,12 @@
 
         mTitleText.setVisibility(View.VISIBLE);
 
-        renderTiledAttachments(tiledAttachments, loaderResult);
-        renderBarAttachments(barAttachments, loaderResult);
+        if (!tiledAttachments.isEmpty()) {
+            renderTiledAttachments(tiledAttachments, loaderResult);
+        }
+        if (!barAttachments.isEmpty()) {
+            renderBarAttachments(barAttachments, loaderResult);
+        }
     }
 
     private void renderTiledAttachments(List<Attachment> tiledAttachments, boolean loaderResult) {
@@ -212,6 +262,13 @@
     private void renderBarAttachments(List<Attachment> barAttachments, boolean loaderResult) {
         mAttachmentBarList.setVisibility(View.VISIBLE);
 
+        if (!barAttachments.isEmpty() &&
+                AttachmentActionHandler.shouldShowAboveBarAttachmentLayout(getContext())) {
+            showAboveAttachmentBarListLayout();
+        } else {
+            hideAboveAttachmentBarListLayout();
+        }
+
         final Account account = getAccount();
         for (Attachment attachment : barAttachments) {
             final Uri id = attachment.getIdentifierUri();
@@ -241,7 +298,7 @@
 
     @Override
     public void onDetachedFromParent() {
-        // Do nothing
+        // Do nothing.
     }
 
     @Override
@@ -285,6 +342,8 @@
     }
 
     private void viewEntireMessage() {
+        Analytics.getInstance().sendEvent("view_entire_message", "clicked", null, 0);
+
         final Context context = getContext();
         final Intent intent = new Intent();
         final String activityName =
@@ -295,11 +354,12 @@
         }
         intent.setClassName(context, activityName);
         final Account account = getAccount();
-        if (account != null) {
-            final Conversation conv = mMessageHeaderItem.getMessage().getConversation();
+        final ConversationMessage message = mMessageHeaderItem.getMessage();
+        if (account != null && !TextUtils.isEmpty(message.permalink)) {
             intent.putExtra(AccountFeedbackActivity.EXTRA_ACCOUNT_URI, account.uri);
-            intent.putExtra(FullMessageContract.EXTRA_PERMALINK, conv.permalink);
+            intent.putExtra(FullMessageContract.EXTRA_PERMALINK, message.permalink);
             intent.putExtra(FullMessageContract.EXTRA_ACCOUNT_NAME, account.getEmailAddress());
+            intent.putExtra(FullMessageContract.EXTRA_SERVER_MESSAGE_ID, message.serverId);
             context.startActivity(intent);
         }
     }
@@ -307,4 +367,28 @@
     private Account getAccount() {
         return mAccountController != null ? mAccountController.getAccount() : null;
     }
+
+    public void collapseAboveBarAttachmentsView() {
+        measureHeight();
+        mAboveAttachmentBarListLayout.setVisibility(View.GONE);
+        updateSpacerHeight();
+    }
+
+    private int measureHeight() {
+        ViewGroup parent = (ViewGroup) getParent();
+        if (parent == null) {
+            LogUtils.e(LOG_TAG, new Error(), "Unable to measure height of detached header");
+            return getHeight();
+        }
+        return Utils.measureViewHeight(this, parent);
+    }
+
+    private void updateSpacerHeight() {
+        final int h = measureHeight();
+
+        mMessageFooterItem.setHeight(h);
+        if (mCallbacks != null) {
+            mCallbacks.setMessageSpacerHeight(mMessageFooterItem, h);
+        }
+    }
 }
diff --git a/src/com/android/mail/browse/MessageHeaderContactBadge.java b/src/com/android/mail/browse/MessageHeaderContactBadge.java
new file mode 100644
index 0000000..1bf745d
--- /dev/null
+++ b/src/com/android/mail/browse/MessageHeaderContactBadge.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed 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 com.android.mail.browse;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.QuickContactBadge;
+
+import com.android.mail.analytics.Analytics;
+import com.android.mail.utils.LogUtils;
+
+public class MessageHeaderContactBadge extends QuickContactBadge {
+    public MessageHeaderContactBadge(Context context) {
+        super(context);
+    }
+
+    public MessageHeaderContactBadge(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public MessageHeaderContactBadge(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public void onClick(View v) {
+        Analytics.getInstance().sendEvent("quick_contact", "clicked", null, 0);
+        super.onClick(v);
+    }
+}
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index 5eb382f..7330338 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -84,15 +84,10 @@
     private static final int SHOW_IMAGE_PROMPT_ONCE = 1;
     private static final int SHOW_IMAGE_PROMPT_ALWAYS = 2;
 
-    private static final String HEADER_INFLATE_TAG = "message header inflate";
-    private static final String HEADER_ADDVIEW_TAG = "message header addView";
     private static final String HEADER_RENDER_TAG = "message header render";
-    private static final String PREMEASURE_TAG = "message header pre-measure";
     private static final String LAYOUT_TAG = "message header layout";
     private static final String MEASURE_TAG = "message header measure";
 
-    private static final String RECIPIENT_HEADING_DELIMITER = "   ";
-
     private static final String LOG_TAG = LogTag.getLogTag();
 
     // This is a debug only feature
@@ -230,6 +225,12 @@
         String getMessageTransforms(Message msg);
 
         FragmentManager getFragmentManager();
+
+        /**
+         * @return <tt>true</tt> if this header is contained within a SecureConversationViewFragment
+         * and cannot assume the content is <strong>not</strong> malicious
+         */
+        boolean isSecure();
     }
 
     public MessageHeaderView(Context context) {
@@ -419,9 +420,21 @@
         mMessage = mMessageHeaderItem.getMessage();
 
         final Account account = getAccount();
-        final boolean alwaysShowImages = (account != null) &&
+        final boolean alwaysShowImagesForAccount = (account != null) &&
                 (account.settings.showImages == Settings.ShowImages.ALWAYS);
-        mShowImagePrompt = mMessage.shouldShowImagePrompt() && !alwaysShowImages;
+
+        final boolean alwaysShowImagesForMessage = mMessage.shouldShowImagePrompt();
+
+        if (!alwaysShowImagesForMessage) {
+            // we don't need the "Show picture" prompt if the user allows images for this message
+            mShowImagePrompt = false;
+        } else if (mCallbacks.isSecure()) {
+            // in a secure view we always display the "Show picture" prompt
+            mShowImagePrompt = true;
+        } else {
+            // otherwise honor the account setting for automatically showing pictures
+            mShowImagePrompt = !alwaysShowImagesForAccount;
+        }
 
         setExpanded(mMessageHeaderItem.isExpanded());
 
@@ -563,13 +576,6 @@
         return TextUtils.isEmpty(displayName) ? sender.getAddress() : displayName;
     }
 
-    /**
-     * Return the address, if a name is present, or null if not.
-     */
-    private static CharSequence getSenderAddress(Address sender) {
-        return (sender != null) ? sender.getAddress() : "";
-    }
-
     private static void setChildVisibility(int visibility, View... children) {
         for (View v : children) {
             if (v != null) {
@@ -854,7 +860,9 @@
         boolean photoSet = false;
         final String email = mSender.getAddress();
         final ContactInfo info = mContactInfoSource.getContactInfo(email);
-        if (info != null) {
+        // Need to also check if we have a contactUri because we put empty
+        // results in the ContactInfoSource if no result was found but a query happened.
+        if (info != null && info.contactUri != null) {
             mPhotoView.assignContactUri(info.contactUri);
             if (info.photo != null) {
                 mPhotoView.setImageBitmap(info.photo);
@@ -1081,7 +1089,7 @@
         public int bottomHeight = -1;
     }
 
-    private boolean isValidPosition(int position, int size) {
+    private static boolean isValidPosition(int position, int size) {
         return position >= 0 && position < size;
     }
 
@@ -1098,11 +1106,15 @@
     private void toggleMessageDetails() {
         int heightBefore = measureHeight();
         final boolean detailsExpanded = !mMessageHeaderItem.detailsExpanded;
+        Analytics.getInstance().sendEvent(
+                "message_header", "toggle_details", detailsExpanded ? "expand" : "collapse", 0);
         setMessageDetailsExpanded(detailsExpanded);
         updateSpacerHeight();
         if (mCallbacks != null) {
             mCallbacks.setMessageDetailsExpanded(mMessageHeaderItem, detailsExpanded, heightBefore);
         }
+
+        requestLayout();
     }
 
     private void setMessageDetailsExpanded(boolean expand) {
@@ -1423,7 +1435,7 @@
             } else {
                 addressShown = address;
             }
-            if (name == null || name.length() == 0) {
+            if (name == null || name.length() == 0 || name.equalsIgnoreCase(addressShown)) {
                 formattedEmails[i] = bidiFormatter.unicodeWrap(addressShown);
             } else {
                 // The one downside to having the showViaDomain here is that
diff --git a/src/com/android/mail/browse/MessageView.java b/src/com/android/mail/browse/MessageView.java
deleted file mode 100644
index 546a453..0000000
--- a/src/com/android/mail/browse/MessageView.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.android.mail.browse;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.webkit.WebSettings;
-
-/**
- * For zooming within a single message.
- */
-public class MessageView extends MailWebView {
-
-    public MessageView(Context c) {
-        this(c, null);
-    }
-
-    public MessageView(Context c, AttributeSet attrs) {
-        super(c, attrs);
-
-        final WebSettings settings = getSettings();
-        settings.setUseWideViewPort(true);
-        settings.setLoadWithOverviewMode(true);
-        settings.setSupportZoom(true);
-        settings.setBuiltInZoomControls(true);
-        settings.setDisplayZoomControls(false);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        requestDisallowInterceptTouchEvent(true);
-        return super.onTouchEvent(event);
-    }
-
-}
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 99e3932..1751ff7 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -122,31 +122,35 @@
         Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, itemId,
                 "cab_mode", 0);
 
+        UndoCallback undoCallback = null;   // not applicable here (yet)
         if (itemId == R.id.delete) {
             LogUtils.i(LOG_TAG, "Delete selected from CAB menu");
-            performDestructiveAction(R.id.delete);
+            performDestructiveAction(R.id.delete, undoCallback);
         } else if (itemId == R.id.discard_drafts) {
-            performDestructiveAction(R.id.discard_drafts);
+            performDestructiveAction(R.id.discard_drafts, undoCallback);
         } else if (itemId == R.id.archive) {
             LogUtils.i(LOG_TAG, "Archive selected from CAB menu");
-            performDestructiveAction(R.id.archive);
+            performDestructiveAction(R.id.archive, undoCallback);
         } else if (itemId == R.id.remove_folder) {
             destroy(R.id.remove_folder, mSelectionSet.values(),
                     mUpdater.getDeferredRemoveFolder(mSelectionSet.values(), mFolder, true,
-                            true, true));
+                            true, true, undoCallback));
         } else if (itemId == R.id.mute) {
-            destroy(R.id.mute, mSelectionSet.values(), mUpdater.getBatchAction(R.id.mute));
+            destroy(R.id.mute, mSelectionSet.values(), mUpdater.getBatchAction(R.id.mute,
+                    undoCallback));
         } else if (itemId == R.id.report_spam) {
             destroy(R.id.report_spam, mSelectionSet.values(),
-                    mUpdater.getBatchAction(R.id.report_spam));
+                    mUpdater.getBatchAction(R.id.report_spam, undoCallback));
         } else if (itemId == R.id.mark_not_spam) {
             // Currently, since spam messages are only shown in list with other spam messages,
             // marking a message not as spam is a destructive action
             destroy (R.id.mark_not_spam,
-                    mSelectionSet.values(), mUpdater.getBatchAction(R.id.mark_not_spam)) ;
+                    mSelectionSet.values(), mUpdater.getBatchAction(R.id.mark_not_spam,
+                            undoCallback)) ;
         } else if (itemId == R.id.report_phishing) {
             destroy(R.id.report_phishing,
-                    mSelectionSet.values(), mUpdater.getBatchAction(R.id.report_phishing));
+                    mSelectionSet.values(), mUpdater.getBatchAction(R.id.report_phishing,
+                            undoCallback));
         } else if (itemId == R.id.read) {
             markConversationsRead(true);
         } else if (itemId == R.id.unread) {
@@ -156,7 +160,7 @@
         } else if (itemId == R.id.remove_star) {
             if (mFolder.isType(UIProvider.FolderType.STARRED)) {
                 LogUtils.d(LOG_TAG, "We are in a starred folder, removing the star");
-                performDestructiveAction(R.id.remove_star);
+                performDestructiveAction(R.id.remove_star, undoCallback);
             } else {
                 LogUtils.d(LOG_TAG, "Not in a starred folder.");
                 starConversations(false);
@@ -213,7 +217,7 @@
             markConversationsImportant(true);
         } else if (itemId == R.id.mark_not_important) {
             if (mFolder.supportsCapability(UIProvider.FolderCapabilities.ONLY_IMPORTANT)) {
-                performDestructiveAction(R.id.mark_not_important);
+                performDestructiveAction(R.id.mark_not_important, undoCallback);
             } else {
                 markConversationsImportant(false);
             }
@@ -244,7 +248,7 @@
         }
     }
 
-    private void performDestructiveAction(final int action) {
+    private void performDestructiveAction(final int action, UndoCallback undoCallback) {
         final Collection<Conversation> conversations = mSelectionSet.values();
         final Settings settings = mAccount.settings;
         final boolean showDialog;
@@ -258,7 +262,7 @@
             showDialog = false;
         }
         if (showDialog) {
-            mUpdater.makeDialogListener(action, true /* fromSelectedSet */);
+            mUpdater.makeDialogListener(action, true /* fromSelectedSet */, null /* undoCallback */);
             final int resId;
             if (action == R.id.delete) {
                 resId = R.plurals.confirm_delete_conversation;
@@ -274,7 +278,7 @@
             // No need to show the dialog, just make a destructive action and destroy the
             // selected set immediately.
             // TODO(viki): Stop using the deferred action here. Use the registered action.
-            destroy(action, conversations, mUpdater.getDeferredBatchAction(action));
+            destroy(action, conversations, mUpdater.getDeferredBatchAction(action, undoCallback));
         }
     }
 
diff --git a/src/com/android/bitmap/Poolable.java b/src/com/android/mail/browse/UndoCallback.java
similarity index 66%
copy from src/com/android/bitmap/Poolable.java
copy to src/com/android/mail/browse/UndoCallback.java
index c68a25c..729cf9b 100644
--- a/src/com/android/bitmap/Poolable.java
+++ b/src/com/android/mail/browse/UndoCallback.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2014 Google Inc.
+ * Licensed to The Android Open Source Project.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +15,11 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.mail.browse;
 
-public interface Poolable {
-    void acquireReference();
-    void releaseReference();
-    int getRefCount();
-    boolean isEligibleForPooling();
-}
+/**
+ * Specifies that further actions need to be taken after a specific undo is performed.
+ */
+public interface UndoCallback {
+    public void performUndoCallback();
+}
\ No newline at end of file
diff --git a/src/com/android/mail/browse/UrlSpan.java b/src/com/android/mail/browse/UrlSpan.java
new file mode 100644
index 0000000..9e3d172
--- /dev/null
+++ b/src/com/android/mail/browse/UrlSpan.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed 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 com.android.mail.browse;
+
+import android.text.TextPaint;
+import android.text.style.URLSpan;
+
+/**
+ * Extension to URLSpan that does not have underlines.
+ * Stupid URLSpan.<p/>
+ *
+ * WARNING: this span will not work if the TextView it uses
+ * saves and restores its text since TextView can only save
+ * and restore {@link android.text.ParcelableSpan}s which
+ * can only be implemented by framework Spans.
+ */
+public class UrlSpan extends URLSpan {
+    public UrlSpan(String url) {
+        super(url);
+    }
+
+    /**
+     * Makes the text in the link color and not underlined.
+     */
+    @Override
+    public void updateDrawState(TextPaint ds) {
+        ds.setColor(ds.linkColor);
+        ds.setUnderlineText(false);
+    }
+}
diff --git a/src/com/android/mail/browse/WebViewContextMenu.java b/src/com/android/mail/browse/WebViewContextMenu.java
index ac37bcb..b81a3be 100644
--- a/src/com/android/mail/browse/WebViewContextMenu.java
+++ b/src/com/android/mail/browse/WebViewContextMenu.java
@@ -35,6 +35,7 @@
 import android.webkit.WebView;
 
 import com.android.mail.R;
+import com.android.mail.analytics.Analytics;
 import com.android.mail.providers.Message;
 
 import java.io.UnsupportedEncodingException;
@@ -62,6 +63,11 @@
 
     private Callbacks mCallbacks;
 
+    // Strings used for analytics.
+    private static final String CATEGORY_WEB_CONTEXT_MENU = "web_context_menu";
+    private static final String ACTION_LONG_PRESS = "long_press";
+    private static final String ACTION_CLICK = "menu_clicked";
+
     protected static enum MenuType {
         OPEN_MENU,
         COPY_LINK_MENU,
@@ -84,7 +90,7 @@
          * @param url URL of a selected link
          * @return Message containing that URL
          */
-        Message getMessageForClickedUrl(String url);
+        ConversationMessage getMessageForClickedUrl(String url);
     }
 
     public WebViewContextMenu(Activity host, InlineAttachmentViewIntentBuilder builder) {
@@ -107,33 +113,84 @@
         mCallbacks = cb;
     }
 
+    /**
+     * Abstract base class that automates sending an analytics event
+     * when the menu item is clicked.
+     */
+    private abstract class AnalyticsClick implements MenuItem.OnMenuItemClickListener {
+        private final String mAnalyticsLabel;
+
+        public AnalyticsClick(String analyticsLabel) {
+            mAnalyticsLabel = analyticsLabel;
+        }
+
+        @Override
+        public final boolean onMenuItemClick(MenuItem item) {
+            Analytics.getInstance().sendEvent(
+                    CATEGORY_WEB_CONTEXT_MENU, ACTION_CLICK, mAnalyticsLabel, 0);
+            return onClick();
+        }
+
+        public abstract boolean onClick();
+    }
+
     // For our copy menu items.
-    private class Copy implements MenuItem.OnMenuItemClickListener {
+    private class Copy extends AnalyticsClick {
         private final CharSequence mText;
 
-        public Copy(CharSequence text) {
+        public Copy(CharSequence text, String analyticsLabel) {
+            super(analyticsLabel);
             mText = text;
         }
 
         @Override
-        public boolean onMenuItemClick(MenuItem item) {
-            copy(mText);
+        public boolean onClick() {
+            ClipboardManager clipboard =
+                    (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
+            clipboard.setPrimaryClip(ClipData.newPlainText(null, mText));
+            return true;
+        }
+    }
+
+    /**
+     * Sends an intent and reports the analytics event.
+     */
+    private class SendIntent extends AnalyticsClick {
+        private Intent mIntent;
+
+        public SendIntent(String analyticsLabel) {
+            super(analyticsLabel);
+        }
+
+        public SendIntent(Intent intent, String analyticsLabel) {
+            super(analyticsLabel);
+            setIntent(intent);
+        }
+
+        void setIntent(Intent intent) {
+            mIntent = intent;
+        }
+
+        @Override
+        public final boolean onClick() {
+            try {
+                mActivity.startActivity(mIntent);
+            } catch(android.content.ActivityNotFoundException ex) {
+                // if no app handles it, do nothing
+            }
             return true;
         }
     }
 
     // For our share menu items.
-    private class Share implements MenuItem.OnMenuItemClickListener {
-        private final String mUri;
-
-        public Share(String text) {
-            mUri = text;
-        }
-
-        @Override
-        public boolean onMenuItemClick(MenuItem item) {
-            shareLink(mUri);
-            return true;
+    private class Share extends SendIntent {
+        public Share(String url, String analyticsLabel) {
+            super(analyticsLabel);
+            final Intent send = new Intent(Intent.ACTION_SEND);
+            send.setType("text/plain");
+            send.putExtra(Intent.EXTRA_TEXT, url);
+            setIntent(Intent.createChooser(send, mActivity.getText(
+                    getChooserTitleStringResIdForMenuType(MenuType.SHARE_LINK_MENU))));
         }
     }
 
@@ -145,25 +202,6 @@
         return ri != null;
     }
 
-    private void shareLink(String url) {
-        Intent send = new Intent(Intent.ACTION_SEND);
-        send.setType("text/plain");
-        send.putExtra(Intent.EXTRA_TEXT, url);
-
-        try {
-            mActivity.startActivity(Intent.createChooser(send, mActivity.getText(
-                    getChooserTitleStringResIdForMenuType(MenuType.SHARE_LINK_MENU))));
-        } catch(android.content.ActivityNotFoundException ex) {
-            // if no app handles it, do nothing
-        }
-    }
-
-    private void copy(CharSequence text) {
-        ClipboardManager clipboard =
-                (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
-        clipboard.setPrimaryClip(ClipData.newPlainText(null, text));
-    }
-
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) {
         // FIXME: This is copied over almost directly from BrowserActivity.
@@ -178,7 +216,12 @@
         int type = result.getType();
         switch (type) {
             case WebView.HitTestResult.UNKNOWN_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "unknown", 0);
+                return;
             case WebView.HitTestResult.EDIT_TEXT_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "edit_text", 0);
                 return;
             default:
                 break;
@@ -213,11 +256,22 @@
         // Setup custom handling depending on the type
         switch (type) {
             case WebView.HitTestResult.PHONE_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "phone", 0);
                 String decodedPhoneExtra;
                 try {
                     decodedPhoneExtra = URLDecoder.decode(extra, Charset.defaultCharset().name());
-                }
-                catch (UnsupportedEncodingException ignore) {
+
+                    // International numbers start with '+' followed by the country code, etc.
+                    // However, during decode, the initial '+' is changed into ' '.
+                    // Let's special case that here to avoid losing that information. If the decoded
+                    // string starts with one space, let's replace that space with + since it's
+                    // impossible for the normal number string to start with a space.
+                    // b/10640197
+                    if (decodedPhoneExtra.startsWith(" ") && !decodedPhoneExtra.startsWith("  ")) {
+                        decodedPhoneExtra = decodedPhoneExtra.replaceFirst(" ", "+");
+                    }
+                } catch (UnsupportedEncodingException ignore) {
                     // Should never happen; default charset is UTF-8
                     decodedPhoneExtra = extra;
                 }
@@ -228,10 +282,9 @@
                         menu.findItem(getMenuResIdForMenuType(MenuType.DIAL_MENU));
 
                 if (mSupportsDial) {
-                    // remove the on click listener
-                    dialMenuItem.setOnMenuItemClickListener(null);
-                    dialMenuItem.setIntent(new Intent(Intent.ACTION_DIAL,
-                            Uri.parse(WebView.SCHEME_TEL + extra)));
+                    final Intent intent = new Intent(Intent.ACTION_DIAL,
+                            Uri.parse(WebView.SCHEME_TEL + extra));
+                    dialMenuItem.setOnMenuItemClickListener(new SendIntent(intent, "dial"));
                 } else {
                     dialMenuItem.setVisible(false);
                 }
@@ -240,10 +293,9 @@
                 final MenuItem sendSmsMenuItem =
                         menu.findItem(getMenuResIdForMenuType(MenuType.SMS_MENU));
                 if (mSupportsSms) {
-                    // remove the on click listener
-                    sendSmsMenuItem.setOnMenuItemClickListener(null);
-                    sendSmsMenuItem.setIntent(new Intent(Intent.ACTION_SENDTO,
-                            Uri.parse("smsto:" + extra)));
+                    final Intent intent =
+                            new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:" + extra));
+                    sendSmsMenuItem.setOnMenuItemClickListener(new SendIntent(intent, "sms"));
                 } else {
                     sendSmsMenuItem.setVisible(false);
                 }
@@ -255,54 +307,58 @@
                 addIntent.putExtra(ContactsContract.Intents.Insert.PHONE, decodedPhoneExtra);
                 final MenuItem addToContactsMenuItem =
                         menu.findItem(getMenuResIdForMenuType(MenuType.ADD_CONTACT_MENU));
-                // remove the on click listener
-                addToContactsMenuItem.setOnMenuItemClickListener(null);
-                addToContactsMenuItem.setIntent(addIntent);
+                addToContactsMenuItem.setOnMenuItemClickListener(
+                        new SendIntent(addIntent, "add_contact"));
 
                 // Copy
                 menu.findItem(getMenuResIdForMenuType(MenuType.COPY_PHONE_MENU)).
-                        setOnMenuItemClickListener(new Copy(extra));
+                        setOnMenuItemClickListener(new Copy(extra, "copy_phone"));
                 break;
-
             case WebView.HitTestResult.EMAIL_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "email", 0);
                 menu.setHeaderTitle(extra);
-                menu.findItem(getMenuResIdForMenuType(MenuType.EMAIL_CONTACT_MENU)).setIntent(
-                        new Intent(Intent.ACTION_VIEW, Uri
-                                .parse(WebView.SCHEME_MAILTO + extra)));
+                final Intent mailtoIntent =
+                        new Intent(Intent.ACTION_VIEW, Uri.parse(WebView.SCHEME_MAILTO + extra));
+                menu.findItem(getMenuResIdForMenuType(MenuType.EMAIL_CONTACT_MENU))
+                        .setOnMenuItemClickListener(new SendIntent(mailtoIntent, "send_email"));
                 menu.findItem(getMenuResIdForMenuType(MenuType.COPY_MAIL_MENU)).
-                        setOnMenuItemClickListener(new Copy(extra));
+                        setOnMenuItemClickListener(new Copy(extra, "copy_email"));
                 break;
-
             case WebView.HitTestResult.GEO_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "geo", 0);
                 menu.setHeaderTitle(extra);
                 String geoExtra = "";
                 try {
                     geoExtra = URLEncoder.encode(extra, Charset.defaultCharset().name());
-                }
-                catch (UnsupportedEncodingException ignore) {
+                } catch (UnsupportedEncodingException ignore) {
                     // Should never happen; default charset is UTF-8
                 }
                 final MenuItem viewMapMenuItem =
                         menu.findItem(getMenuResIdForMenuType(MenuType.MAP_MENU));
-                // remove the on click listener
-                viewMapMenuItem.setOnMenuItemClickListener(null);
-                viewMapMenuItem.setIntent(new Intent(Intent.ACTION_VIEW,
-                        Uri.parse(WebView.SCHEME_GEO + geoExtra)));
-                menu.findItem(getMenuResIdForMenuType(MenuType.COPY_GEO_MENU)).
-                        setOnMenuItemClickListener(new Copy(extra));
-                break;
 
+                final Intent viewMap =
+                        new Intent(Intent.ACTION_VIEW, Uri.parse(WebView.SCHEME_GEO + geoExtra));
+                viewMapMenuItem.setOnMenuItemClickListener(new SendIntent(viewMap, "view_map"));
+                menu.findItem(getMenuResIdForMenuType(MenuType.COPY_GEO_MENU)).
+                        setOnMenuItemClickListener(new Copy(extra, "copy_geo"));
+                break;
             case WebView.HitTestResult.SRC_ANCHOR_TYPE:
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "src_anchor", 0);
                 setupAnchorMenu(extra, menu);
                 break;
             case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
-                // Deliberately do not break because we want to fall through to image type.
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "src_image_anchor", 0);
                 setupAnchorMenu(extra, menu);
+                setupImageMenu(extra, menu);
+                break;
             case WebView.HitTestResult.IMAGE_TYPE:
-                // The image menu will be visible whenever the
-                // intent builder returns an intent. If it returns null,
-                // the image menu will not be shown.
-                menu.setGroupVisible(R.id.IMAGE_MENU, setupImageMenu(extra, menu));
+                Analytics.getInstance().sendEvent(
+                        CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "image", 0);
+                setupImageMenu(extra, menu);
                 break;
             default:
                 break;
@@ -320,16 +376,15 @@
         menu.setHeaderTitle(extra);
 
         menu.findItem(getMenuResIdForMenuType(MenuType.COPY_LINK_MENU)).
-                setOnMenuItemClickListener(new Copy(extra));
+                setOnMenuItemClickListener(new Copy(extra, "copy_link"));
 
         final MenuItem openLinkMenuItem =
                 menu.findItem(getMenuResIdForMenuType(MenuType.OPEN_MENU));
-        // remove the on click listener
-        openLinkMenuItem.setOnMenuItemClickListener(null);
-        openLinkMenuItem.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
+        openLinkMenuItem.setOnMenuItemClickListener(
+                new SendIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(extra)), "open_link"));
 
         menu.findItem(getMenuResIdForMenuType(MenuType.SHARE_LINK_MENU)).
-                setOnMenuItemClickListener(new Share(extra));
+                setOnMenuItemClickListener(new Share(extra, "share_link"));
     }
 
     /**
@@ -338,21 +393,25 @@
      * {@link android.webkit.WebView.HitTestResult#SRC_IMAGE_ANCHOR_TYPE}.
      * @param url Url that was long pressed.
      * @param menu The {@link android.view.ContextMenu} that is about to be shown.
-     * @return {@code true} if the view image menu item should be visible.
-     * {@code false}, otherwise.
      */
-    protected boolean setupImageMenu(String url, ContextMenu menu) {
-        final Message msg = (mCallbacks != null) ? mCallbacks.getMessageForClickedUrl(url) : null;
+    private void setupImageMenu(String url, ContextMenu menu) {
+        final ConversationMessage msg =
+                (mCallbacks != null) ? mCallbacks.getMessageForClickedUrl(url) : null;
+        if (msg == null) {
+            menu.setGroupVisible(R.id.IMAGE_MENU, false);
+            return;
+        }
+
         final Intent intent = mIntentBuilder.createInlineAttachmentViewIntent(mActivity, url, msg);
         if (intent == null) {
-            return false;
+            menu.setGroupVisible(R.id.IMAGE_MENU, false);
+            return;
         }
 
         final MenuItem menuItem = menu.findItem(R.id.view_image_context_menu_id);
-        menuItem.setOnMenuItemClickListener(null);
-        menuItem.setIntent(intent);
+        menuItem.setOnMenuItemClickListener(new SendIntent(intent, "view_image"));
 
-        return true;
+        menu.setGroupVisible(R.id.IMAGE_MENU, true);
     }
 
     @Override
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index c0768ed..41049de 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -39,6 +39,7 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -102,6 +103,7 @@
 import com.android.mail.utils.ContentProviderTask;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.NotificationActionUtils;
 import com.android.mail.utils.Utils;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
@@ -125,6 +127,13 @@
         RespondInlineListener, TextWatcher,
         AttachmentAddedOrDeletedListener, OnAccountChangedListener,
         LoaderManager.LoaderCallbacks<Cursor>, TextView.OnEditorActionListener {
+    /**
+     * An {@link Intent} action that launches {@link ComposeActivity}, but is handled as if the
+     * {@link Activity} were launched with no special action.
+     */
+    private static final String ACTION_LAUNCH_COMPOSE =
+            "com.android.mail.intent.action.LAUNCH_COMPOSE";
+
     // Identifiers for which type of composition this is
     public static final int COMPOSE = -1;
     public static final int REPLY = 0;
@@ -173,6 +182,8 @@
     static final String[] ALL_EXTRAS = { EXTRA_SUBJECT, EXTRA_BODY, EXTRA_TO, EXTRA_CC, EXTRA_BCC,
             EXTRA_QUOTED_TEXT };
 
+    private static final String WEAR_EXTRA = "com.google.android.wearable.extras";
+
     private static SendOrSaveCallback sTestSendOrSaveCallback = null;
     // Map containing information about requests to create new messages, and the id of the
     // messages that were the result of those requests.
@@ -234,7 +245,14 @@
     /**
      * A single thread for running tasks in the background.
      */
-    private Handler mSendSaveTaskHandler = null;
+    private final static Handler SEND_SAVE_TASK_HANDLER;
+    static {
+        HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
+        handlerThread.start();
+
+        SEND_SAVE_TASK_HANDLER = new Handler(handlerThread.getLooper());
+    }
+
     private RecipientEditTextView mTo;
     private RecipientEditTextView mCc;
     private RecipientEditTextView mBcc;
@@ -319,14 +337,6 @@
     /**
      * Can be called from a non-UI thread.
      */
-    public static void composeWithQuotedText(Context launcher, Account account,
-            String quotedText, String subject, final ContentValues extraValues) {
-        launch(launcher, account, null, COMPOSE, null, null, quotedText, subject, extraValues);
-    }
-
-    /**
-     * Can be called from a non-UI thread.
-     */
     public static void composeWithExtraValues(Context launcher, Account account,
             String subject, final ContentValues extraValues) {
         launch(launcher, account, null, COMPOSE, null, null, null, subject, extraValues);
@@ -348,9 +358,10 @@
         return createActionIntent(launcher, account, messageUri, FORWARD);
     }
 
-    private static Intent createActionIntent(final Context launcher, final Account account,
+    private static Intent createActionIntent(final Context context, final Account account,
             final Uri messageUri, final int action) {
-        final Intent intent = new Intent(launcher, ComposeActivity.class);
+        final Intent intent = new Intent(ACTION_LAUNCH_COMPOSE);
+        intent.setPackage(context.getPackageName());
 
         updateActionIntent(account, messageUri, action, intent);
 
@@ -395,10 +406,11 @@
                 "android-gmail-readability@google.com", body, null, null, null /* extraValues */);
     }
 
-    private static void launch(Context launcher, Account account, Message message, int action,
+    private static void launch(Context context, Account account, Message message, int action,
             String toAddress, String body, String quotedText, String subject,
             final ContentValues extraValues) {
-        Intent intent = new Intent(launcher, ComposeActivity.class);
+        Intent intent = new Intent(ACTION_LAUNCH_COMPOSE);
+        intent.setPackage(context.getPackageName());
         intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
         intent.putExtra(EXTRA_ACTION, action);
         intent.putExtra(Utils.EXTRA_ACCOUNT, account);
@@ -423,15 +435,15 @@
             LogUtils.d(LOG_TAG, "Launching with extraValues: %s", extraValues.toString());
             intent.putExtra(EXTRA_VALUES, extraValues);
         }
-        launcher.startActivity(intent);
+        context.startActivity(intent);
     }
 
-    public static void composeMailto(Context launcher, Account account, Uri mailto) {
-        final Intent intent = new Intent(Intent.ACTION_VIEW, mailto, launcher,
-                ComposeActivity.class);
+    public static void composeMailto(Context context, Account account, Uri mailto) {
+        final Intent intent = new Intent(Intent.ACTION_VIEW, mailto);
+        intent.setPackage(context.getPackageName());
         intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
         intent.putExtra(Utils.EXTRA_ACCOUNT, account);
-        launcher.startActivity(intent);
+        context.startActivity(intent);
     }
 
     @Override
@@ -517,12 +529,29 @@
         if (mRefMessageUri != null) {
             mShowQuotedText = true;
             mComposeMode = action;
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+                // TODO: Use the wrapper api to get the clip data.
+                ClipData clipData = intent.getClipData();
+                if (clipData != null
+                        && WEAR_EXTRA.equals(clipData.getDescription().getLabel())) {
+                    Bundle extras = clipData.getItemAt(0).getIntent().getExtras();
+                    if (extras != null) {
+                        String wearReply =
+                                extras.getString(NotificationActionUtils.WEAR_REPLY_INPUT);
+                        createWearReplyTask(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION,
+                                mComposeMode, wearReply).execute();
+                        finish();
+                        return;
+                    }
+                }
+            }
+
             getLoaderManager().initLoader(INIT_DRAFT_USING_REFERENCE_MESSAGE, null, this);
             return;
         } else if (message != null && action != EDIT_DRAFT) {
             initFromDraftMessage(message);
             initQuotedTextFromRefMessage(mRefMessage, action);
-            showCcBcc(savedState);
             mShowQuotedText = message.appendRefMessageContent;
             // if we should be showing quoted text but mRefMessage is null
             // and we have some quotedText, display that
@@ -539,9 +568,6 @@
                 throw new IllegalStateException("Message must not be null to edit draft");
             }
             initFromDraftMessage(message);
-            final boolean showBcc = !TextUtils.isEmpty(message.getBcc());
-            final boolean showCc = showBcc || !TextUtils.isEmpty(message.getCc());
-            mCcBccView.show(false, showCc, showBcc);
             // Update the action to the draft type of the previous draft
             switch (message.draftType) {
                 case UIProvider.DraftType.REPLY:
@@ -585,6 +611,48 @@
         finishSetup(action, intent, savedState);
     }
 
+    private static AsyncTask<Void, Void, Message> createWearReplyTask(
+            final ComposeActivity composeActivity,
+            final Uri refMessageUri, final String[] projection, final int action,
+            final String wearReply) {
+        return new AsyncTask<Void, Void, Message>() {
+            private Intent mEmptyServiceIntent = new Intent(composeActivity, EmptyService.class);
+
+            @Override
+            protected void onPreExecute() {
+                // Start service so we won't be killed if this app is put in the background.
+                composeActivity.startService(mEmptyServiceIntent);
+            }
+
+            @Override
+            protected Message doInBackground(Void... params) {
+                Cursor cursor = composeActivity.getContentResolver()
+                        .query(refMessageUri, projection, null, null, null, null);
+                if (cursor != null) {
+                    try {
+                        cursor.moveToFirst();
+                        return new Message(cursor);
+                    } finally {
+                        cursor.close();
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Message message) {
+                composeActivity.stopService(mEmptyServiceIntent);
+
+                composeActivity.mRefMessage = message;
+                composeActivity.initFromRefMessage(action);
+                composeActivity.setBody(wearReply, false);
+                composeActivity.finishSetup(action, composeActivity.getIntent(), null);
+                composeActivity.sendOrSaveWithSanityChecks(false /* save */, true /* show  toast */,
+                        false /* orientationChanged */, true /* autoSend */);
+            }
+        };
+    }
+
     private void checkValidAccounts() {
         final Account[] allAccounts = AccountUtils.getAccounts(this);
         if (allAccounts == null || allAccounts.length == 0) {
@@ -684,6 +752,13 @@
         }
 
         initChangeListeners();
+
+        // These two should be identical since we check CC and BCC the same way
+        boolean showCc = !TextUtils.isEmpty(mCc.getText()) || (savedInstanceState != null &&
+                savedInstanceState.getBoolean(EXTRA_SHOW_CC));
+        boolean showBcc = !TextUtils.isEmpty(mBcc.getText()) || (savedInstanceState != null &&
+                savedInstanceState.getBoolean(EXTRA_SHOW_BCC));
+        mCcBccView.show(false /* animate */, showCc, showBcc);
         updateHideOrShowCcBcc();
         updateHideOrShowQuotedText(mShowQuotedText);
 
@@ -886,7 +961,7 @@
         int mode = getMode();
         state.putInt(EXTRA_ACTION, mode);
 
-        final Message message = createMessage(selectedReplyFromAccount, mode);
+        final Message message = createMessage(selectedReplyFromAccount, mRefMessage, mode);
         if (mDraft != null) {
             message.id = mDraft.id;
             message.serverId = mDraft.serverId;
@@ -921,7 +996,8 @@
         return mode;
     }
 
-    private Message createMessage(ReplyFromAccount selectedReplyFromAccount, int mode) {
+    private Message createMessage(ReplyFromAccount selectedReplyFromAccount, Message refMessage,
+            int mode) {
         Message message = new Message();
         message.id = UIProvider.INVALID_MESSAGE_ID;
         message.serverId = null;
@@ -946,8 +1022,18 @@
         message.alwaysShowImages = false;
         message.attachmentsJson = Attachment.toJSONArray(attachments);
         CharSequence quotedText = mQuotedTextView.getQuotedText();
-        message.quotedTextOffset = !TextUtils.isEmpty(quotedText) ? QuotedTextView
-                .getQuotedTextOffset(quotedText.toString()) : -1;
+        message.quotedTextOffset = -1; // Just a default value.
+        if (refMessage != null && !TextUtils.isEmpty(quotedText)) {
+            if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
+                // We want the index to point to just the quoted text and not the
+                // "On December 25, 2014..." part of it.
+                message.quotedTextOffset =
+                        QuotedTextView.getQuotedTextOffset(quotedText.toString());
+            } else if (!TextUtils.isEmpty(refMessage.bodyText)) {
+                // We want to point to the entire quoted text.
+                message.quotedTextOffset = QuotedTextView.findQuotedTextIndex(quotedText);
+            }
+        }
         message.accountUri = null;
         final String email = selectedReplyFromAccount != null ? selectedReplyFromAccount.address
                 : mAccount != null ? mAccount.getEmailAddress() : null;
@@ -1264,16 +1350,6 @@
                 }
             }
         }
-
-        if (mRefMessage != null) {
-            // CC field only gets populated when doing REPLY_ALL.
-            // BCC never gets auto-populated, unless the user is editing
-            // a draft with one.
-            if (!TextUtils.isEmpty(mCc.getText()) && action == REPLY_ALL) {
-                mCcBccView.show(false, true, false);
-            }
-        }
-        updateHideOrShowCcBcc();
     }
 
     private void setFieldsFromRefMessage(int action) {
@@ -1306,14 +1382,17 @@
                 addAttachmentAndUpdateView(a);
             }
         }
-        int quotedTextIndex = message.appendRefMessageContent ?
-                message.quotedTextOffset : -1;
+        int quotedTextIndex = message.appendRefMessageContent ? message.quotedTextOffset : -1;
         // Set the body
         CharSequence quotedText = null;
         if (!TextUtils.isEmpty(message.bodyHtml)) {
             CharSequence htmlText = "";
             if (quotedTextIndex > -1) {
-                // Find the offset in the htmltext of the actual quoted text and strip it out.
+                // Find the offset in the html text of the actual quoted text and strip it out.
+                // Note that the actual quotedTextOffset in the message has not changed as
+                // this different offset is used only for display purposes. They point to different
+                // parts of the original message.  Please see the comments in QuoteTextView
+                // to see the differences.
                 quotedTextIndex = QuotedTextView.findQuotedTextIndex(message.bodyHtml);
                 if (quotedTextIndex > -1) {
                     htmlText = Utils.convertHtmlToPlainText(message.bodyHtml.substring(0,
@@ -1327,13 +1406,23 @@
             mBodyView.setText(htmlText);
         } else {
             final String body = message.bodyText;
-            final CharSequence bodyText = !TextUtils.isEmpty(body) ?
-                    (quotedTextIndex > -1 ?
-                            message.bodyText.substring(0, quotedTextIndex) : message.bodyText)
-                            : "";
-            if (quotedTextIndex > -1) {
-                quotedText = !TextUtils.isEmpty(body) ? message.bodyText.substring(quotedTextIndex)
-                        : null;
+            final CharSequence bodyText;
+            if (TextUtils.isEmpty(body)) {
+                bodyText = "";
+                quotedText = null;
+            } else {
+                if (quotedTextIndex > body.length()) {
+                    // Sanity check to guarantee that we will not over index the String.
+                    // If this happens there is a bigger problem. This should never happen hence
+                    // the wtf logging.
+                    quotedTextIndex = -1;
+                    LogUtils.wtf(LOG_TAG, "quotedTextIndex (%d) > body.length() (%d)",
+                            quotedTextIndex, body.length());
+                }
+                bodyText = quotedTextIndex > -1 ? body.substring(0, quotedTextIndex) : body;
+                if (quotedTextIndex > -1) {
+                    quotedText = body.substring(quotedTextIndex);
+                }
             }
             mBodyView.setText(bodyText);
         }
@@ -1465,6 +1554,31 @@
     }
 
     /**
+     * Replaces all occurrences of '%' with "%25", to prevent URLDecode.decode from
+     * crashing on decoded '%' symbols
+     *
+     * @param toReplace Input string
+     * @return The string with all "%" characters replaced with "%25"
+     */
+    private static String replacePercent(String toReplace) {
+        return toReplace.replace("%", "%25");
+    }
+
+    /**
+     * Helper function to encapsulate encoding/decoding string from Uri.getQueryParameters
+     * @param content Input string
+     * @return The string that's properly escaped to be shown in mail subject/content
+     */
+    private static String decodeContentFromQueryParam(String content) {
+        try {
+            return URLDecoder.decode(replacePlus(replacePercent(content)), UTF8_ENCODING_NAME);
+        } catch (UnsupportedEncodingException e) {
+            LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), content);
+            return "";  // Default to empty string so setText/setBody has same behavior as before.
+        }
+    }
+
+    /**
      * Initialize the compose view from a String representing a mailTo uri.
      * @param mailToString The uri as a string.
      */
@@ -1502,25 +1616,15 @@
         List<String> bcc = uri.getQueryParameters("bcc");
         addBccAddresses(Arrays.asList(bcc.toArray(new String[bcc.size()])));
 
+        // NOTE: Uri.getQueryParameters already decodes % encoded characters
         List<String> subject = uri.getQueryParameters("subject");
         if (subject.size() > 0) {
-            try {
-                mSubject.setText(URLDecoder.decode(replacePlus(subject.get(0)),
-                        UTF8_ENCODING_NAME));
-            } catch (UnsupportedEncodingException e) {
-                LogUtils.e(LOG_TAG, "%s while decoding subject '%s'",
-                        e.getMessage(), subject);
-            }
+            mSubject.setText(decodeContentFromQueryParam(subject.get(0)));
         }
 
         List<String> body = uri.getQueryParameters("body");
         if (body.size() > 0) {
-            try {
-                setBody(URLDecoder.decode(replacePlus(body.get(0)), UTF8_ENCODING_NAME),
-                        true /* with signature */);
-            } catch (UnsupportedEncodingException e) {
-                LogUtils.e(LOG_TAG, "%s while decoding body '%s'", e.getMessage(), body);
-            }
+            setBody(decodeContentFromQueryParam(body.get(0)), true /* with signature */);
         }
     }
 
@@ -1674,16 +1778,6 @@
         }
     }
 
-    private void showCcBcc(Bundle state) {
-        if (state != null && state.containsKey(EXTRA_SHOW_CC)) {
-            boolean showCc = state.getBoolean(EXTRA_SHOW_CC);
-            boolean showBcc = state.getBoolean(EXTRA_SHOW_BCC);
-            if (showCc || showBcc) {
-                mCcBccView.show(false, showCc, showBcc);
-            }
-        }
-    }
-
     /**
      * Add attachment and update the compose area appropriately.
      */
@@ -2761,46 +2855,63 @@
         MessageModification.putToAddresses(values, message.getToAddresses());
         MessageModification.putCcAddresses(values, message.getCcAddresses());
         MessageModification.putBccAddresses(values, message.getBccAddresses());
-
         MessageModification.putCustomFromAddress(values, message.getFrom());
 
         MessageModification.putSubject(values, message.subject);
+
         // Make sure to remove only the composing spans from the Spannable before saving.
         final String htmlBody = Html.toHtml(removeComposingSpans(body));
-
-        boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
-        StringBuilder fullBody = new StringBuilder(htmlBody);
-        if (includeQuotedText) {
-            // HTML gets converted to text for now
-            final String text = quotedText.toString();
-            if (QuotedTextView.containsQuotedText(text)) {
-                int pos = QuotedTextView.getQuotedTextOffset(text);
-                final int quoteStartPos = fullBody.length() + pos;
-                fullBody.append(text);
-                MessageModification.putQuoteStartPos(values, quoteStartPos);
-                MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
-                MessageModification.putAppendRefMessageContent(values, true /* include quoted */);
-            } else {
-                LogUtils.w(LOG_TAG, "Couldn't find quoted text");
-                // This shouldn't happen, but just use what we have,
-                // and don't do server-side expansion
-                fullBody.append(text);
-            }
+        final String textBody = Utils.convertHtmlToPlainText(htmlBody);
+        // fullbody will contain the actual body plus the quoted text.
+        final String fullBody;
+        final String quotedString;
+        final boolean hasQuotedText = !TextUtils.isEmpty(quotedText);
+        if (hasQuotedText) {
+            // The quoted text is HTML at this point.
+            quotedString = quotedText.toString();
+            fullBody = htmlBody + quotedString;
+            MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
+            MessageModification.putAppendRefMessageContent(values, true /* include quoted */);
+        } else {
+            fullBody = htmlBody;
+            quotedString = null;
         }
-        int draftType = getDraftType(composeMode);
-        MessageModification.putDraftType(values, draftType);
         if (refMessage != null) {
+            // The code below might need to be revisited. The quoted text position is different
+            // between text/html and text/plain parts and they should be stored seperately and
+            // the right version should be used in the UI. text/html should have preference
+            // if both exist.  Issues like this made me file b/14256940 to make sure that we
+            // properly handle the existing of both text/html and text/plain parts and to verify
+            // that we are not making some assumptions that break if there is no text/html part.
+            int quotedTextPos = -1;
             if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
                 MessageModification.putBodyHtml(values, fullBody.toString());
+                if (hasQuotedText) {
+                    quotedTextPos = htmlBody.length() +
+                            QuotedTextView.getQuotedTextOffset(quotedString);
+                }
             }
             if (!TextUtils.isEmpty(refMessage.bodyText)) {
                 MessageModification.putBody(values,
                         Utils.convertHtmlToPlainText(fullBody.toString()));
+                if (hasQuotedText && (quotedTextPos == -1)) {
+                    quotedTextPos = textBody.length();
+                }
+            }
+            if (quotedTextPos != -1) {
+                // The quoted text pos is the text/html version first and the text/plan version
+                // if there is no text/html part. The reason for this is because preference
+                // is given to text/html in the compose window if it exists. In the future, we
+                // should calculate the index for both since the user could choose to compose
+                // explicitly in text/plain.
+                MessageModification.putQuoteStartPos(values, quotedTextPos);
             }
         } else {
             MessageModification.putBodyHtml(values, fullBody.toString());
             MessageModification.putBody(values, Utils.convertHtmlToPlainText(fullBody.toString()));
         }
+        int draftType = getDraftType(composeMode);
+        MessageModification.putDraftType(values, draftType);
         MessageModification.putAttachments(values, message.getAttachments());
         if (!TextUtils.isEmpty(refMessageId)) {
             MessageModification.putRefMessageId(values, refMessageId);
@@ -2941,17 +3052,10 @@
 
         setAccount(mReplyFromAccount.account);
 
-        if (mSendSaveTaskHandler == null) {
-            HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
-            handlerThread.start();
-
-            mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
-        }
-
-        Message msg = createMessage(mReplyFromAccount, getMode());
+        Message msg = createMessage(mReplyFromAccount, mRefMessage, getMode());
         mRequestId = sendOrSaveInternal(this, mReplyFromAccount, msg, mRefMessage, body,
                 mQuotedTextView.getQuotedTextIfIncluded(), callback,
-                mSendSaveTaskHandler, save, mComposeMode, mDraftAccount, mExtraValues);
+                SEND_SAVE_TASK_HANDLER, save, mComposeMode, mDraftAccount, mExtraValues);
 
         // Don't display the toast if the user is just changing the orientation,
         // but we still need to save the draft to the cursor because this is how we restore
@@ -3502,7 +3606,7 @@
             fragment.updateAccount(account);
         } else {
             findViewById(R.id.wait).setVisibility(View.VISIBLE);
-            replaceFragment(WaitFragment.newInstance(account, true),
+            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
                     FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
         }
     }
diff --git a/src/com/android/mail/compose/QuotedTextView.java b/src/com/android/mail/compose/QuotedTextView.java
index 2dd64d5..db35ae6 100644
--- a/src/com/android/mail/compose/QuotedTextView.java
+++ b/src/com/android/mail/compose/QuotedTextView.java
@@ -340,6 +340,13 @@
         return pos >= 0;
     }
 
+    /**
+     * Returns the index of the actual quoted text and NOT the meta information such as:
+     * "On April 4, 2013 Joe Smith <jsmith@example.com> wrote:" that is part of the original
+     * message when replying and including the original text.
+     * @param text HTML text that includes quoted text.
+     * @return The offset found.
+     */
     public static int getQuotedTextOffset(String text) {
         return text.indexOf(QuotedTextView.HEADER_SEPARATOR)
                 + QuotedTextView.HEADER_SEPARATOR_LENGTH;
diff --git a/src/com/android/mail/photo/MailPhotoViewActivity.java b/src/com/android/mail/photo/MailPhotoViewActivity.java
index 3113bdf..5d24394 100644
--- a/src/com/android/mail/photo/MailPhotoViewActivity.java
+++ b/src/com/android/mail/photo/MailPhotoViewActivity.java
@@ -39,6 +39,7 @@
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
 import com.android.mail.browse.AttachmentActionHandler;
+import com.android.mail.browse.ConversationMessage;
 import com.android.mail.print.PrintUtils;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.Message;
@@ -79,6 +80,10 @@
 
     private static final String EXTRA_ACCOUNT = MailPhotoViewActivity.class.getName() + "-acct";
     private static final String EXTRA_MESSAGE = MailPhotoViewActivity.class.getName() + "-msg";
+    private static final String EXTRA_HIDE_EXTRA_OPTION_ONE =
+            MailPhotoViewActivity.class.getName() + "-hide-extra-option-one";
+
+    private boolean mHideExtraOptionOne;
 
     /**
      * Start a new MailPhotoViewActivity to view the given images.
@@ -86,7 +91,7 @@
      * @param photoIndex The index of the photo to show first.
      */
     public static void startMailPhotoViewActivity(final Context context, final String account,
-            final Message msg, final int photoIndex) {
+            final ConversationMessage msg, final int photoIndex) {
         final Intents.PhotoViewIntentBuilder builder =
                 Intents.newPhotoViewIntentBuilder(context,
                         "com.android.mail.photo.MailPhotoViewActivity");
@@ -104,13 +109,13 @@
      * @param initialPhotoUri The uri of the photo to show first.
      */
     public static void startMailPhotoViewActivity(final Context context, final String account,
-            final Message msg, final String initialPhotoUri) {
+            final ConversationMessage msg, final String initialPhotoUri) {
         context.startActivity(
                 buildMailPhotoViewActivityIntent(context, account, msg, initialPhotoUri));
     }
 
     public static Intent buildMailPhotoViewActivityIntent(
-            final Context context, final String account, final Message msg,
+            final Context context, final String account, final ConversationMessage msg,
             final String initialPhotoUri) {
         final Intents.PhotoViewIntentBuilder builder = Intents.newPhotoViewIntentBuilder(
                 context, "com.android.mail.photo.MailPhotoViewActivity");
@@ -122,9 +127,11 @@
         return wrapIntent(builder.build(), account, msg);
     }
 
-    private static Intent wrapIntent(final Intent intent, final String account, final Message msg) {
+    private static Intent wrapIntent(
+            final Intent intent, final String account, final ConversationMessage msg) {
         intent.putExtra(EXTRA_MESSAGE, msg);
         intent.putExtra(EXTRA_ACCOUNT, account);
+        intent.putExtra(EXTRA_HIDE_EXTRA_OPTION_ONE, msg.getConversation() == null);
         return intent;
     }
 
@@ -139,6 +146,7 @@
         final Intent intent = getIntent();
         final String account = intent.getStringExtra(EXTRA_ACCOUNT);
         final Message msg = intent.getParcelableExtra(EXTRA_MESSAGE);
+        mHideExtraOptionOne = intent.getBooleanExtra(EXTRA_HIDE_EXTRA_OPTION_ONE, false);
         mActionHandler.setAccount(account);
         mActionHandler.setMessage(msg);
     }
@@ -183,7 +191,11 @@
             mShareItem.setEnabled(canShare);
             mPrintItem.setEnabled(canShare);
             mDownloadAgainItem.setEnabled(attachment.canSave() && attachment.isDownloading());
-            mExtraOption1Item.setEnabled(mActionHandler.shouldShowExtraOption1());
+            if (mHideExtraOptionOne) {
+                mExtraOption1Item.setVisible(false);
+            } else {
+                mExtraOption1Item.setEnabled(mActionHandler.shouldShowExtraOption1());
+            }
         } else {
             if (mMenu != null) {
                 mMenu.setGroupEnabled(R.id.photo_view_menu_group, false);
diff --git a/src/com/android/mail/providers/Attachment.java b/src/com/android/mail/providers/Attachment.java
index 569d1ed..b8e86ed 100644
--- a/src/com/android/mail/providers/Attachment.java
+++ b/src/com/android/mail/providers/Attachment.java
@@ -233,7 +233,8 @@
     /**
      * Constructor for use when creating attachments in eml files.
      */
-    public Attachment(Context context, Part part, Uri emlFileUri, String messageId, String partId) {
+    public Attachment(Context context, Part part, Uri emlFileUri, String messageId, String cid,
+                      boolean inline) {
         try {
             // Transfer fields from mime format to provider format
             final String contentTypeHeader = MimeUtility.unfoldAndDecode(part.getContentType());
@@ -245,7 +246,7 @@
             }
 
             contentType = MimeType.inferMimeType(name, part.getMimeType());
-            uri = EmlAttachmentProvider.getAttachmentUri(emlFileUri, messageId, partId);
+            uri = EmlAttachmentProvider.getAttachmentUri(emlFileUri, messageId, cid);
             contentUri = uri;
             thumbnailUri = uri;
             previewIntentUri = null;
@@ -253,7 +254,8 @@
             providerData = null;
             supportsDownloadAgain = false;
             destination = AttachmentDestination.CACHE;
-            type = AttachmentType.STANDARD;
+            type = inline ? AttachmentType.INLINE_CURRENT_MESSAGE : AttachmentType.STANDARD;
+            partId = cid;
             flags = 0;
 
             // insert attachment into content provider so that we can open the file
@@ -300,6 +302,7 @@
         supportsDownloadAgain = values.getAsBoolean(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN);
         type = values.getAsInteger(AttachmentColumns.TYPE);
         flags = values.getAsInteger(AttachmentColumns.FLAGS);
+        partId = values.getAsString(AttachmentColumns.CONTENT_ID);
     }
 
     /**
@@ -324,6 +327,7 @@
         values.put(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, supportsDownloadAgain);
         values.put(AttachmentColumns.TYPE, type);
         values.put(AttachmentColumns.FLAGS, flags);
+        values.put(AttachmentColumns.CONTENT_ID, partId);
 
         return values;
     }
diff --git a/src/com/android/mail/providers/Conversation.java b/src/com/android/mail/providers/Conversation.java
index d53acbb..df3a1ed 100644
--- a/src/com/android/mail/providers/Conversation.java
+++ b/src/com/android/mail/providers/Conversation.java
@@ -157,10 +157,6 @@
      * @see UIProvider.ConversationColumns#REMOTE
      */
     public final boolean isRemote;
-    /**
-     * @see UIProvider.ConversationColumns#PERMALINK
-     */
-    public final String permalink;
 
     /**
      * Used within the UI to indicate the adapter position of this conversation
@@ -224,7 +220,6 @@
         dest.writeString(attachmentPreviewUri1);
         dest.writeInt(attachmentPreviewStates);
         dest.writeInt(attachmentPreviewsCount);
-        dest.writeString(permalink);
     }
 
     private Conversation(Parcel in, ClassLoader loader) {
@@ -257,7 +252,6 @@
         attachmentPreviewUri1 = in.readString();
         attachmentPreviewStates = in.readInt();
         attachmentPreviewsCount = in.readInt();
-        permalink = in.readString();
     }
 
     @Override
@@ -351,7 +345,6 @@
                 UIProvider.CONVERSATION_ATTACHMENT_PREVIEW_STATES_COLUMN);
         attachmentPreviewsCount = cursor.getInt(
                 UIProvider.CONVERSATION_ATTACHMENT_PREVIEWS_COUNT_COLUMN);
-        permalink = cursor.getString(UIProvider.CONVERSATION_PERMALINK_COLUMN);
     }
 
     public Conversation(Conversation other) {
@@ -390,7 +383,6 @@
         attachmentPreviewUri1 = other.attachmentPreviewUri1;
         attachmentPreviewStates = other.attachmentPreviewStates;
         attachmentPreviewsCount = other.attachmentPreviewsCount;
-        permalink = other.permalink;
     }
 
     private Conversation(long id, Uri uri, String subject, long dateMs,
@@ -431,7 +423,6 @@
         this.attachmentPreviewUri1 = attachmentPreviewUri1;
         this.attachmentPreviewStates = attachmentPreviewStates;
         this.attachmentPreviewsCount = attachmentPreviewsCount;
-        this.permalink = permalink;
     }
 
     public static class Builder {
diff --git a/src/com/android/mail/providers/EmlAttachmentProvider.java b/src/com/android/mail/providers/EmlAttachmentProvider.java
index 901fec3..f1c360b 100644
--- a/src/com/android/mail/providers/EmlAttachmentProvider.java
+++ b/src/com/android/mail/providers/EmlAttachmentProvider.java
@@ -19,6 +19,7 @@
 
 import android.app.DownloadManager;
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -29,6 +30,7 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
+import android.text.TextUtils;
 
 import com.android.ex.photo.provider.PhotoContract;
 import com.android.mail.R;
@@ -66,6 +68,7 @@
 
     private static final int ATTACHMENT_LIST = 0;
     private static final int ATTACHMENT = 1;
+    private static final int ATTACHMENT_BY_CID = 2;
 
     /**
      * The buffer size used to copy data from cache to sd card.
@@ -98,8 +101,9 @@
 
         if (!sUrisAddedToMatcher) {
             sUrisAddedToMatcher = true;
-            sUriMatcher.addURI(authority, "*/*", ATTACHMENT_LIST);
-            sUriMatcher.addURI(authority, "*/*/#", ATTACHMENT);
+            sUriMatcher.addURI(authority, "attachments/*/*", ATTACHMENT_LIST);
+            sUriMatcher.addURI(authority, "attachment/*/*/#", ATTACHMENT);
+            sUriMatcher.addURI(authority, "attachmentByCid/*/*/*", ATTACHMENT_BY_CID);
         }
 
         mDownloadManager =
@@ -116,9 +120,10 @@
         final int match = sUriMatcher.match(uri);
         // ignore other projections
         final MatrixCursor cursor = new MatrixCursor(UIProvider.ATTACHMENT_PROJECTION);
+        final ContentResolver cr = getContext().getContentResolver();
 
         switch (match) {
-            case ATTACHMENT_LIST:
+            case ATTACHMENT_LIST: {
                 final List<String> contentTypeQueryParameters =
                         uri.getQueryParameters(PhotoContract.ContentTypeParameters.CONTENT_TYPE);
                 uri = uri.buildUpon().clearQuery().build();
@@ -126,13 +131,35 @@
                 for (final Uri attachmentUri : attachmentUris) {
                     addRow(cursor, attachmentUri, contentTypeQueryParameters);
                 }
-                cursor.setNotificationUri(getContext().getContentResolver(), uri);
+                cursor.setNotificationUri(cr, uri);
                 break;
-            case ATTACHMENT:
+            }
+            case ATTACHMENT: {
                 addRow(cursor, mUriAttachmentMap.get(uri));
-                cursor.setNotificationUri(
-                        getContext().getContentResolver(), getListUriFromAttachmentUri(uri));
+                cursor.setNotificationUri(cr, getListUriFromAttachmentUri(uri));
                 break;
+            }
+            case ATTACHMENT_BY_CID: {
+                // form the attachment lists uri by clipping off the cid from the given uri
+                final Uri attachmentsListUri = getListUriFromAttachmentUri(uri);
+                final String cid = uri.getPathSegments().get(3);
+
+                // find all uris for the parent message
+                final List<Uri> attachmentUris = mUriListMap.get(attachmentsListUri);
+
+                if (attachmentUris != null) {
+                    // find the attachment that contains the given cid
+                    for (Uri attachmentsUri : attachmentUris) {
+                        final Attachment attachment = mUriAttachmentMap.get(attachmentsUri);
+                        if (TextUtils.equals(cid, attachment.partId)) {
+                            addRow(cursor, attachment);
+                            cursor.setNotificationUri(cr, attachmentsListUri);
+                            break;
+                        }
+                    }
+                }
+                break;
+            }
             default:
                 break;
         }
@@ -244,7 +271,8 @@
                 .add(attachment.providerData)                       // providerData
                 .add(attachment.supportsDownloadAgain() ? 1 : 0)    // supportsDownloadAgain
                 .add(attachment.type)                               // type
-                .add(attachment.flags);                             // flags
+                .add(attachment.flags)                              // flags
+                .add(attachment.partId);                            // partId (same as RFC822 cid)
     }
 
     /**
@@ -389,21 +417,38 @@
     }
 
     /**
-     * Returns an attachment list uri for an eml file at the given uri
-     * with the given message id.
+     * Returns an attachment list uri for the specific attachment uri passed.
      */
-    public static Uri getAttachmentsListUri(Uri emlFileUri, String messageId) {
-        return BASE_URI.buildUpon().appendPath(Integer.toString(emlFileUri.hashCode()))
-                .appendPath(messageId).build();
+    private static Uri getListUriFromAttachmentUri(Uri uri) {
+        final List<String> segments = uri.getPathSegments();
+        return BASE_URI.buildUpon()
+                .appendPath("attachments")
+                .appendPath(segments.get(1))
+                .appendPath(segments.get(2))
+                .build();
     }
 
     /**
-     * Returns an attachment list uri for the specific attachment uri passed.
+     * Returns an attachment list uri for an eml file at the given uri with the given message id.
      */
-    public static Uri getListUriFromAttachmentUri(Uri uri) {
-        final List<String> segments = uri.getPathSegments();
+    public static Uri getAttachmentsListUri(Uri emlFileUri, String messageId) {
         return BASE_URI.buildUpon()
-                .appendPath(segments.get(0)).appendPath(segments.get(1)).build();
+                .appendPath("attachments")
+                .appendPath(Integer.toString(emlFileUri.hashCode()))
+                .appendPath(messageId)
+                .build();
+    }
+
+    /**
+     * Returns an attachment uri for an eml file at the given uri with the given message id.
+     * The consumer of this uri must append a specific CID to it to complete the uri.
+     */
+    public static Uri getAttachmentByCidUri(Uri emlFileUri, String messageId) {
+        return BASE_URI.buildUpon()
+                .appendPath("attachmentByCid")
+                .appendPath(Integer.toString(emlFileUri.hashCode()))
+                .appendPath(messageId)
+                .build();
     }
 
     /**
@@ -411,8 +456,12 @@
      * the given message id and part id.
      */
     public static Uri getAttachmentUri(Uri emlFileUri, String messageId, String partId) {
-        return BASE_URI.buildUpon().appendPath(Integer.toString(emlFileUri.hashCode()))
-                .appendPath(messageId).appendPath(partId).build();
+        return BASE_URI.buildUpon()
+                .appendPath("attachment")
+                .appendPath(Integer.toString(emlFileUri.hashCode()))
+                .appendPath(messageId)
+                .appendPath(partId)
+                .build();
     }
 
     /**
@@ -441,7 +490,7 @@
      * Returns the root directory for the attachments for the specific uri.
      */
     private String getCacheFileDirectory(Uri uri) {
-        return getCacheDir() + "/" + Uri.encode(uri.getPathSegments().get(0));
+        return getCacheDir() + "/" + Uri.encode(uri.getPathSegments().get(1));
     }
 
     /**
diff --git a/src/com/android/mail/providers/Folder.java b/src/com/android/mail/providers/Folder.java
index 62c0801..57cfa45 100644
--- a/src/com/android/mail/providers/Folder.java
+++ b/src/com/android/mail/providers/Folder.java
@@ -746,6 +746,13 @@
     }
 
     /**
+     * Return if this is the sent folder.
+     */
+    public boolean isSent() {
+        return isType(FolderType.SENT);
+    }
+
+    /**
      * Whether this is the special folder just used to display all mail for an account.
      */
     public boolean isViewAll() {
diff --git a/src/com/android/mail/providers/Message.java b/src/com/android/mail/providers/Message.java
index 501237d..7fd3b41 100644
--- a/src/com/android/mail/providers/Message.java
+++ b/src/com/android/mail/providers/Message.java
@@ -31,9 +31,10 @@
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
 
-import com.android.emailcommon.mail.Address;
+import com.android.emailcommon.internet.MimeHeader;
 import com.android.emailcommon.internet.MimeMessage;
 import com.android.emailcommon.internet.MimeUtility;
+import com.android.emailcommon.mail.Address;
 import com.android.emailcommon.mail.MessagingException;
 import com.android.emailcommon.mail.Part;
 import com.android.emailcommon.utility.ConversionUtilities;
@@ -60,6 +61,9 @@
     private static Pattern INLINE_IMAGE_PATTERN = Pattern.compile("<img\\s+[^>]*src=",
             Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
 
+    // regex that matches content id surrounded by "<>" optionally.
+    private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
+
     /**
      * @see BaseColumns#_ID
      */
@@ -141,6 +145,10 @@
      */
     public Uri attachmentListUri;
     /**
+     * @see UIProvider.MessageColumns#ATTACHMENT_BY_CID_URI
+     */
+    public Uri attachmentByCidUri;
+    /**
      * @see UIProvider.MessageColumns#MESSAGE_FLAGS
      */
     public long messageFlags;
@@ -203,6 +211,10 @@
      * @see UIProvider.MessageColumns#CLIPPED
      */
     public boolean clipped;
+    /**
+     * @see UIProvider.MessageColumns#PERMALINK
+     */
+    public String permalink;
 
     private transient String[] mFromAddresses = null;
     private transient String[] mToAddresses = null;
@@ -228,6 +240,25 @@
         return uri == null ? 0 : uri.hashCode();
     }
 
+    /**
+     * Helper equality function to check if the two Message objects are equal in terms of
+     * the fields that the user can input from ComposeActivity. This is primarily used to
+     * ensure draft preview/composition are synced.
+     * @param o the Message being compared to
+     * @return True if they are equal in fields, false otherwise
+     */
+    public boolean isEqual(Message o) {
+        return TextUtils.equals(this.getFrom(), o.getFrom()) &&
+                this.isSending == o.isSending &&
+                TextUtils.equals(this.getTo(), o.getTo()) &&
+                TextUtils.equals(this.getCc(), o.getCc()) &&
+                TextUtils.equals(this.getBcc(), o.getBcc()) &&
+                TextUtils.equals(this.subject, o.subject) &&
+                TextUtils.equals(this.bodyHtml, o.bodyHtml) &&
+                TextUtils.equals(this.bodyText, o.bodyText) &&
+                Objects.equal(this.attachmentListUri, o.attachmentListUri);
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(id);
@@ -262,6 +293,7 @@
         dest.writeString(viaDomain);
         dest.writeInt(isSending ? 1 : 0);
         dest.writeInt(clipped ? 1 : 0);
+        dest.writeString(permalink);
     }
 
     private Message(Parcel in) {
@@ -297,6 +329,7 @@
         viaDomain = in.readString();
         isSending = in.readInt() != 0;
         clipped = in.readInt() != 0;
+        permalink = in.readString();
     }
 
     public Message() {
@@ -354,6 +387,10 @@
                     .getString(UIProvider.MESSAGE_ATTACHMENT_LIST_URI_COLUMN);
             attachmentListUri = hasAttachments && !TextUtils.isEmpty(attachmentsUri) ? Uri
                     .parse(attachmentsUri) : null;
+            final String attachmentsByCidUri = cursor
+                    .getString(UIProvider.MESSAGE_ATTACHMENT_BY_CID_URI_COLUMN);
+            attachmentByCidUri = hasAttachments && !TextUtils.isEmpty(attachmentsByCidUri) ?
+                    Uri.parse(attachmentsByCidUri) : null;
             messageFlags = cursor.getLong(UIProvider.MESSAGE_FLAGS_COLUMN);
             alwaysShowImages = cursor.getInt(UIProvider.MESSAGE_ALWAYS_SHOW_IMAGES_COLUMN) != 0;
             read = cursor.getInt(UIProvider.MESSAGE_READ_COLUMN) != 0;
@@ -372,6 +409,7 @@
             viaDomain = cursor.getString(UIProvider.MESSAGE_VIA_DOMAIN_COLUMN);
             isSending = cursor.getInt(UIProvider.MESSAGE_IS_SENDING_COLUMN) != 0;
             clipped = cursor.getInt(UIProvider.MESSAGE_CLIPPED_COLUMN) != 0;
+            permalink = cursor.getString(UIProvider.MESSAGE_PERMALINK_COLUMN);
         }
     }
 
@@ -407,6 +445,7 @@
         spamWarningString = null;
         messageFlags = 0;
         clipped = false;
+        permalink = null;
         hasAttachments = false;
 
         // body values (snippet/bodyText/bodyHtml)
@@ -415,8 +454,7 @@
         ArrayList<Part> attachments = new ArrayList<Part>();
         MimeUtility.collectParts(mimeMessage, viewables, attachments);
 
-        ConversionUtilities.BodyFieldData data =
-                ConversionUtilities.parseBodyFields(viewables);
+        ConversionUtilities.BodyFieldData data = ConversionUtilities.parseBodyFields(viewables);
 
         snippet = data.snippet;
         bodyText = data.textContent;
@@ -425,17 +463,31 @@
         // populate mAttachments
         mAttachments = Lists.newArrayList();
 
-        int partId = 0;
         final String messageId = mimeMessage.getMessageId();
+
+        int partId = 0;
         for (final Part attachmentPart : attachments) {
             mAttachments.add(new Attachment(context, attachmentPart,
-                    emlFileUri, messageId, Integer.toString(partId++)));
+                    emlFileUri, messageId, Integer.toString(partId++), false /* inline */));
+        }
+
+        // instantiating an Attachment for each viewable will cause it to be registered within the
+        // EmlAttachmentProvider for later access when displaying inline attachments
+        for (final Part viewablePart : viewables) {
+            final String[] cids = viewablePart.getHeader(MimeHeader.HEADER_CONTENT_ID);
+            if (cids != null && cids.length == 1) {
+                final String cid = REMOVE_OPTIONAL_BRACKETS.matcher(cids[0]).replaceAll("$1");
+                mAttachments.add(new Attachment(context, viewablePart, emlFileUri, messageId, cid,
+                        true /* inline */));
+            }
         }
 
         hasAttachments = !mAttachments.isEmpty();
 
-        attachmentListUri =  hasAttachments ?
+        attachmentListUri = hasAttachments ?
                 EmlAttachmentProvider.getAttachmentsListUri(emlFileUri, messageId) : null;
+
+        attachmentByCidUri = EmlAttachmentProvider.getAttachmentByCidUri(emlFileUri, messageId);
     }
 
     public boolean isFlaggedReplied() {
diff --git a/src/com/android/mail/providers/ReplyFromAccount.java b/src/com/android/mail/providers/ReplyFromAccount.java
index 0916208..ad5054d 100644
--- a/src/com/android/mail/providers/ReplyFromAccount.java
+++ b/src/com/android/mail/providers/ReplyFromAccount.java
@@ -109,6 +109,9 @@
      */
     public static boolean matchesAccountOrCustomFrom(Account account, String possibleCustomFrom,
             List<ReplyFromAccount> replyFromAccounts) {
+        if (TextUtils.isEmpty(possibleCustomFrom)) {
+            return false;
+        }
         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(possibleCustomFrom);
         if (tokens != null && tokens.length > 0) {
             String parsedFromAddress = Utils.normalizeEmailAddress(tokens[0].getAddress());
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 9bcaccc..154166d 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -1018,8 +1018,7 @@
         ConversationColumns.ATTACHMENT_PREVIEW_URI0,
         ConversationColumns.ATTACHMENT_PREVIEW_URI1,
         ConversationColumns.ATTACHMENT_PREVIEW_STATES,
-        ConversationColumns.ATTACHMENT_PREVIEWS_COUNT,
-        ConversationColumns.PERMALINK
+        ConversationColumns.ATTACHMENT_PREVIEWS_COUNT
     };
 
     /**
@@ -1061,7 +1060,6 @@
     public static final int CONVERSATION_ATTACHMENT_PREVIEW_URI1_COLUMN = 27;
     public static final int CONVERSATION_ATTACHMENT_PREVIEW_STATES_COLUMN = 28;
     public static final int CONVERSATION_ATTACHMENT_PREVIEWS_COUNT_COLUMN = 29;
-    public static final int CONVERSATION_PERMALINK_COLUMN = 30;
 
     public static final class ConversationSendingState {
         public static final int OTHER = 0;
@@ -1269,12 +1267,6 @@
          */
         public static final String ATTACHMENT_PREVIEWS_COUNT = "attachmentPreviewsCount";
 
-        /**
-         * This string column contains the permalink value of the conversation
-         * or null if one does not exist.
-         */
-        public static final String PERMALINK = "permalink";
-
         private ConversationColumns() {
         }
     }
@@ -1553,6 +1545,7 @@
         MessageColumns.APPEND_REF_MESSAGE_CONTENT,
         MessageColumns.HAS_ATTACHMENTS,
         MessageColumns.ATTACHMENT_LIST_URI,
+        MessageColumns.ATTACHMENT_BY_CID_URI,
         MessageColumns.MESSAGE_FLAGS,
         MessageColumns.ALWAYS_SHOW_IMAGES,
         MessageColumns.READ,
@@ -1568,7 +1561,8 @@
         MessageColumns.SPAM_WARNING_LINK_TYPE,
         MessageColumns.VIA_DOMAIN,
         MessageColumns.IS_SENDING,
-        MessageColumns.CLIPPED
+        MessageColumns.CLIPPED,
+        MessageColumns.PERMALINK
     };
 
     /** Separates attachment info parts in strings in a message. */
@@ -1599,22 +1593,24 @@
     public static final int MESSAGE_APPEND_REF_MESSAGE_CONTENT_COLUMN = 17;
     public static final int MESSAGE_HAS_ATTACHMENTS_COLUMN = 18;
     public static final int MESSAGE_ATTACHMENT_LIST_URI_COLUMN = 19;
-    public static final int MESSAGE_FLAGS_COLUMN = 20;
-    public static final int MESSAGE_ALWAYS_SHOW_IMAGES_COLUMN = 21;
-    public static final int MESSAGE_READ_COLUMN = 22;
-    public static final int MESSAGE_SEEN_COLUMN = 23;
-    public static final int MESSAGE_STARRED_COLUMN = 24;
-    public static final int QUOTED_TEXT_OFFSET_COLUMN = 25;
-    public static final int MESSAGE_ATTACHMENTS_COLUMN = 26;
-    public static final int MESSAGE_CUSTOM_FROM_ADDRESS_COLUMN = 27;
-    public static final int MESSAGE_ACCOUNT_URI_COLUMN = 28;
-    public static final int MESSAGE_EVENT_INTENT_COLUMN = 29;
-    public static final int MESSAGE_SPAM_WARNING_STRING_ID_COLUMN = 30;
-    public static final int MESSAGE_SPAM_WARNING_LEVEL_COLUMN = 31;
-    public static final int MESSAGE_SPAM_WARNING_LINK_TYPE_COLUMN = 32;
-    public static final int MESSAGE_VIA_DOMAIN_COLUMN = 33;
-    public static final int MESSAGE_IS_SENDING_COLUMN = 34;
-    public static final int MESSAGE_CLIPPED_COLUMN = 35;
+    public static final int MESSAGE_ATTACHMENT_BY_CID_URI_COLUMN = 20;
+    public static final int MESSAGE_FLAGS_COLUMN = 21;
+    public static final int MESSAGE_ALWAYS_SHOW_IMAGES_COLUMN = 22;
+    public static final int MESSAGE_READ_COLUMN = 23;
+    public static final int MESSAGE_SEEN_COLUMN = 24;
+    public static final int MESSAGE_STARRED_COLUMN = 25;
+    public static final int QUOTED_TEXT_OFFSET_COLUMN = 26;
+    public static final int MESSAGE_ATTACHMENTS_COLUMN = 27;
+    public static final int MESSAGE_CUSTOM_FROM_ADDRESS_COLUMN = 28;
+    public static final int MESSAGE_ACCOUNT_URI_COLUMN = 29;
+    public static final int MESSAGE_EVENT_INTENT_COLUMN = 30;
+    public static final int MESSAGE_SPAM_WARNING_STRING_ID_COLUMN = 31;
+    public static final int MESSAGE_SPAM_WARNING_LEVEL_COLUMN = 32;
+    public static final int MESSAGE_SPAM_WARNING_LINK_TYPE_COLUMN = 33;
+    public static final int MESSAGE_VIA_DOMAIN_COLUMN = 34;
+    public static final int MESSAGE_IS_SENDING_COLUMN = 35;
+    public static final int MESSAGE_CLIPPED_COLUMN = 36;
+    public static final int MESSAGE_PERMALINK_COLUMN = 37;
 
     public static final class CursorStatus {
         // The cursor is actively loading more data
@@ -1755,6 +1751,11 @@
          */
         public static final String ATTACHMENT_LIST_URI = "attachmentListUri";
         /**
+         * This string column contains the content provider URI for the details of an attachment
+         * associated with this message. (CID to be appended at the time the URI is used)
+         */
+        public static final String ATTACHMENT_BY_CID_URI = "attachmentByCidUri";
+        /**
          * This long column is a bit field of flags defined in {@link MessageFlags}.
          */
         public static final String MESSAGE_FLAGS = "messageFlags";
@@ -1834,6 +1835,11 @@
          * This boolean column indicates whether the message body has been clipped.
          */
         public static final String CLIPPED = "clipped";
+        /**
+         * This string column contains the permalink value of the conversation
+         * for which this message belongs or null if one does not exist.
+         */
+        public static final String PERMALINK = "permalink";
 
         private MessageColumns() {}
     }
@@ -1888,7 +1894,8 @@
         AttachmentColumns.PROVIDER_DATA,
         AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN,
         AttachmentColumns.TYPE,
-        AttachmentColumns.FLAGS
+        AttachmentColumns.FLAGS,
+        AttachmentColumns.CONTENT_ID
     };
     public static final int ATTACHMENT_NAME_COLUMN = 0;
     public static final int ATTACHMENT_SIZE_COLUMN = 1;
@@ -1903,6 +1910,7 @@
     public static final int ATTACHMENT_SUPPORTS_DOWNLOAD_AGAIN_COLUMN = 10;
     public static final int ATTACHMENT_TYPE_COLUMN = 11;
     public static final int ATTACHMENT_FLAGS_COLUMN = 12;
+    public static final int ATTACHMENT_CONTENT_ID_COLUMN = 13;
 
     /** Separates attachment info parts in strings in the database. */
     public static final String ATTACHMENT_INFO_SEPARATOR = "\n"; // use to join
@@ -2083,6 +2091,12 @@
          */
         public static final String FLAGS = "flags";
 
+        /**
+         * This column holds the RFC 2392 content id of the email part for this attachment, if
+         * possible; otherwise it holds an identifier unique to the parent message.
+         */
+        public static final String CONTENT_ID = "contentId";
+
         private AttachmentColumns() {}
     }
 
@@ -2359,9 +2373,9 @@
          */
         public static final String EXTRA_ORIGINAL_URI = "original_uri";
         /**
-         * Parcelable extra passed to the proxy which indicates the account being viewed from.
+         * String extra passed to the proxy which indicates the account being viewed.
          */
-        public static final String EXTRA_ACCOUNT = "account";
+        public static final String EXTRA_ACCOUNT_NAME = "account_name";
         /**
          * String extra passed from the proxy which indicates the salt used to generate the digest.
          */
diff --git a/src/com/android/mail/providers/protos/mock/MockUiProvider.java b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
index 7a4c5e6..92fdd01 100644
--- a/src/com/android/mail/providers/protos/mock/MockUiProvider.java
+++ b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
@@ -199,7 +199,6 @@
         conversationMap.put(ConversationColumns.STARRED, 0);
         conversationMap.put(ConversationColumns.CONVERSATION_INFO,
                 generateConversationInfo(messageCount, draftCount));
-        conversationMap.put(ConversationColumns.PERMALINK, null);
         final List<Folder> folders = new ArrayList<Folder>(3);
         for (int i = 0; i < 3; i++) {
             final Folder folder = Folder.newUnsafeInstance();
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index d075ca0..0393436 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -66,6 +66,7 @@
 import com.android.mail.MailLogService;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
+import com.android.mail.analytics.AnalyticsTimer;
 import com.android.mail.analytics.AnalyticsUtils;
 import com.android.mail.browse.ConfirmDialogFragment;
 import com.android.mail.browse.ConversationCursor;
@@ -75,6 +76,7 @@
 import com.android.mail.browse.ConversationPagerController;
 import com.android.mail.browse.SelectedConversationsActionMenu;
 import com.android.mail.browse.SyncErrorDialogFragment;
+import com.android.mail.browse.UndoCallback;
 import com.android.mail.compose.ComposeActivity;
 import com.android.mail.content.CursorCreator;
 import com.android.mail.content.ObjectCursor;
@@ -177,6 +179,13 @@
     private final String BUNDLE_ACCOUNT_KEY = "account";
     /** Key to store a folder in a bundle */
     private final String BUNDLE_FOLDER_KEY = "folder";
+    /**
+     * Key to set a flag for the ConversationCursorLoader to ignore any
+     * initial load limit that may be set by the Account. Instead,
+     * perform a full load instead of the full-stage load.
+     */
+    private final String BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY =
+            "ignore-initial-conversation-limit";
 
     protected Account mAccount;
     protected Folder mFolder;
@@ -240,6 +249,8 @@
 
     private FolderWatcher mFolderWatcher;
 
+    private boolean mIgnoreInitialConversationLimit;
+
     /**
      * Interface for actions that are deferred until after a load completes. This is for handling
      * user actions which affect cursors (e.g. marking messages read or unread) that happen before
@@ -1042,6 +1053,9 @@
         final Bundle args = new Bundle(2);
         args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
         args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
+        args.putBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY,
+                mIgnoreInitialConversationLimit);
+        mIgnoreInitialConversationLimit = false;
         lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
     }
 
@@ -1376,6 +1390,52 @@
 
     public abstract boolean doesActionChangeConversationListVisibility(int action);
 
+    /**
+     * Helper function that determines if we should associate an undo callback with
+     * the current menu action item
+     * @param actionId the id of the action
+     * @return the appropriate callback handler, or null if not applicable
+     */
+    private UndoCallback getUndoCallbackForDestructiveActionsWithAutoAdvance(
+            int actionId, final Conversation conv) {
+        // We associated the undoCallback if the user is going to perform an action on the current
+        // conversation, causing the current conversation to be removed from view and replacing it
+        // with another (via Auto Advance). The undoCallback will bring the removed conversation
+        // back into the view if the action is undone.
+        final Collection<Conversation> convCol = Conversation.listOf(conv);
+        final boolean isApplicableForReshow = mAccount != null &&
+                mAccount.settings != null &&
+                mTracker != null &&
+                // ensure that we will show another conversation due to Auto Advance
+                mTracker.getNextConversation(
+                        mAccount.settings.getAutoAdvanceSetting(), convCol) != null &&
+                // ensure that we are performing the action from conversation view
+                isCurrentConversationInView(convCol) &&
+                // check for the appropriate destructive actions
+                doesActionRemoveCurrentConversationFromView(actionId);
+        return (isApplicableForReshow) ?
+            new UndoCallback() {
+                @Override
+                public void performUndoCallback() {
+                    showConversation(conv);
+                }
+            } : null;
+    }
+
+    /**
+     * Check if the provided action will remove the active conversation from view
+     * @param actionId the applied action
+     * @return true if it will remove the conversation from view, false otherwise
+     */
+    private boolean doesActionRemoveCurrentConversationFromView(int actionId) {
+        return actionId == R.id.archive ||
+                actionId == R.id.delete ||
+                actionId == R.id.remove_folder ||
+                actionId == R.id.report_spam ||
+                actionId == R.id.report_phishing ||
+                actionId == R.id.move_to;
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
 
@@ -1402,44 +1462,50 @@
         // The user is choosing a new action; commit whatever they had been
         // doing before. Don't animate if we are launching a new screen.
         commitDestructiveActions(!doesActionChangeConversationListVisibility(id));
+        final UndoCallback undoCallback = getUndoCallbackForDestructiveActionsWithAutoAdvance(
+                id, mCurrentConversation);
+
         if (id == R.id.archive) {
             final boolean showDialog = (settings != null && settings.confirmArchive);
-            confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation);
+            confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation, undoCallback);
         } else if (id == R.id.remove_folder) {
             delete(R.id.remove_folder, target,
-                    getDeferredRemoveFolder(target, mFolder, true, isBatch, true), isBatch);
+                    getDeferredRemoveFolder(target, mFolder, true, isBatch, true, undoCallback),
+                    isBatch);
         } else if (id == R.id.delete) {
             final boolean showDialog = (settings != null && settings.confirmDelete);
-            confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation);
+            confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation, undoCallback);
         } else if (id == R.id.discard_drafts) {
             // drafts are lost forever, so always confirm
             confirmAndDelete(id, target, true /* showDialog */,
-                    R.plurals.confirm_discard_drafts_conversation);
+                    R.plurals.confirm_discard_drafts_conversation, undoCallback);
         } else if (id == R.id.mark_important) {
             updateConversation(Conversation.listOf(mCurrentConversation),
                     ConversationColumns.PRIORITY, UIProvider.ConversationPriority.HIGH);
         } else if (id == R.id.mark_not_important) {
             if (mFolder != null && mFolder.isImportantOnly()) {
                 delete(R.id.mark_not_important, target,
-                        getDeferredAction(R.id.mark_not_important, target, isBatch), isBatch);
+                        getDeferredAction(R.id.mark_not_important, target, isBatch, undoCallback),
+                        isBatch);
             } else {
                 updateConversation(Conversation.listOf(mCurrentConversation),
                         ConversationColumns.PRIORITY, UIProvider.ConversationPriority.LOW);
             }
         } else if (id == R.id.mute) {
-            delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch), isBatch);
+            delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch, undoCallback),
+                    isBatch);
         } else if (id == R.id.report_spam) {
             delete(R.id.report_spam, target,
-                    getDeferredAction(R.id.report_spam, target, isBatch), isBatch);
+                    getDeferredAction(R.id.report_spam, target, isBatch, undoCallback), isBatch);
         } else if (id == R.id.mark_not_spam) {
             // Currently, since spam messages are only shown in list with
             // other spam messages,
             // marking a message not as spam is a destructive action
             delete(R.id.mark_not_spam, target,
-                    getDeferredAction(R.id.mark_not_spam, target, isBatch), isBatch);
+                    getDeferredAction(R.id.mark_not_spam, target, isBatch, undoCallback), isBatch);
         } else if (id == R.id.report_phishing) {
             delete(R.id.report_phishing, target,
-                    getDeferredAction(R.id.report_phishing, target, isBatch), isBatch);
+                    getDeferredAction(R.id.report_phishing, target, isBatch, undoCallback), isBatch);
         } else if (id == android.R.id.home) {
             onUpPressed();
         } else if (id == R.id.compose) {
@@ -1782,6 +1848,18 @@
     }
 
     /**
+     * Helper function to determine if the provided set of conversations is in view
+     * @param target set of conversations that we are interested in
+     * @return true if they are in view, false otherwise
+     */
+    private boolean isCurrentConversationInView(final Collection<Conversation> target) {
+        final int viewMode = mViewMode.getMode();
+        return (viewMode == ViewMode.CONVERSATION
+                || viewMode == ViewMode.SEARCH_RESULTS_CONVERSATION)
+                && Conversation.contains(target, mCurrentConversation);
+    }
+
+    /**
      * Auto-advance to a different conversation if the currently visible conversation in
      * conversation mode is affected (deleted, marked unread, etc.).
      *
@@ -1805,12 +1883,7 @@
      */
     private boolean showNextConversation(final Collection<Conversation> target,
             final Runnable operation) {
-        final int viewMode = mViewMode.getMode();
-        final boolean currentConversationInView = (viewMode == ViewMode.CONVERSATION
-                || viewMode == ViewMode.SEARCH_RESULTS_CONVERSATION)
-                && Conversation.contains(target, mCurrentConversation);
-
-        if (currentConversationInView) {
+        if (isCurrentConversationInView(target)) {
             final int autoAdvanceSetting = mAccount.settings.getAutoAdvanceSetting();
 
             if (autoAdvanceSetting == AutoAdvance.UNSET && mIsTablet) {
@@ -1946,16 +2019,16 @@
      * @param confirmResource the resource ID of the string that is shown in the confirmation dialog
      */
     private void confirmAndDelete(int actionId, final Collection<Conversation> target,
-            boolean showDialog, int confirmResource) {
+            boolean showDialog, int confirmResource, UndoCallback undoCallback) {
         final boolean isBatch = false;
         if (showDialog) {
-            makeDialogListener(actionId, isBatch);
+            makeDialogListener(actionId, isBatch, undoCallback);
             final CharSequence message = Utils.formatPlural(mContext, confirmResource,
                     target.size());
             final ConfirmDialogFragment c = ConfirmDialogFragment.newInstance(message);
             c.displayDialog(mActivity.getFragmentManager());
         } else {
-            delete(0, target, getDeferredAction(actionId, target, isBatch), isBatch);
+            delete(0, target, getDeferredAction(actionId, target, isBatch, undoCallback), isBatch);
         }
     }
 
@@ -2097,6 +2170,7 @@
 
     @Override
     public void executeSearch(String query) {
+        AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.SEARCH_TO_LIST);
         Intent intent = new Intent();
         intent.setAction(Intent.ACTION_SEARCH);
         intent.putExtra(ConversationListContext.EXTRA_SEARCH_QUERY, query);
@@ -2285,7 +2359,9 @@
         // If there has been an orientation change, and we need to recreate the listener for the
         // confirm dialog fragment (delete/archive/...), then do it here.
         if (mDialogAction != -1) {
-            makeDialogListener(mDialogAction, mDialogFromSelectedSet);
+            makeDialogListener(mDialogAction, mDialogFromSelectedSet,
+                    getUndoCallbackForDestructiveActionsWithAutoAdvance(
+                            mDialogAction, mCurrentConversation));
         }
 
         mInbox = savedState.getParcelable(SAVED_INBOX_KEY);
@@ -2333,7 +2409,7 @@
 
             final Uri folderUri;
             if (intent.hasExtra(Utils.EXTRA_FOLDER_URI)) {
-                folderUri = (Uri) intent.getParcelableExtra(Utils.EXTRA_FOLDER_URI);
+                folderUri = intent.getParcelableExtra(Utils.EXTRA_FOLDER_URI);
             } else if (intent.hasExtra(Utils.EXTRA_FOLDER)) {
                 final Folder folder =
                         Folder.fromString(intent.getStringExtra(Utils.EXTRA_FOLDER));
@@ -2345,6 +2421,11 @@
                 folderUri = mAccount.settings.defaultInbox;
             }
 
+            // Check if we should load all conversations instead of using
+            // the default behavior which loads an initial subset.
+            mIgnoreInitialConversationLimit =
+                    intent.getBooleanExtra(Utils.EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT, false);
+
             args.putParcelable(Utils.EXTRA_FOLDER_URI, folderUri);
             args.putParcelable(Utils.EXTRA_CONVERSATION,
                     intent.getParcelableExtra(Utils.EXTRA_CONVERSATION));
@@ -2402,20 +2483,15 @@
         mSelectedSet.putAll(selectedSet);
     }
 
-    private void showConversation(Conversation conversation) {
-        showConversation(conversation, false /* inLoaderCallbacks */);
-    }
-
     /**
      * Show the conversation provided in the arguments. It is safe to pass a null conversation
      * object, which is a signal to back out of conversation view mode.
      * Child classes must call super.showConversation() <b>before</b> their own implementations.
      * @param conversation the conversation to be shown, or null if we want to back out to list
      *                     mode.
-     * @param inLoaderCallbacks true if the method is called as a result of
      * onLoadFinished(Loader, Cursor) on any callback.
      */
-    protected void showConversation(Conversation conversation, boolean inLoaderCallbacks) {
+    protected void showConversation(Conversation conversation) {
         if (conversation != null) {
             Utils.sConvLoadTimer.start();
         }
@@ -2432,7 +2508,7 @@
     @Override
     public void showWaitForInitialization() {
         mViewMode.enterWaitingForInitializationMode();
-        mWaitFragment = WaitFragment.newInstance(mAccount);
+        mWaitFragment = WaitFragment.newInstance(mAccount, true /* expectingMessages */);
     }
 
     private void updateWaitMode() {
@@ -2499,7 +2575,7 @@
         // Only animate destructive actions if we are going to be showing the
         // conversation list when we show the next conversation.
         commitDestructiveActions(mIsTablet);
-        showConversation(conversation, inLoaderCallbacks);
+        showConversation(conversation);
     }
 
     @Override
@@ -2769,6 +2845,8 @@
         /** Whether this is an action on the currently selected set. */
         private final boolean mIsSelectedSet;
 
+        private UndoCallback mCallback;
+
         /**
          * Create a listener object.
          * @param action action is one of four constants: R.id.y_button (archive),
@@ -2782,6 +2860,11 @@
             mIsSelectedSet = isBatch;
         }
 
+        @Override
+        public void setUndoCallback(UndoCallback undoCallback) {
+            mCallback = undoCallback;
+        }
+
         /**
          * The action common to child classes. This performs the action specified in the constructor
          * on the conversations given here.
@@ -2809,10 +2892,10 @@
 
             if (mAction == R.id.archive) {
                 LogUtils.d(LOG_TAG, "Archiving");
-                mConversationListCursor.archive(mTarget);
+                mConversationListCursor.archive(mTarget, mCallback);
             } else if (mAction == R.id.delete) {
                 LogUtils.d(LOG_TAG, "Deleting");
-                mConversationListCursor.delete(mTarget);
+                mConversationListCursor.delete(mTarget, mCallback);
                 if (mFolder.supportsCapability(FolderCapabilities.DELETE_ACTION_FINAL)) {
                     undoEnabled = false;
                 }
@@ -2823,16 +2906,16 @@
                         c.localDeleteOnUpdate = true;
                     }
                 }
-                mConversationListCursor.mute(mTarget);
+                mConversationListCursor.mute(mTarget, mCallback);
             } else if (mAction == R.id.report_spam) {
                 LogUtils.d(LOG_TAG, "Reporting spam");
-                mConversationListCursor.reportSpam(mTarget);
+                mConversationListCursor.reportSpam(mTarget, mCallback);
             } else if (mAction == R.id.mark_not_spam) {
                 LogUtils.d(LOG_TAG, "Marking not spam");
-                mConversationListCursor.reportNotSpam(mTarget);
+                mConversationListCursor.reportNotSpam(mTarget, mCallback);
             } else if (mAction == R.id.report_phishing) {
                 LogUtils.d(LOG_TAG, "Reporting phishing");
-                mConversationListCursor.reportPhishing(mTarget);
+                mConversationListCursor.reportPhishing(mTarget, mCallback);
             } else if (mAction == R.id.remove_star) {
                 LogUtils.d(LOG_TAG, "Removing star");
                 // Star removal is destructive in the Starred folder.
@@ -2908,6 +2991,10 @@
             }
         }
         final DestructiveAction folderChange;
+        final UndoCallback undoCallback = isMoveTo ?
+                getUndoCallbackForDestructiveActionsWithAutoAdvance(R.id.move_to,
+                        mCurrentConversation)
+                : null;
         // Update the UI elements depending no their visibility and availability
         // TODO(viki): Consolidate this into a single method requestDelete.
         if (isDestructive) {
@@ -2940,11 +3027,11 @@
             }
 
             folderChange = getDeferredFolderChange(target, folderOps, isDestructive,
-                    batch, showUndo, isMoveTo, actionFolder);
+                    batch, showUndo, isMoveTo, actionFolder, undoCallback);
             delete(0, target, folderChange, batch);
         } else {
             folderChange = getFolderChange(target, folderOps, isDestructive,
-                    batch, showUndo, false /* isMoveTo */, mFolder);
+                    batch, showUndo, false /* isMoveTo */, mFolder, undoCallback);
             requestUpdate(folderChange);
         }
     }
@@ -2952,7 +3039,10 @@
     @Override
     public final void onRefreshRequired() {
         if (isAnimating() || isDragging()) {
-            LogUtils.i(ConversationCursor.LOG_TAG, "onRefreshRequired: delay until animating done");
+            final ConversationListFragment f = getConversationListFragment();
+            LogUtils.w(ConversationCursor.LOG_TAG,
+                    "onRefreshRequired: delay until animating done. cursor=%s adapter=%s",
+                    mConversationListCursor, (f != null) ? f.getAnimatedAdapter() : null);
             return;
         }
         // Refresh the query in the background
@@ -3012,6 +3102,11 @@
         if (!isAnimating()) {
             // Swap cursors
             mConversationListCursor.sync();
+        } else {
+            // (CLF guaranteed to be non-null due to check in isAnimating)
+            LogUtils.w(LOG_TAG,
+                    "AAC.onRefreshReady suppressing sync() due to animation. cursor=%s aa=%s",
+                    mConversationListCursor, getConversationListFragment().getAnimatedAdapter());
         }
         mTracker.onCursorUpdated();
         perhapsShowFirstSearchResult();
@@ -3072,6 +3167,10 @@
 
     @Override
     public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
+        if (animatedAdapter != null) {
+            LogUtils.i(LOG_TAG, "AAC.onAnimationEnd. cursor=%s adapter=%s", mConversationListCursor,
+                    animatedAdapter);
+        }
         if (mConversationListCursor == null) {
             LogUtils.e(LOG_TAG, "null ConversationCursor in onAnimationEnd");
             return;
@@ -3224,7 +3323,8 @@
         // current folder.
         final DestructiveAction action =
                 getFolderChange(conversations, dragDropOperations, isDestructive,
-                        true /* isBatch */, true /* showUndo */, true /* isMoveTo */, folder);
+                        true /* isBatch */, true /* showUndo */, true /* isMoveTo */, folder,
+                        null /* undoCallback */);
         if (isDestructive) {
             delete(0, conversations, action, true);
         } else {
@@ -3290,6 +3390,11 @@
         }
 
         @Override
+        public void setUndoCallback(UndoCallback undoCallback) {
+            return;     // currently not applicable
+        }
+
+        @Override
         public void performAction() {
             ToastBarOperation undoOp = new ToastBarOperation(mConversations.size(),
                     R.id.change_folders, ToastBarOperation.UNDO, true /* batch */, mInitialFolder);
@@ -3362,11 +3467,14 @@
         public Loader<ConversationCursor> onCreateLoader(int id, Bundle args) {
             final Account account = args.getParcelable(BUNDLE_ACCOUNT_KEY);
             final Folder folder = args.getParcelable(BUNDLE_FOLDER_KEY);
+            final boolean ignoreInitialConversationLimit =
+                    args.getBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY, false);
             if (account == null || folder == null) {
                 return null;
             }
-            return new ConversationCursorLoader((Activity) mActivity, account,
-                    folder.conversationListUri, folder.name);
+            return new ConversationCursorLoader(mActivity, account,
+                    folder.conversationListUri, folder.getTypeDescription(),
+                    ignoreInitialConversationLimit);
         }
 
         @Override
@@ -3733,15 +3841,16 @@
     }
 
     @Override
-    public final DestructiveAction getBatchAction(int action) {
+    public final DestructiveAction getBatchAction(int action, UndoCallback undoCallback) {
         final DestructiveAction da = new ConversationAction(action, mSelectedSet.values(), true);
+        da.setUndoCallback(undoCallback);
         registerDestructiveAction(da);
         return da;
     }
 
     @Override
-    public final DestructiveAction getDeferredBatchAction(int action) {
-        return getDeferredAction(action, mSelectedSet.values(), true);
+    public final DestructiveAction getDeferredBatchAction(int action, UndoCallback undoCallback) {
+        return getDeferredAction(action, mSelectedSet.values(), true, undoCallback);
     }
 
     /**
@@ -3754,8 +3863,10 @@
      * @return a {@link DestructiveAction} that performs the specified action.
      */
     private DestructiveAction getDeferredAction(int action, Collection<Conversation> target,
-            boolean batch) {
-        return new ConversationAction(action, target, batch);
+            boolean batch, UndoCallback callback) {
+        ConversationAction cAction = new ConversationAction(action, target, batch);
+        cAction.setUndoCallback(callback);
+        return cAction;
     }
 
     /**
@@ -3774,6 +3885,8 @@
         private final int mAction;
         private final Folder mActionFolder;
 
+        private UndoCallback mUndoCallback;
+
         /**
          * Create a new folder destruction object to act on the given conversations.
          * @param target conversations to act upon.
@@ -3792,6 +3905,11 @@
         }
 
         @Override
+        public void setUndoCallback(UndoCallback undoCallback) {
+            mUndoCallback = undoCallback;
+        }
+
+        @Override
         public void performAction() {
             if (isPerformed()) {
                 return;
@@ -3824,7 +3942,7 @@
                     }
                 }
                 ops.add(mConversationListCursor.getConversationFolderOperation(target,
-                        folderUris, adds, targetFolders.values()));
+                        folderUris, adds, targetFolders.values(), mUndoCallback));
             }
             if (mConversationListCursor != null) {
                 mConversationListCursor.updateBulkValues(ops);
@@ -3850,28 +3968,34 @@
 
     public final DestructiveAction getFolderChange(Collection<Conversation> target,
             Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
-            boolean showUndo, final boolean isMoveTo, final Folder actionFolder) {
+            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
+            UndoCallback undoCallback) {
         final DestructiveAction da = getDeferredFolderChange(target, folders, isDestructive,
-                isBatch, showUndo, isMoveTo, actionFolder);
+                isBatch, showUndo, isMoveTo, actionFolder, undoCallback);
         registerDestructiveAction(da);
         return da;
     }
 
     public final DestructiveAction getDeferredFolderChange(Collection<Conversation> target,
             Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
-            boolean showUndo, final boolean isMoveTo, final Folder actionFolder) {
-        return new FolderDestruction(target, folders, isDestructive, isBatch, showUndo,
-                isMoveTo ? R.id.move_folder : R.id.change_folders, actionFolder);
+            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
+            UndoCallback undoCallback) {
+        final DestructiveAction fd = new FolderDestruction(target, folders, isDestructive, isBatch,
+                showUndo, isMoveTo ? R.id.move_folder : R.id.change_folders, actionFolder);
+        fd.setUndoCallback(undoCallback);
+        return fd;
     }
 
     @Override
     public final DestructiveAction getDeferredRemoveFolder(Collection<Conversation> target,
             Folder toRemove, boolean isDestructive, boolean isBatch,
-            boolean showUndo) {
+            boolean showUndo, UndoCallback undoCallback) {
         Collection<FolderOperation> folderOps = new ArrayList<FolderOperation>();
         folderOps.add(new FolderOperation(toRemove, false));
-        return new FolderDestruction(target, folderOps, isDestructive, isBatch,
+        final DestructiveAction da = new FolderDestruction(target, folderOps, isDestructive, isBatch,
                 showUndo, R.id.remove_folder, mFolder);
+        da.setUndoCallback(undoCallback);
+        return da;
     }
 
     @Override
@@ -4076,7 +4200,8 @@
     }
 
     @Override
-    public void makeDialogListener (final int action, final boolean isBatch) {
+    public void makeDialogListener (final int action, final boolean isBatch,
+            UndoCallback undoCallback) {
         final Collection<Conversation> target;
         if (isBatch) {
             target = mSelectedSet.values();
@@ -4084,7 +4209,8 @@
             LogUtils.d(LOG_TAG, "Will act upon %s", mCurrentConversation);
             target = Conversation.listOf(mCurrentConversation);
         }
-        final DestructiveAction destructiveAction = getDeferredAction(action, target, isBatch);
+        final DestructiveAction destructiveAction = getDeferredAction(action, target, isBatch,
+                undoCallback);
         mDialogAction = action;
         mDialogFromSelectedSet = isBatch;
         mDialogListener = new AlertDialog.OnClickListener() {
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index 5d665d7..43113c9 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -40,7 +40,6 @@
 import com.android.mail.browse.MessageCursor.ConversationController;
 import com.android.mail.content.ObjectCursor;
 import com.android.mail.content.ObjectCursorLoader;
-import com.android.mail.preferences.AccountPreferences;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Conversation;
diff --git a/src/com/android/mail/ui/AbstractConversationWebViewClient.java b/src/com/android/mail/ui/AbstractConversationWebViewClient.java
index c08115d..2a52d36 100644
--- a/src/com/android/mail/ui/AbstractConversationWebViewClient.java
+++ b/src/com/android/mail/ui/AbstractConversationWebViewClient.java
@@ -19,21 +19,31 @@
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
 import android.provider.Browser;
+import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 
+import com.android.mail.browse.ConversationMessage;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.Attachment;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
 
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
 import java.util.List;
 
 /**
@@ -63,6 +73,66 @@
         return mActivity;
     }
 
+    /**
+     * Translates Content ID urls (CID urls) into provider queries for the associated attachment.
+     * With the attachment in hand, it's trivial to open a stream to the file containing the content
+     * of the attachment.
+     *
+     * @param uri the raw URI from the HTML document in the Webview
+     * @param message the message containing the HTML that is being rendered
+     * @return a response if a stream to the attachment file can be created from the CID URL;
+     *      <tt>null</tt> if it cannot for any reason
+     */
+    protected final WebResourceResponse loadCIDUri(Uri uri, ConversationMessage message) {
+        // if the url is not a CID url, we do nothing
+        if (!"cid".equals(uri.getScheme())) {
+            return null;
+        }
+
+        // cid urls can be translated to content urls
+        final String cid = uri.getSchemeSpecificPart();
+        if (cid == null) {
+            return null;
+        }
+
+        if (message.attachmentByCidUri == null) {
+            return null;
+        }
+
+        final Uri queryUri = Uri.withAppendedPath(message.attachmentByCidUri, cid);
+        if (queryUri == null) {
+            return null;
+        }
+
+        // query for the attachment using its cid
+        final ContentResolver cr = getActivity().getContentResolver();
+        final Cursor c = cr.query(queryUri, UIProvider.ATTACHMENT_PROJECTION, null, null, null);
+        if (c == null) {
+            return null;
+        }
+
+        // create the attachment from the cursor, if one was found
+        final Attachment target;
+        try {
+            if (!c.moveToFirst()) {
+                return null;
+            }
+            target = new Attachment(c);
+        } finally {
+            c.close();
+        }
+
+        // try to return a response that includes a stream to the attachment data
+        try {
+            final ParcelFileDescriptor fd = cr.openFileDescriptor(target.contentUri, "r");
+            final InputStream stream = new FileInputStream(fd.getFileDescriptor());
+            return new WebResourceResponse(target.getContentType(), null, stream);
+        } catch (FileNotFoundException e) {
+            // if no attachment file was found return null to let webview handle it
+            return null;
+        }
+    }
+
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
         if (mActivity == null) {
@@ -98,15 +168,21 @@
     }
 
     private Intent generateProxyIntent(Uri uri) {
-        final Intent intent = new Intent(Intent.ACTION_VIEW, mAccount.viewIntentProxyUri);
+        return generateProxyIntent(
+                mActivity, mAccount.viewIntentProxyUri, uri, mAccount.getEmailAddress());
+    }
+
+    public static Intent generateProxyIntent(
+            Context context, Uri proxyUri, Uri uri, String accountName) {
+        final Intent intent = new Intent(Intent.ACTION_VIEW, proxyUri);
         intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ORIGINAL_URI, uri);
-        intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT, mAccount);
+        intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT_NAME, accountName);
 
         PackageManager manager = null;
         // We need to catch the exception to make CanvasConversationHeaderView
         // test pass.  Bug: http://b/issue?id=3470653.
         try {
-            manager = mActivity.getPackageManager();
+            manager = context.getPackageManager();
         } catch (UnsupportedOperationException e) {
             LogUtils.e(LOG_TAG, e, "Error getting package manager");
         }
@@ -116,7 +192,7 @@
             final List<ResolveInfo> resolvedActivities = manager.queryIntentActivities(
                     intent, PackageManager.MATCH_DEFAULT_ONLY);
 
-            final String packageName = mActivity.getPackageName();
+            final String packageName = context.getPackageName();
 
             // Now try and find one that came from this package, if one is not found, the UI
             // provider must have specified an intent that is to be handled by a different apk.
diff --git a/src/com/android/mail/ui/AbstractMailActivity.java b/src/com/android/mail/ui/AbstractMailActivity.java
index ceb3d9f..717cc7b 100644
--- a/src/com/android/mail/ui/AbstractMailActivity.java
+++ b/src/com/android/mail/ui/AbstractMailActivity.java
@@ -39,8 +39,8 @@
 
     private final UiHandler mUiHandler = new UiHandler();
 
-    // ship with false
-    private static final boolean STRICT_MODE = false;
+    // STOPSHIP: ship with false
+    private static final boolean STRICT_MODE = true;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/src/com/android/mail/ui/AccountItemView.java b/src/com/android/mail/ui/AccountItemView.java
index 74b0245..f77e469 100644
--- a/src/com/android/mail/ui/AccountItemView.java
+++ b/src/com/android/mail/ui/AccountItemView.java
@@ -24,7 +24,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.bitmap.BitmapCache;
+import com.android.oldbitmap.BitmapCache;
 import com.android.mail.R;
 import com.android.mail.bitmap.AccountAvatarDrawable;
 import com.android.mail.bitmap.ContactResolver;
diff --git a/src/com/android/mail/ui/AnimatedAdapter.java b/src/com/android/mail/ui/AnimatedAdapter.java
index 866c0e5..811e636 100644
--- a/src/com/android/mail/ui/AnimatedAdapter.java
+++ b/src/com/android/mail/ui/AnimatedAdapter.java
@@ -36,9 +36,9 @@
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.SimpleCursorAdapter;
 
-import com.android.bitmap.AltBitmapCache;
-import com.android.bitmap.BitmapCache;
-import com.android.bitmap.DecodeAggregator;
+import com.android.oldbitmap.AltBitmapCache;
+import com.android.oldbitmap.BitmapCache;
+import com.android.oldbitmap.DecodeAggregator;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
 import com.android.mail.bitmap.ContactResolver;
@@ -973,6 +973,25 @@
                 || !mSwipeDeletingItems.isEmpty();
     }
 
+    /**
+     * Forcibly clear any internal state that would cause {@link #isAnimating()} to return true.
+     * Call this in times of desperation, when you really, really want to trash state and just
+     * start over.
+     */
+    public void clearAnimationState() {
+        if (!isAnimating()) {
+            return;
+        }
+
+        mUndoingItems.clear();
+        mSwipeUndoingItems.clear();
+        mFadeLeaveBehindItems.clear();
+        mDeletingItems.clear();
+        mSwipeDeletingItems.clear();
+        mAnimatingViews.clear();
+        LogUtils.w(LOG_TAG, "AA.clearAnimationState forcibly cleared state, this=%s", this);
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("{");
@@ -991,6 +1010,10 @@
         sb.append(mFadeLeaveBehindItems);
         sb.append(" mLastDeletingItems=");
         sb.append(mLastDeletingItems);
+        sb.append(" mAnimatingViews=");
+        sb.append(mAnimatingViews);
+        sb.append(" mPendingDestruction=");
+        sb.append(mPendingDestruction);
         sb.append("}");
         return sb.toString();
     }
diff --git a/src/com/android/mail/ui/AttachmentTileGrid.java b/src/com/android/mail/ui/AttachmentTileGrid.java
index 59ff704..b5fefcc 100644
--- a/src/com/android/mail/ui/AttachmentTileGrid.java
+++ b/src/com/android/mail/ui/AttachmentTileGrid.java
@@ -31,7 +31,6 @@
 import com.android.mail.photo.MailPhotoViewActivity;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Attachment;
-import com.android.mail.providers.Message;
 import com.android.mail.ui.AttachmentTile.AttachmentPreview;
 import com.android.mail.ui.AttachmentTile.AttachmentPreviewCache;
 import com.android.mail.utils.ViewUtils;
@@ -54,7 +53,7 @@
     private final HashMap<String, AttachmentPreview> mAttachmentPreviews;
     private FragmentManager mFragmentManager;
     private Account mAccount;
-    private Message mMessage;
+    private ConversationMessage mMessage;
 
     public AttachmentTileGrid(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/src/com/android/mail/ui/ButteryProgressBar.java b/src/com/android/mail/ui/ButteryProgressBar.java
index 00bf032..abf0214 100644
--- a/src/com/android/mail/ui/ButteryProgressBar.java
+++ b/src/com/android/mail/ui/ButteryProgressBar.java
@@ -161,6 +161,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        start();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        stop();
+    }
+
+    @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
 
@@ -172,16 +184,13 @@
     }
 
     private void start() {
-        if (mAnimator == null) {
+        if (getVisibility() != VISIBLE) {
             return;
         }
         mAnimator.start();
     }
 
     private void stop() {
-        if (mAnimator == null) {
-            return;
-        }
         mAnimator.cancel();
     }
 
diff --git a/src/com/android/mail/ui/ConversationCursorLoader.java b/src/com/android/mail/ui/ConversationCursorLoader.java
index 8d9df0b..f91d015 100644
--- a/src/com/android/mail/ui/ConversationCursorLoader.java
+++ b/src/com/android/mail/ui/ConversationCursorLoader.java
@@ -31,7 +31,6 @@
 public class ConversationCursorLoader extends AsyncTaskLoader<ConversationCursor> {
     private static final String TAG = "ConversationCursorLoader";
     private final Uri mUri;
-    private boolean mInitialConversationLimit;
     private final ConversationCursor mConversationCursor;
     private boolean mInit = false;
     private boolean mClosed = false;
@@ -44,15 +43,16 @@
     private static final ArrayList<ConversationCursorLoader> sLoaders =
             new ArrayList<ConversationCursorLoader>();
 
-    public ConversationCursorLoader(Activity activity, Account account, Uri uri, String name) {
+    public ConversationCursorLoader(Activity activity, Account account,
+            Uri uri, String name, boolean ignoreInitialConversationLimit) {
         super(activity);
         mUri = uri;
         mName = name;
-        mInitialConversationLimit =
+        final boolean useInitialConversationLimit = ignoreInitialConversationLimit ? false :
                 account.supportsCapability(AccountCapabilities.INITIAL_CONVERSATION_LIMIT);
         // Initialize the state of the conversation cursor
         mConversationCursor = new ConversationCursor(
-                activity, mUri, mInitialConversationLimit, name);
+                activity, mUri, useInitialConversationLimit, name);
         addLoader();
     }
 
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index 7a52d9c..2c9bf36 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -17,6 +17,7 @@
 
 package com.android.mail.ui;
 
+import android.animation.LayoutTransition;
 import android.app.Activity;
 import android.app.ListFragment;
 import android.app.LoaderManager;
@@ -27,7 +28,6 @@
 import android.os.Handler;
 import android.os.Parcelable;
 import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
-import android.text.format.DateUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,6 +39,7 @@
 import com.android.mail.ConversationListContext;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
+import com.android.mail.analytics.AnalyticsTimer;
 import com.android.mail.browse.ConversationCursor;
 import com.android.mail.browse.ConversationItemView;
 import com.android.mail.browse.ConversationItemViewModel;
@@ -96,8 +97,6 @@
      */
     private static int TIMESTAMP_UPDATE_INTERVAL = 0;
 
-    private static long NO_NEW_MESSAGE_DURATION = 1 * DateUtils.SECOND_IN_MILLIS;
-
     private ControllableActivity mActivity;
 
     // Control state.
@@ -149,19 +148,42 @@
     private ConversationUpdater mUpdater;
     /** Hash of the Conversation Cursor we last obtained from the controller. */
     private int mConversationCursorHash;
+    // The number of items in the last known ConversationCursor
+    private int mConversationCursorLastCount;
+    // State variable to keep track if we just loaded a new list, used for analytics only
+    // True if NO DATA has returned, false if we either partially or fully loaded the data
+    private boolean mInitialCursorLoading;
 
     /** Duration, in milliseconds, of the CAB mode (peek icon) animation. */
     private static long sSelectionModeAnimationDuration = -1;
-    /** The time at which we last exited CAB mode. */
-    private long mSelectionModeExitedTimestamp = -1;
+
+    // Let's ensure that we are only showing one out of the three views at once
+    private void showListView() {
+        mListView.setVisibility(View.VISIBLE);
+        mEmptyView.setVisibility(View.INVISIBLE);
+        mLoadingView.setVisibility(View.INVISIBLE);
+    }
+
+    private void showEmptyView() {
+        mEmptyView.setupEmptyView(
+                mFolder, mViewContext.searchQuery, mListAdapter.getBidiFormatter());
+        mListView.setVisibility(View.INVISIBLE);
+        mEmptyView.setVisibility(View.VISIBLE);
+        mLoadingView.setVisibility(View.INVISIBLE);
+    }
+
+    private void showLoadingView() {
+        mListView.setVisibility(View.INVISIBLE);
+        mEmptyView.setVisibility(View.INVISIBLE);
+        mLoadingView.setVisibility(View.VISIBLE);
+    }
 
     private final Runnable mLoadingViewRunnable = new FragmentRunnable("LoadingRunnable", this) {
         @Override
         public void go() {
-            if (isLoadingAndEmpty()) {
-                mLoadingView.setVisibility(View.VISIBLE);
-                mEmptyView.setVisibility(View.GONE);
+            if (!isCursorReadyToShow()) {
                 mCanTakeDownLoadingView = false;
+                showLoadingView();
                 mHandler.removeCallbacks(mHideLoadingRunnable);
                 mHandler.postDelayed(mHideLoadingRunnable, MINIMUM_LOADING_DURATION);
             }
@@ -173,12 +195,14 @@
         @Override
         public void go() {
             mCanTakeDownLoadingView = true;
-            if (!isLoadingAndEmpty()) {
+            if (isCursorReadyToShow()) {
                 hideLoadingViewAndShowContents();
             }
         }
     };
 
+    // Keep track of if we are waiting for the loading view. This variable is also used to check
+    // if the cursor corresponding to the current folder loaded (either partially or completely).
     private boolean mLoadingViewPending;
     private boolean mCanTakeDownLoadingView;
 
@@ -242,12 +266,7 @@
         // needs to reside in a separate class that handles the text view in
         // isolation. Then,
         // that logic can be reused in other fragments.
-        if (showHeader) {
-            mSearchStatusTextView.setText(res.getString(R.string.search_results_searching_header));
-            // Initially reset the count
-            mSearchResultCountTextView.setText("");
-        }
-        mSearchStatusView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
+        mSearchStatusView.setVisibility(showHeader ? View.INVISIBLE : View.GONE);
         int paddingTop = showHeader ? (int) res.getDimension(R.dimen.notification_view_height) : 0;
         mListView.setPadding(mListView.getPaddingLeft(), paddingTop, mListView.getPaddingRight(),
                 mListView.getPaddingBottom());
@@ -268,6 +287,7 @@
             mSearchStatusTextView.setText(res.getString(R.string.search_results_header));
             mSearchResultCountTextView
                     .setText(res.getString(R.string.search_results_loaded, count));
+            mSearchStatusView.setVisibility(View.VISIBLE);
         }
     }
 
@@ -471,6 +491,15 @@
         mListView.setListItemSwipedListener(this);
         mListView.setSwipeListener(this);
 
+        // enable animateOnLayout (equivalent of setLayoutTransition) only for >=JB (b/14302062)
+        if (Utils.isRunningJellybeanOrLater()) {
+            ((ViewGroup) rootView.findViewById(R.id.conversation_list_parent_frame))
+                    .setLayoutTransition(new LayoutTransition());
+        }
+
+        // By default let's show the list view
+        showListView();
+
         if (savedState != null && savedState.containsKey(LIST_STATE_KEY)) {
             mListView.onRestoreInstanceState(savedState.getParcelable(LIST_STATE_KEY));
         }
@@ -602,6 +631,7 @@
                     // this is a peek.
                     Analytics.getInstance().sendEvent("peek", null, null, mSelectedSet.size());
                 }
+                AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.OPEN_CONV_VIEW_FROM_LIST);
                 viewConversation(position);
             }
         } else {
@@ -620,6 +650,13 @@
     public void onResume() {
         super.onResume();
 
+        if (!isCursorReadyToShow()) {
+            // If the cursor got reset, let's reset the analytics state variable and show the list
+            // view since we are waiting for load again
+            mInitialCursorLoading = true;
+            showListView();
+        }
+
         final ConversationCursor conversationCursor = getConversationListCursor();
         if (conversationCursor != null) {
             conversationCursor.handleNotificationActions();
@@ -680,8 +717,14 @@
 
     public boolean isAnimating() {
         final AnimatedAdapter adapter = getAnimatedAdapter();
-        return (adapter != null && adapter.isAnimating()) ||
-                (mListView != null && mListView.isScrolling());
+        if (adapter != null && adapter.isAnimating()) {
+            return true;
+        }
+        final boolean isScrolling = (mListView != null && mListView.isScrolling());
+        if (isScrolling) {
+            LogUtils.i(LOG_TAG, "CLF.isAnimating=true due to scrolling");
+        }
+        return isScrolling;
     }
 
     private void clearChoicesAndActivated() {
@@ -697,7 +740,7 @@
      * must be called on the UI thread.
      */
     private void showList() {
-        mListView.setEmptyView(null);
+        mInitialCursorLoading = true;
         onFolderUpdated(mActivity.getFolderController().getFolder());
         onConversationListStatusUpdated();
 
@@ -833,6 +876,17 @@
     }
 
     public void onFolderUpdated(Folder folder) {
+        if (!isCursorReadyToShow()) {
+            // Wait a bit before showing either the empty or loading view. If the messages are
+            // actually local, it's disorienting to see this appear on every folder transition.
+            // If they aren't, then it will likely take more than 200 milliseconds to load, and
+            // then we'll see the loading view.
+            if (!mLoadingViewPending) {
+                mHandler.postDelayed(mLoadingViewRunnable, LOADING_DELAY_MS);
+                mLoadingViewPending = true;
+            }
+        }
+
         mFolder = folder;
         setSwipeAction();
 
@@ -848,85 +902,40 @@
             mErrorListener.onError(mFolder, false);
         }
 
-        // Notify of changes to the Folder.
-        onFolderStatusUpdated();
+        // Update the sync status bar with sync results if needed
+        checkSyncStatus();
 
         // Blow away conversation items cache.
         ConversationItemViewModel.onFolderUpdated(mFolder);
     }
 
-    private boolean isLoadingAndEmpty() {
-        final ConversationCursor cursor = getConversationListCursor();
-        if (cursor == null) {
-            return true;
-        } else {
-            final Bundle extras = cursor.getExtras();
-            final int cursorStatus = extras.getInt(UIProvider.CursorExtraKeys.EXTRA_STATUS);
-            return(UIProvider.CursorStatus.isWaitingForResults(cursorStatus) &&
-                    cursor.getCount() == 0);
-        }
-    }
     /**
      * Updates the footer visibility and updates the conversation cursor
      */
     public void onConversationListStatusUpdated() {
-        if (isLoadingAndEmpty()) {
-            // Wait a bit before showing either the empty or loading view. If the messages are
-            // actually local, it's disorienting to see this appear on every folder transition.
-            // If they aren't, then it will likely take more than 200 milliseconds to load, and
-            // then we'll see the loading view.
-            if (!mLoadingViewPending) {
-                mHandler.postDelayed(mLoadingViewRunnable, LOADING_DELAY_MS);
-                mLoadingViewPending = true;
-            }
-
-        } else {
-            if (mCanTakeDownLoadingView) {
-                hideLoadingViewAndShowContents();
-            }
-        }
-
         // Also change the cursor here.
         onCursorUpdated();
+
+        if (isCursorReadyToShow() && mCanTakeDownLoadingView) {
+            hideLoadingViewAndShowContents();
+        }
     }
 
     private void hideLoadingViewAndShowContents() {
-        mLoadingView.setVisibility(View.GONE);
         final ConversationCursor cursor = getConversationListCursor();
+        if (cursor != null && cursor.getCount() == 0) {
+            showEmptyView();
+        } else {
+            showListView();
+        }
         final boolean showFooter = mFooterView.updateStatus(cursor);
-        // Update the folder status, in case the cursor could affect it.
-        onFolderStatusUpdated();
+        // Update the sync status bar with sync results if needed
+        checkSyncStatus();
         mListAdapter.setFooterVisibility(showFooter);
         mLoadingViewPending = false;
         mHandler.removeCallbacks(mLoadingViewRunnable);
     }
 
-    private void onFolderStatusUpdated() {
-        // Update the sync status bar with sync results if needed
-        checkSyncStatus();
-
-        final ConversationCursor cursor = getConversationListCursor();
-        Bundle extras = cursor != null ? cursor.getExtras() : Bundle.EMPTY;
-        int errorStatus = extras.containsKey(UIProvider.CursorExtraKeys.EXTRA_ERROR) ?
-                extras.getInt(UIProvider.CursorExtraKeys.EXTRA_ERROR)
-                : UIProvider.LastSyncResult.SUCCESS;
-        int cursorStatus = extras.getInt(UIProvider.CursorExtraKeys.EXTRA_STATUS);
-        // We want to update the UI with this information if either we are loaded or complete, or
-        // we have a folder with a non-0 count.
-        final int folderCount = mFolder != null ? mFolder.totalCount : 0;
-        if (errorStatus == UIProvider.LastSyncResult.SUCCESS
-                && (cursorStatus == UIProvider.CursorStatus.LOADED
-                || cursorStatus == UIProvider.CursorStatus.COMPLETE) || folderCount > 0) {
-            updateSearchResultHeader(folderCount);
-            if (folderCount == 0) {
-                mEmptyView.setupEmptyView(
-                        mFolder, mViewContext.searchQuery, mListAdapter.getBidiFormatter());
-                mListView.setEmptyView(mEmptyView);
-                mLoadingView.setVisibility(View.GONE);
-            }
-        }
-    }
-
     private void setSwipeAction() {
         int swipeSetting = Settings.getSwipeSetting(mAccount.settings);
         if (swipeSetting == Swipe.DISABLED
@@ -936,16 +945,18 @@
         } else {
             final int action;
             mListView.enableSwipe(true);
-            if (ConversationListContext.isSearchResult(mViewContext)
-                    || (mFolder != null && mFolder.isType(UIProvider.FolderType.SPAM))) {
-                action = R.id.delete;
-            } else if (mFolder == null) {
+            if (mFolder == null) {
                 action = R.id.remove_folder;
             } else {
-                // We have enough information to respect user settings.
                 switch (swipeSetting) {
+                    // Try to respect user's setting as best as we can and default to doing nothing
+                    case Swipe.DELETE:
+                        action = R.id.delete;
+                        break;
                     case Swipe.ARCHIVE:
-                        if (mAccount.supportsCapability(AccountCapabilities.ARCHIVE)) {
+                        // Special case spam since it shouldn't remove spam folder label on swipe
+                        if (mAccount.supportsCapability(AccountCapabilities.ARCHIVE)
+                                && !mFolder.isSpam()) {
                             if (mFolder.supportsCapability(FolderCapabilities.ARCHIVE)) {
                                 action = R.id.archive;
                                 break;
@@ -958,12 +969,12 @@
 
                         /*
                          * If we get here, we don't support archive, on either the account or the
-                         * folder, so we want to fall through into the delete case.
+                         * folder, so we want to fall through to swipe doing nothing
                          */
                         //$FALL-THROUGH$
-                    case Swipe.DELETE:
                     default:
-                        action = R.id.delete;
+                        mListView.enableSwipe(false);
+                        action = 0; // Use default value so setSwipeAction essentially has no effect
                         break;
                 }
             }
@@ -1000,9 +1011,14 @@
         }
         mConversationCursorHash = newCursorHash;
 
-        if (newCursor != null && newCursor.getCount() > 0) {
-            newCursor.markContentsSeen();
-            restoreLastScrolledPosition();
+        updateAnalyticsData(newCursor);
+        if (newCursor != null) {
+            final int newCursorCount = newCursor.getCount();
+            updateSearchResultHeader(newCursorCount);
+            if (newCursorCount > 0) {
+                newCursor.markContentsSeen();
+                restoreLastScrolledPosition();
+            }
         }
 
         // If a current conversation is available, and none is selected in the list, then ask
@@ -1063,7 +1079,6 @@
 
         @Override
         public void onSetEmpty() {
-            mSelectionModeExitedTimestamp = System.currentTimeMillis();
             mSwipeRefreshWidget.setEnabled(true);
         }
 
@@ -1108,5 +1123,72 @@
 
         // This will call back to showSyncStatusBar():
         mActivity.getFolderController().requestFolderRefresh();
+
+        // Clear list adapter state out of an abundance of caution.
+        // There is a class of bugs where an animation that should have finished doesn't (maybe
+        // it didn't start, or it didn't finish), and the list gets stuck pretty much forever.
+        // Clearing the state here is in line with user expectation for 'refresh'.
+        getAnimatedAdapter().clearAnimationState();
+        // possibly act on the now-cleared state
+        mActivity.onAnimationEnd(mListAdapter);
+    }
+
+    /**
+     * Extracted function that handles Analytics state and logging updates for each new cursor
+     * @param newCursor the new cursor pointer
+     */
+    private void updateAnalyticsData(ConversationCursor newCursor) {
+        if (newCursor != null) {
+            // Check if the initial data returned yet
+            if (mInitialCursorLoading) {
+                // This marks the very first time the cursor with the data the user sees returned.
+                // We either have a cursor in LOADING state with cursor's count > 0, OR the cursor
+                // completed loading.
+                // Use this point to log the appropriate timing information that depends on when
+                // the conversation list view finishes loading
+                if (isCursorReadyToShow()) {
+                    if (newCursor.getCount() == 0) {
+                        Analytics.getInstance().sendEvent("empty_state", "post_label_change",
+                                mFolder.getTypeDescription(), 0);
+                    }
+                    AnalyticsTimer.getInstance().logDuration(AnalyticsTimer.COLD_START_LAUNCHER,
+                            true /* isDestructive */, "cold_start_to_list", "from_launcher", null);
+                    // Don't need null checks because the activity, controller, and folder cannot
+                    // be null in this case
+                    if (mActivity.getFolderController().getFolder().isSearch()) {
+                        AnalyticsTimer.getInstance().logDuration(AnalyticsTimer.SEARCH_TO_LIST,
+                                true /* isDestructive */, "search_to_list", null, null);
+                    }
+
+                    mInitialCursorLoading = false;
+                }
+            } else {
+                // Log the appropriate events that happen after the initial cursor is loaded
+                if (newCursor.getCount() == 0 && mConversationCursorLastCount > 0) {
+                    Analytics.getInstance().sendEvent("empty_state", "post_delete",
+                            mFolder.getTypeDescription(), 0);
+                }
+            }
+
+            // We save the count here because for folders that are empty, multiple successful
+            // cursor loads will occur with size of 0. Thus we don't want to emit any false
+            // positive post_delete events.
+            mConversationCursorLastCount = newCursor.getCount();
+        } else {
+            mConversationCursorLastCount = 0;
+        }
+    }
+
+    /**
+     * Helper function to determine if the current cursor is ready to populate the UI
+     * @return
+     */
+    private boolean isCursorReadyToShow() {
+        ConversationCursor cursor = getConversationListCursor();
+        if (cursor == null) {
+            return false;
+        }
+        final int status = cursor.getExtras().getInt(UIProvider.CursorExtraKeys.EXTRA_STATUS);
+        return (cursor.getCount() > 0 || !UIProvider.CursorStatus.isWaitingForResults(status));
     }
 }
diff --git a/src/com/android/mail/ui/ConversationUpdater.java b/src/com/android/mail/ui/ConversationUpdater.java
index c3b71af..da2204c 100644
--- a/src/com/android/mail/ui/ConversationUpdater.java
+++ b/src/com/android/mail/ui/ConversationUpdater.java
@@ -24,6 +24,7 @@
 import com.android.mail.browse.ConfirmDialogFragment;
 import com.android.mail.browse.ConversationCursor;
 import com.android.mail.browse.ConversationMessage;
+import com.android.mail.browse.UndoCallback;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.ConversationInfo;
 import com.android.mail.providers.Folder;
@@ -122,7 +123,7 @@
      * @param action
      * @return
      */
-    public DestructiveAction getBatchAction(int action);
+    public DestructiveAction getBatchAction(int action, UndoCallback undoCallback);
 
     /**
      * Get a destructive action for selected conversations. The action corresponds to Menu item
@@ -130,17 +131,22 @@
      * @param action
      * @return
      */
-    public DestructiveAction getDeferredBatchAction(int action);
+    public DestructiveAction getDeferredBatchAction(int action, UndoCallback undoCallback);
 
     /**
      * Get destructive folder change for selected conversations.
      * The caller must explicitly call performAction.
-     * @param action
+     * @param target
+     * @param toRemove
+     * @param isDestructive
+     * @param isBatch
+     * @param showUndo
+     * @param undoCallback
      * @return
      */
     public DestructiveAction getDeferredRemoveFolder(Collection<Conversation> target,
             Folder toRemove, boolean isDestructive, boolean isBatch,
-            boolean showUndo);
+            boolean showUndo, UndoCallback undoCallback);
 
     /**
      * Assign the target conversations to the given folders, and remove them from all other folders
@@ -175,8 +181,11 @@
      * @param action the resource ID of the menu action: R.id.delete, for example
      * @param fromSelectedSet true if the listener acts on the selected set, false if the listener
      *        acts on the current conversation.
+     * @param undoCallback the appropriate callback (if any) that needs to be run when this
+     *        specific action is undone
      */
-    public void makeDialogListener(final int action, boolean fromSelectedSet);
+    public void makeDialogListener(final int action, boolean fromSelectedSet,
+            UndoCallback undoCallback);
 
     /**
      * If set, get the listener associated with the existing {@link ConfirmDialogFragment}.  This
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index 623fdb8..262d4fe 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -47,6 +47,8 @@
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
+import com.android.mail.analytics.AnalyticsTimer;
+import com.android.mail.browse.AttachmentActionHandler;
 import com.android.mail.browse.ConversationContainer;
 import com.android.mail.browse.ConversationContainer.OverlayPosition;
 import com.android.mail.browse.ConversationMessage;
@@ -62,6 +64,7 @@
 import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreatorHolder;
 import com.android.mail.browse.MailWebView.ContentSizeChangeListener;
 import com.android.mail.browse.MessageCursor;
+import com.android.mail.browse.MessageFooterView;
 import com.android.mail.browse.MessageHeaderView;
 import com.android.mail.browse.ScrollIndicatorsView;
 import com.android.mail.browse.SuperCollapsedBlock;
@@ -93,7 +96,8 @@
  */
 public class ConversationViewFragment extends AbstractConversationViewFragment implements
         SuperCollapsedBlock.OnClickListener, OnLayoutChangeListener,
-        MessageHeaderView.MessageHeaderViewCallbacks, WebViewContextMenu.Callbacks {
+        MessageHeaderView.MessageHeaderViewCallbacks,
+        MessageFooterView.MessageFooterCallbacks, WebViewContextMenu.Callbacks {
 
     private static final String LOG_TAG = LogTag.getLogTag();
     public static final String LAYOUT_TAG = "ConvLayout";
@@ -126,7 +130,7 @@
 
     protected ConversationWebView mWebView;
 
-  private ConversationViewProgressController mProgressController;
+    private ConversationViewProgressController mProgressController;
 
     private Button mNewMessageBar;
 
@@ -271,7 +275,7 @@
         final FormattedDateBuilder dateBuilder = new FormattedDateBuilder(context);
 
         mAdapter = new ConversationViewAdapter(mActivity, this,
-                getLoaderManager(), this, getContactInfoSource(), this,
+                getLoaderManager(), this, this, getContactInfoSource(), this,
                 this, mAddressCache, dateBuilder, mBidiFormatter);
         mConversationContainer.setOverlayAdapter(mAdapter);
 
@@ -399,7 +403,7 @@
         final WebSettings settings = mWebView.getSettings();
 
         final ScrollIndicatorsView scrollIndicators =
-            (ScrollIndicatorsView) rootView.findViewById(R.id.scroll_indicators);
+                (ScrollIndicatorsView) rootView.findViewById(R.id.scroll_indicators);
         scrollIndicators.setSourceView(mWebView);
 
         settings.setJavaScriptEnabled(true);
@@ -447,6 +451,10 @@
     public void onDestroyView() {
         super.onDestroyView();
         mConversationContainer.setOverlayAdapter(null);
+        // AdViewFragment has no conversation and will crash.
+        if (mConversation != null) {
+            AttachmentActionHandler.unregisterDismissListeners(mConversation.uri);
+        }
         mAdapter = null;
         resetLoadWaiting(); // be sure to unregister any active load observer
         mViewsCreated = false;
@@ -619,6 +627,10 @@
     private void revealConversation() {
         timerMark("revealing conversation");
         mProgressController.dismissLoadingStatus(mOnProgressDismiss);
+        if (isUserVisible()) {
+            AnalyticsTimer.getInstance().logDuration(AnalyticsTimer.OPEN_CONV_VIEW_FROM_LIST,
+                    true /* isDestructive */, "open_conversation", "from_list", null);
+        }
     }
 
     private boolean isLoadWaiting() {
@@ -881,7 +893,7 @@
             final MessageHeaderItem header = ConversationViewAdapter.newMessageHeaderItem(
                     mAdapter, mAdapter.getDateBuilder(), msg, false /* expanded */,
                     alwaysShowImages || mViewState.getShouldShowImages(msg));
-            final MessageFooterItem footer = mAdapter.newMessageFooterItem(header);
+            final MessageFooterItem footer = mAdapter.newMessageFooterItem(mAdapter, header);
 
             final int headerPx = measureOverlayHeight(header);
             final int footerPx = measureOverlayHeight(footer);
@@ -928,7 +940,7 @@
      * @param convItem adapter item with data to render and measure
      * @return height of the rendered view in screen px
      */
-    private int measureOverlayHeight(ConversationOverlayItem convItem) {
+    public int measureOverlayHeight(ConversationOverlayItem convItem) {
         final int type = convItem.getType();
 
         final View convertView = mConversationContainer.getScrapView(type);
@@ -1029,6 +1041,11 @@
         return (domId == null) ? null : mMessageTransforms.get(domId);
     }
 
+    @Override
+    public boolean isSecure() {
+        return false;
+    }
+
     // END message header callbacks
 
     @Override
@@ -1116,7 +1133,7 @@
     }
 
     @Override
-    public Message getMessageForClickedUrl(String url) {
+    public ConversationMessage getMessageForClickedUrl(String url) {
         final String domMessageId = mUrlToMessageIdMap.get(url);
         if (domMessageId == null) {
             return null;
@@ -1485,10 +1502,12 @@
             final ConversationMessage newMsg = newCursor.getMessage();
             final ConversationMessage oldMsg = oldCursor.getMessage();
 
-            if (!TextUtils.equals(newMsg.getFrom(), oldMsg.getFrom()) ||
-                    newMsg.isSending != oldMsg.isSending) {
+            // We are going to update the data in the adapter whenever any input fields change.
+            // This ensures that the Message object that ComposeActivity uses will be correctly
+            // aligned with the most up-to-date data.
+            if (!newMsg.isEqual(oldMsg)) {
                 mAdapter.updateItemsForMessage(newMsg, changedOverlayPositions);
-                LogUtils.i(LOG_TAG, "msg #%d (%d): detected from/sending change. isSending=%s",
+                LogUtils.i(LOG_TAG, "msg #%d (%d): detected field(s) change. isSending=%s",
                         pos, newMsg.id, newMsg.isSending);
             }
 
@@ -1626,6 +1645,31 @@
         mDiff = (expanded ? 1 : -1) * Math.abs(i.getHeight() - heightBefore);
     }
 
+    // START MessageFooterCallbacks
+
+    @Override
+    public void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight) {
+        mConversationContainer.invalidateSpacerGeometry();
+
+        // update message HTML spacer height
+        final int h = mWebView.screenPxToWebPx(newSpacerHeight);
+        LogUtils.i(LAYOUT_TAG, "setting HTML spacer h=%dwebPx (%dscreenPx)", h, newSpacerHeight);
+        mWebView.loadUrl(String.format("javascript:setMessageFooterSpacerHeight('%s', %s);",
+                mTemplates.getMessageDomId(item.getHeaderItem().getMessage()), h));
+    }
+
+    @Override
+    public MessageFooterView getViewForItem(MessageFooterItem item) {
+        return (MessageFooterView) mConversationContainer.getViewForItem(item);
+    }
+
+    @Override
+    public int getUpdatedHeight(MessageFooterItem item) {
+        return measureOverlayHeight(item);
+    }
+
+    // END MessageFooterCallbacks
+
     /**
      * @return {@code true} because either the Print or Print All menu item is shown in GMail
      */
diff --git a/src/com/android/mail/ui/DestructiveAction.java b/src/com/android/mail/ui/DestructiveAction.java
index acec6be..515fa6c 100644
--- a/src/com/android/mail/ui/DestructiveAction.java
+++ b/src/com/android/mail/ui/DestructiveAction.java
@@ -18,6 +18,7 @@
 package com.android.mail.ui;
 
 import com.android.mail.browse.SelectedConversationsActionMenu;
+import com.android.mail.browse.UndoCallback;
 
 /**
  * Interface for listening to completed UI actions like Archive, Delete, star, etc.
@@ -27,6 +28,12 @@
  */
 public interface DestructiveAction {
     /**
+     * Sets the UndoCallback if the destructive action is undone
+     * Note that this needs to be set before performAction is called for the callback to be
+     * properly associated
+     */
+    public void setUndoCallback(UndoCallback undoCallback);
+    /**
      * Performs the destructive action.
      */
     public void performAction();
diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java
index 3d18de1..ac052be 100644
--- a/src/com/android/mail/ui/FolderListFragment.java
+++ b/src/com/android/mail/ui/FolderListFragment.java
@@ -39,8 +39,8 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.bitmap.AltBitmapCache;
-import com.android.bitmap.BitmapCache;
+import com.android.oldbitmap.AltBitmapCache;
+import com.android.oldbitmap.BitmapCache;
 import com.android.mail.R;
 import com.android.mail.adapter.DrawerItem;
 import com.android.mail.analytics.Analytics;
diff --git a/src/com/android/mail/ui/FolderSelectorAdapter.java b/src/com/android/mail/ui/FolderSelectorAdapter.java
index 378a1a5..f8fa011 100644
--- a/src/com/android/mail/ui/FolderSelectorAdapter.java
+++ b/src/com/android/mail/ui/FolderSelectorAdapter.java
@@ -117,6 +117,11 @@
         }
         final List<FolderRow> allFolders = new ArrayList<FolderRow>(folders.getCount());
 
+        // Rows corresponding to user created, unchecked folders.
+        final List<FolderRow> userUnselected = new ArrayList<FolderRow>();
+        // Rows corresponding to system created, unchecked folders.
+        final List<FolderRow> systemUnselected = new ArrayList<FolderRow>();
+
         if (folders.moveToFirst()) {
             do {
                 final Folder folder = new Folder(folders);
@@ -125,16 +130,17 @@
                         folder.folderUri.getComparisonUri().toString());
                 final FolderRow row = new FolderRow(folder, isSelected);
                 allFolders.add(row);
+
+                // Add system folders here since we want the original unsorted order (for now..)
+                if (!row.isPresent() && meetsRequirements(folder) &&
+                        !Objects.equal(folder, mExcludedFolder) && folder.isProviderFolder()) {
+                    systemUnselected.add(row);
+                }
             } while (folders.moveToNext());
         }
         // Need to do the foldersort first with all folders present to avoid dropping orphans
         folderSort(allFolders);
 
-        // Rows corresponding to user created, unchecked folders.
-        final List<FolderRow> userUnselected = new ArrayList<FolderRow>();
-        // Rows corresponding to system created, unchecked folders.
-        final List<FolderRow> systemUnselected = new ArrayList<FolderRow>();
-
         // Divert the folders to the appropriate sections
         for (final FolderRow row : allFolders) {
             final Folder folder = row.getFolder();
@@ -142,9 +148,7 @@
                 // Add the currently selected first.
                 if (row.isPresent()) {
                     mFolderRows.add(row);
-                } else if (folder.isProviderFolder()) {
-                    systemUnselected.add(row);
-                } else {
+                } else if (!folder.isProviderFolder()) {
                     userUnselected.add(row);
                 }
             }
diff --git a/src/com/android/mail/ui/HierarchicalFolderSelectorAdapter.java b/src/com/android/mail/ui/HierarchicalFolderSelectorAdapter.java
index bd21c79..49fd81a 100644
--- a/src/com/android/mail/ui/HierarchicalFolderSelectorAdapter.java
+++ b/src/com/android/mail/ui/HierarchicalFolderSelectorAdapter.java
@@ -38,6 +38,7 @@
 
     private Context mContext;
 
+    @VisibleForTesting
     public HierarchicalFolderSelectorAdapter(Context context, Cursor folders,
             Set<String> initiallySelected, int layout, String header) {
         super(context, folders, initiallySelected, layout, header);
diff --git a/src/com/android/mail/ui/HtmlConversationTemplates.java b/src/com/android/mail/ui/HtmlConversationTemplates.java
index b530b8e..4029886 100644
--- a/src/com/android/mail/ui/HtmlConversationTemplates.java
+++ b/src/com/android/mail/ui/HtmlConversationTemplates.java
@@ -127,10 +127,14 @@
      *
      */
     public static String wrapMessageBody(String msgBody) {
-        final StringBuilder sb = new StringBuilder("<div style=\"display: table-cell;\">");
-        sb.append(msgBody);
-        sb.append("</div>");
-        return sb.toString();
+        // FIXME: this breaks RTL for an as-yet undetermined reason. b/13678928
+        // no-op for now.
+        return msgBody;
+
+//        final StringBuilder sb = new StringBuilder("<div style=\"display: table-cell;\">");
+//        sb.append(msgBody);
+//        sb.append("</div>");
+//        return sb.toString();
     }
 
     public void appendMessageHtml(HtmlMessage message, boolean isExpanded,
@@ -209,7 +213,7 @@
         append(sConversationLower, contentReadyClass, mContext.getString(R.string.hide_elided),
                 showElided, docBaseUri, conversationBaseUri, viewportWidth, webviewWidth,
                 enableContentReadySignal, normalizeMessageWidths,
-                enableMungeTables, enableMungeImages);
+                enableMungeTables, enableMungeImages, Utils.isRunningKitkatOrLater());
 
         mInProgress = false;
 
diff --git a/src/com/android/mail/ui/MailActionBarView.java b/src/com/android/mail/ui/MailActionBarView.java
index 5c48a17..64a1238 100644
--- a/src/com/android/mail/ui/MailActionBarView.java
+++ b/src/com/android/mail/ui/MailActionBarView.java
@@ -367,6 +367,31 @@
         }
     }
 
+    /**
+     * Helper function to ensure that the menu items that are prone to variable changes and race
+     * conditions are properly set to the correct visibility
+     */
+    public void validateVolatileMenuOptionVisibility() {
+        if (mFolderSettingsItem != null) {
+            mFolderSettingsItem.setVisible(mFolder != null
+                    && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS));
+        }
+        if (mEmptyTrashItem != null) {
+            mEmptyTrashItem.setVisible(mAccount != null && mFolder != null
+                    && mAccount.supportsCapability(AccountCapabilities.EMPTY_TRASH)
+                    && mFolder.isTrash() && mFolder.totalCount > 0
+                    && (mController.getConversationListCursor() == null
+                    || mController.getConversationListCursor().getCount() > 0));
+        }
+        if (mEmptySpamItem != null) {
+            mEmptySpamItem.setVisible(mAccount != null && mFolder != null
+                    && mAccount.supportsCapability(AccountCapabilities.EMPTY_SPAM)
+                    && mFolder.isType(FolderType.SPAM) && mFolder.totalCount > 0
+                    && (mController.getConversationListCursor() == null
+                    || mController.getConversationListCursor().getCount() > 0));
+        }
+    }
+
     public boolean onPrepareOptionsMenu(Menu menu) {
         // We start out with every option enabled. Based on the current view, we disable actions
         // that are possible.
@@ -403,20 +428,7 @@
             }
             return false;
         }
-        if (mFolderSettingsItem != null) {
-            mFolderSettingsItem.setVisible(mFolder != null
-                    && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS));
-        }
-        if (mEmptyTrashItem != null) {
-            mEmptyTrashItem.setVisible(mAccount != null && mFolder != null
-                    && mAccount.supportsCapability(AccountCapabilities.EMPTY_TRASH)
-                    && mFolder.isTrash() && mFolder.totalCount > 0);
-        }
-        if (mEmptySpamItem != null) {
-            mEmptySpamItem.setVisible(mAccount != null && mFolder != null
-                    && mAccount.supportsCapability(AccountCapabilities.EMPTY_SPAM)
-                    && mFolder.isType(FolderType.SPAM) && mFolder.totalCount > 0);
-        }
+        validateVolatileMenuOptionVisibility();
 
         switch (getMode()) {
             case ViewMode.CONVERSATION:
@@ -654,7 +666,7 @@
         // what is in the text field
         String queryText = mSearchWidget.getQuery().toString();
         // What the suggested query is
-        String query = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY));
+        String query = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
         // If the text the user typed in is a prefix of what is in the search
         // widget suggestion query, just take the search widget suggestion
         // query. Otherwise, it is a suffix and we want to remove matching
@@ -767,6 +779,8 @@
         if (changingFolders && !ConversationListContext.isSearchResult(listContext)) {
             closeSearchField();
         }
+        // make sure that we re-validate the optional menu items
+        validateVolatileMenuOptionVisibility();
     }
 
     @Override
diff --git a/src/com/android/mail/ui/MailActivity.java b/src/com/android/mail/ui/MailActivity.java
index 802511f..6f33b7d 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -31,9 +31,9 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.mail.analytics.AnalyticsTimer;
 import com.android.mail.compose.ComposeActivity;
 import com.android.mail.providers.Folder;
 import com.android.mail.utils.StorageLowState;
@@ -138,6 +138,12 @@
     @Override
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
+        // Log the start time if this is launched from the launcher with no saved states
+        Intent i = getIntent();
+        if (i != null && i.getCategories() != null &&
+                i.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+            AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.COLD_START_LAUNCHER);
+        }
 
         mViewMode = new ViewMode();
         final boolean tabletUi = Utils.useTabletUI(this.getResources());
@@ -353,17 +359,6 @@
         return mPendingToastOp;
     }
 
-    /**
-     * Sole purpose of this method is to stop clicks where we don't want them in
-     * the action bar. This can be hooked as a listener to view with
-     * android:onClick.
-     *
-     * @param v
-     */
-    public void doNothingClickHandler(View v) {
-        // Do nothing.
-    }
-
     @Override
     public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
         mController.onAnimationEnd(animatedAdapter);
diff --git a/src/com/android/mail/ui/MailboxSelectionActivity.java b/src/com/android/mail/ui/MailboxSelectionActivity.java
index 2f9253e..e81fbce 100644
--- a/src/com/android/mail/ui/MailboxSelectionActivity.java
+++ b/src/com/android/mail/ui/MailboxSelectionActivity.java
@@ -330,7 +330,7 @@
             fragment.updateAccount(account);
         } else {
             mWait.setVisibility(View.VISIBLE);
-            replaceFragment(WaitFragment.newInstance(account, true),
+            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
                     FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
         }
         mContent.setVisibility(View.GONE);
diff --git a/src/com/android/mail/ui/MultiFoldersSelectionDialog.java b/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
index fcc53b3..4a7f69d 100644
--- a/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
+++ b/src/com/android/mail/ui/MultiFoldersSelectionDialog.java
@@ -119,6 +119,7 @@
                             // TODO: this makes these entries sort to the top after rotation
                             checked.add(folderUri.toString());
                         }
+                        mAdapter.clearSections();
                         // TODO(mindyp) : bring this back in UR8 when Email providers
                         // will have divided folder sections.
                         /* final String[] headers = mContext.getResources()
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index a9c21f0..2d16f1b 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -210,8 +210,8 @@
     }
 
     @Override
-    protected void showConversation(Conversation conversation, boolean inLoaderCallbacks) {
-        super.showConversation(conversation, inLoaderCallbacks);
+    protected void showConversation(Conversation conversation) {
+        super.showConversation(conversation);
         mConversationListVisible = false;
         if (conversation == null) {
             transitionBackToConversationListMode();
diff --git a/src/com/android/mail/ui/SecureConversationViewController.java b/src/com/android/mail/ui/SecureConversationViewController.java
index 088a4e5..1fed820 100644
--- a/src/com/android/mail/ui/SecureConversationViewController.java
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -22,7 +22,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.support.v4.text.BidiFormatter;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,9 +29,11 @@
 
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
+import com.android.mail.browse.AttachmentActionHandler;
 import com.android.mail.browse.BorderView;
 import com.android.mail.browse.ConversationMessage;
 import com.android.mail.browse.ConversationViewAdapter;
+import com.android.mail.browse.ConversationViewAdapter.MessageFooterItem;
 import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
 import com.android.mail.browse.ConversationViewHeader;
 import com.android.mail.browse.InlineAttachmentViewIntentBuilderCreator;
@@ -55,13 +56,13 @@
  * is pretty much the rendering logic.
  */
 public class SecureConversationViewController implements
-        MessageHeaderView.MessageHeaderViewCallbacks, ScrollListener {
+        MessageHeaderView.MessageHeaderViewCallbacks, ScrollListener,
+        MessageFooterView.MessageFooterCallbacks {
     private static final String BEGIN_HTML =
-                                           "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
+            "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
     private static final String END_HTML = "</div></body>";
 
     private final SecureConversationViewControllerCallbacks mCallbacks;
-    private final BidiFormatter mBidiFormatter;
 
     private MessageWebView mWebView;
     private ConversationViewHeader mConversationHeaderView;
@@ -78,7 +79,6 @@
 
     public SecureConversationViewController(SecureConversationViewControllerCallbacks callbacks) {
         mCallbacks = callbacks;
-        mBidiFormatter = BidiFormatter.getInstance();
     }
 
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -159,7 +159,7 @@
         mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
 
         mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager(),
-                mCallbacks.getConversationAccountController());
+                mCallbacks.getConversationAccountController(), this);
 
         mCallbacks.startMessageLoader();
 
@@ -170,6 +170,12 @@
                 R.dimen.conversation_message_content_margin_side) / r.getDisplayMetrics().density);
     }
 
+    public void onDestroyView() {
+        if (mMessage != null && mMessage.getConversation() != null) {
+            AttachmentActionHandler.unregisterDismissListeners(mMessage.getConversation().uri);
+        }
+    }
+
     @Override
     public void onNotifierScroll(final int y) {
         // We need to decide whether or not to display the snap header.
@@ -219,7 +225,8 @@
 
         if (mMessage.hasAttachments) {
             mMessageFooterView.setVisibility(View.VISIBLE);
-            mMessageFooterView.bind(item, false);
+            mMessageFooterView.bind(
+                    item, ConversationViewAdapter.newMessageFooterItem(null, item), false);
         }
     }
 
@@ -247,10 +254,6 @@
 
     }
 
-    public BidiFormatter getBidiFormatter() {
-        return mBidiFormatter;
-    }
-
     // Start MessageHeaderViewCallbacks implementations
 
     @Override
@@ -290,9 +293,33 @@
     }
 
     @Override
+    public boolean isSecure() {
+        return true;
+    }
+
+    @Override
     public FragmentManager getFragmentManager() {
         return mCallbacks.getFragment().getFragmentManager();
     }
 
     // End MessageHeaderViewCallbacks implementations
+
+    // START MessageFooterCallbacks
+
+    @Override
+    public void setMessageSpacerHeight(MessageFooterItem item, int newSpacerHeight) {
+        // Do nothing.
+    }
+
+    @Override
+    public MessageFooterView getViewForItem(MessageFooterItem item) {
+        return mMessageFooterView;
+    }
+
+    @Override
+    public int getUpdatedHeight(MessageFooterItem item) {
+        return 0; // should never get called since we'll always have a footer view
+    }
+
+    // END MessageFooterCallbacks
 }
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index f13bb0d..b9682b6 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -24,6 +24,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 
 import com.android.emailcommon.mail.Address;
@@ -57,6 +58,19 @@
         }
 
         @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+            // try to load the url assuming it is a cid url
+            final Uri uri = Uri.parse(url);
+            final WebResourceResponse response = loadCIDUri(uri, mViewController.getMessage());
+            if (response != null) {
+                return response;
+            }
+
+            // otherwise, attempt the default handling
+            return super.shouldInterceptRequest(view, url);
+        }
+
+        @Override
         public void onPageFinished(WebView view, String url) {
             // Ignore unsafe calls made after a fragment is detached from an activity.
             // This method needs to, for example, get at the loader manager, which needs
@@ -127,6 +141,12 @@
         mViewController.onActivityCreated(savedInstanceState);
     }
 
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mViewController.onDestroyView();
+    }
+
     // Start implementations of SecureConversationViewControllerCallbacks
 
     @Override
diff --git a/src/com/android/mail/ui/SwipeHelper.java b/src/com/android/mail/ui/SwipeHelper.java
index 1b869d5..18cfb41 100644
--- a/src/com/android/mail/ui/SwipeHelper.java
+++ b/src/com/android/mail/ui/SwipeHelper.java
@@ -264,7 +264,7 @@
      *            move
      */
     private void dismissChild(final SwipeableItemView view, float velocity) {
-        final View animView = mCurrView.getSwipeableView().getView();
+        final View animView = view.getSwipeableView().getView();
         final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
         float newPos = determinePos(animView, velocity);
         int duration = determineDuration(animView, newPos, velocity);
@@ -274,7 +274,7 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                mCallback.onChildDismissed(mCurrView);
+                mCallback.onChildDismissed(view);
                 animView.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
diff --git a/src/com/android/mail/ui/SwipeableListView.java b/src/com/android/mail/ui/SwipeableListView.java
index 8e03b93..9b96fc2 100644
--- a/src/com/android/mail/ui/SwipeableListView.java
+++ b/src/com/android/mail/ui/SwipeableListView.java
@@ -165,7 +165,7 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mScrolling || !mEnableSwipe) {
+        if (mScrolling) {
             return super.onInterceptTouchEvent(ev);
         } else {
             return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
@@ -174,11 +174,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mEnableSwipe) {
-            return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
-        } else {
-            return super.onTouchEvent(ev);
-        }
+        return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
     }
 
     @Override
@@ -205,7 +201,7 @@
 
     @Override
     public boolean canChildBeDismissed(SwipeableItemView v) {
-        return v.canChildBeDismissed();
+        return mEnableSwipe && v.canChildBeDismissed();
     }
 
     @Override
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 579ceca..ef4dfa3 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -297,8 +297,8 @@
     }
 
     @Override
-    protected void showConversation(Conversation conversation, boolean inLoaderCallbacks) {
-        super.showConversation(conversation, inLoaderCallbacks);
+    protected void showConversation(Conversation conversation) {
+        super.showConversation(conversation);
 
         // 2-pane can ignore inLoaderCallbacks because it doesn't use
         // FragmentManager.popBackStack().
diff --git a/src/com/android/mail/ui/WaitFragment.java b/src/com/android/mail/ui/WaitFragment.java
index 7feba14..13eb440 100644
--- a/src/com/android/mail/ui/WaitFragment.java
+++ b/src/com/android/mail/ui/WaitFragment.java
@@ -38,7 +38,7 @@
     // Keys used to pass data to {@link WaitFragment}.
     private static final String ACCOUNT_KEY = "account";
 
-    private static final String DEFAULT_KEY = "isDefault";
+    private static final String EXPECTING_MESSAGES_KEY = "expectingMessages";
 
     private static final int MANUAL_SYNC_LOADER = 0;
 
@@ -47,21 +47,17 @@
 
     private LayoutInflater mInflater;
 
-    private boolean mDefault;
+    private boolean mExpectingMessages;
 
     // Public no-args constructor needed for fragment re-instantiation
     public WaitFragment() {}
 
-    public static WaitFragment newInstance(Account account) {
-        return newInstance(account, false);
-    }
-
-    public static WaitFragment newInstance(Account account, boolean def) {
+    public static WaitFragment newInstance(Account account, boolean expectingMessages) {
         WaitFragment fragment = new WaitFragment();
 
         final Bundle args = new Bundle();
         args.putParcelable(ACCOUNT_KEY, account);
-        args.putBoolean(DEFAULT_KEY, def);
+        args.putBoolean(EXPECTING_MESSAGES_KEY, expectingMessages);
         fragment.setArguments(args);
         return fragment;
     }
@@ -72,7 +68,7 @@
 
         Bundle args = getArguments();
         mAccount = (Account)args.getParcelable(ACCOUNT_KEY);
-        mDefault = args.getBoolean(DEFAULT_KEY, false);
+        mExpectingMessages = args.getBoolean(EXPECTING_MESSAGES_KEY, false);
     }
 
     @Override
@@ -96,10 +92,10 @@
             view.findViewById(R.id.manual_sync).setOnClickListener(this);
             view.findViewById(R.id.change_sync_settings).setOnClickListener(this);
 
-        } else if (mDefault) {
-            view = mInflater.inflate(R.layout.wait_default, root, false);
+        } else if (mExpectingMessages) {
+            view = mInflater.inflate(R.layout.loading_messages, root, false);
         } else {
-            view = mInflater.inflate(R.layout.wait_for_sync, root, false);
+            view = mInflater.inflate(R.layout.wait_default, root, false);
         }
 
         return view;
diff --git a/src/com/android/mail/utils/LogUtils.java b/src/com/android/mail/utils/LogUtils.java
index 09e53b3..b2cfc5b 100644
--- a/src/com/android/mail/utils/LogUtils.java
+++ b/src/com/android/mail/utils/LogUtils.java
@@ -64,8 +64,8 @@
      * production releases.  This should be set to DEBUG for production releases, and VERBOSE for
      * internal builds.
      */
-    // ship with DEBUG set
-    private static final int MAX_ENABLED_LOG_LEVEL = DEBUG;
+    // STOPSHIP: ship with DEBUG set
+    private static final int MAX_ENABLED_LOG_LEVEL = VERBOSE;
 
     private static Boolean sDebugLoggingEnabledForTests = null;
 
diff --git a/src/com/android/mail/utils/NotificationActionUtils.java b/src/com/android/mail/utils/NotificationActionUtils.java
index e298d91..baf3b8a 100644
--- a/src/com/android/mail/utils/NotificationActionUtils.java
+++ b/src/com/android/mail/utils/NotificationActionUtils.java
@@ -30,6 +30,8 @@
 import android.os.SystemClock;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.TaskStackBuilder;
+import android.support.wearable.notifications.RemoteInput;
+import android.support.wearable.notifications.WearableNotifications;
 import android.widget.RemoteViews;
 
 import com.android.mail.MailIntentService;
@@ -54,6 +56,8 @@
 public class NotificationActionUtils {
     private static final String LOG_TAG = "NotifActionUtils";
 
+    public static final String WEAR_REPLY_INPUT = "wear_reply";
+
     private static long sUndoTimeoutMillis = -1;
 
     /**
@@ -194,18 +198,35 @@
      */
     public static void addNotificationActions(final Context context,
             final Intent notificationIntent, final NotificationCompat.Builder notification,
-            final Account account, final Conversation conversation, final Message message,
+            WearableNotifications.Builder wearableNotification, final Account account,
+            final Conversation conversation, final Message message,
             final Folder folder, final int notificationId, final long when,
             final Set<String> notificationActions) {
         final List<NotificationActionType> sortedActions =
                 getSortedNotificationActions(folder, notificationActions);
 
         for (final NotificationActionType notificationAction : sortedActions) {
-            notification.addAction(notificationAction.getActionIconResId(
-                    folder, conversation, message), context.getString(notificationAction
-                    .getDisplayStringResId(folder, conversation, message)),
-                    getNotificationActionPendingIntent(context, account, conversation, message,
-                            folder, notificationIntent, notificationAction, notificationId, when));
+            PendingIntent pendingIntent = getNotificationActionPendingIntent(
+                    context, account, conversation, message,
+                    folder, notificationIntent, notificationAction, notificationId, when);
+            int actionIconResId = notificationAction.getActionIconResId(folder, conversation,
+                    message);
+            String title = context.getString(notificationAction.getDisplayStringResId(
+                    folder, conversation, message));
+
+            // Always add all actions to both standard and wearable notifications.
+            notification.addAction(actionIconResId, title, pendingIntent);
+
+            WearableNotifications.Action.Builder wearableActionBuilder =
+                    new WearableNotifications.Action.Builder(actionIconResId, title, pendingIntent);
+            if (notificationAction == NotificationActionType.REPLY
+                    || notificationAction == NotificationActionType.REPLY_ALL) {
+                wearableActionBuilder.addRemoteInput(
+                        new RemoteInput.Builder(WEAR_REPLY_INPUT).setLabel(title).build());
+                LogUtils.d(LOG_TAG, "Adding wearable action!!");
+            }
+
+            wearableNotification.addAction(wearableActionBuilder.build());
         }
     }
 
@@ -314,11 +335,6 @@
                 final Intent intent = createReplyIntent(context, account, messageUri, false);
                 intent.setPackage(context.getPackageName());
                 intent.putExtra(ComposeActivity.EXTRA_NOTIFICATION_FOLDER, folder);
-                // To make sure that the reply intents one notification don't clobber over
-                // intents for other notification, force a data uri on the intent
-                final Uri notificationUri =
-                        Uri.parse("mailfrom://mail/account/" + "reply/" + notificationId);
-                intent.setData(notificationUri);
 
                 taskStackBuilder.addNextIntent(notificationIntent).addNextIntent(intent);
 
@@ -332,11 +348,6 @@
                 final Intent intent = createReplyIntent(context, account, messageUri, true);
                 intent.setPackage(context.getPackageName());
                 intent.putExtra(ComposeActivity.EXTRA_NOTIFICATION_FOLDER, folder);
-                // To make sure that the reply intents one notification don't clobber over
-                // intents for other notification, force a data uri on the intent
-                final Uri notificationUri =
-                        Uri.parse("mailfrom://mail/account/" + "replyall/" + notificationId);
-                intent.setData(notificationUri);
 
                 taskStackBuilder.addNextIntent(notificationIntent).addNextIntent(intent);
 
@@ -378,16 +389,6 @@
         return intent;
     }
 
-    /**
-     * @return an intent which, if launched, will forward the conversation
-     */
-    public static Intent createForwardIntent(
-            final Context context, final Account account, final Uri messageUri) {
-        final Intent intent = ComposeActivity.createForwardIntent(context, account, messageUri);
-        intent.putExtra(Utils.EXTRA_FROM_NOTIFICATION, true);
-        return intent;
-    }
-
     public static class NotificationAction implements Parcelable {
         private final NotificationActionType mNotificationActionType;
         private final Account mAccount;
diff --git a/src/com/android/mail/utils/NotificationUtils.java b/src/com/android/mail/utils/NotificationUtils.java
index e85545c..fa93733 100644
--- a/src/com/android/mail/utils/NotificationUtils.java
+++ b/src/com/android/mail/utils/NotificationUtils.java
@@ -28,11 +28,14 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
+import android.preview.support.v4.app.NotificationManagerCompat;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.Contacts.Photo;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.text.BidiFormatter;
+import android.support.v4.util.ArrayMap;
+import android.support.wearable.notifications.WearableNotifications;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -72,7 +75,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -98,6 +104,9 @@
 
     private static BidiFormatter sBidiFormatter = BidiFormatter.getInstance();
 
+    private static Map<NotificationKey, Set<Integer>> sChildNotificationsMap =
+            new HashMap<NotificationKey, Set<Integer>>();
+
     /**
      * Clears all notifications in response to the user tapping "Clear" in the status bar.
      */
@@ -301,8 +310,7 @@
      **/
     public static void cancelAllNotifications(Context context) {
         LogUtils.d(LOG_TAG, "cancelAllNotifications - cancelling all");
-        NotificationManager nm = (NotificationManager) context.getSystemService(
-                Context.NOTIFICATION_SERVICE);
+        NotificationManagerCompat nm = NotificationManagerCompat.from(context);
         nm.cancelAll();
         clearAllNotfications(context);
     }
@@ -338,8 +346,7 @@
 
         if (cancelExisting) {
             LogUtils.d(LOG_TAG, "resendNotifications - cancelling all");
-            NotificationManager nm =
-                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+            NotificationManagerCompat nm = NotificationManagerCompat.from(context);
             nm.cancelAll();
         }
         // Re-validate the notifications.
@@ -491,8 +498,7 @@
             final Account account, boolean getAttention, boolean ignoreUnobtrusiveSetting,
             NotificationKey key) {
 
-        NotificationManager nm = (NotificationManager)
-                context.getSystemService(Context.NOTIFICATION_SERVICE);
+        NotificationManagerCompat nm = NotificationManagerCompat.from(context);
 
         final NotificationMap notificationMap = getNotificationMap(context);
         if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) {
@@ -551,11 +557,15 @@
             final int notificationId =
                     getNotificationId(account.getAccountManagerAccount(), folder);
 
+            NotificationKey notificationKey = new NotificationKey(account, folder);
+
             if (unseenCount == 0) {
                 LogUtils.i(LOG_TAG, "validateNotifications - cancelling account %s / folder %s",
                         LogUtils.sanitizeName(LOG_TAG, account.getEmailAddress()),
                         LogUtils.sanitizeName(LOG_TAG, folder.persistentId));
                 nm.cancel(notificationId);
+                cancelChildNotifications(notificationKey, nm);
+
                 return;
             }
 
@@ -563,6 +573,10 @@
             PendingIntent clickIntent;
 
             NotificationCompat.Builder notification = new NotificationCompat.Builder(context);
+            WearableNotifications.Builder wearableNotification =
+                    new WearableNotifications.Builder(notification);
+            Map<Integer, WearableNotifications.Builder> msgNotifications =
+                    new ArrayMap<Integer, WearableNotifications.Builder>();
             notification.setSmallIcon(R.drawable.stat_notify_email);
             notification.setTicker(account.getDisplayName());
 
@@ -640,20 +654,12 @@
                         return;
                     }
 
-                    // Amend the click intent with a hint that its source was a notification,
-                    // but remove the hint before it's used to generate notification action
-                    // intents. This prevents the following sequence:
-                    // 1. generate single notification
-                    // 2. user clicks reply, then completes Compose activity
-                    // 3. main activity launches, gets FROM_NOTIFICATION hint in intent
-                    notificationIntent.putExtra(Utils.EXTRA_FROM_NOTIFICATION, true);
-                    clickIntent = PendingIntent.getActivity(context, -1, notificationIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT);
-                    notificationIntent.removeExtra(Utils.EXTRA_FROM_NOTIFICATION);
+                    clickIntent = createClickPendingIntent(context, notificationIntent);
 
                     configureLatestEventInfoFromConversation(context, account, folderPreferences,
-                            notification, cursor, clickIntent, notificationIntent,
-                            unreadCount, unseenCount, folder, when);
+                            notification, wearableNotification, msgNotifications, notificationId,
+                            cursor, clickIntent, notificationIntent, unreadCount, unseenCount,
+                            folder, when);
                     eventInfoConfigured = true;
                 }
             }
@@ -676,11 +682,30 @@
 
             int defaults = 0;
 
+            // Check if any current child notifications exist previously.  Only notify if one of
+            // them is new.
+            boolean hasNewChildNotification;
+            Set<Integer> prevChildNotifications = sChildNotificationsMap.get(notificationKey);
+            if (prevChildNotifications != null) {
+                hasNewChildNotification = false;
+                for (Integer currentNotificationId : msgNotifications.keySet()) {
+                    if (!prevChildNotifications.contains(currentNotificationId)) {
+                        hasNewChildNotification = true;
+                        break;
+                    }
+                }
+            } else {
+                hasNewChildNotification = true;
+            }
+
+            LogUtils.d(LOG_TAG, "getAttention=%s,oldWhen=%s,hasNewChildNotification=%s",
+                    getAttention, oldWhen, hasNewChildNotification);
+
             /*
              * We do not want to notify if this is coming back from an Undo notification, hence the
              * oldWhen check.
              */
-            if (getAttention && oldWhen == 0) {
+            if (getAttention && oldWhen == 0 && hasNewChildNotification) {
                 final AccountPreferences accountPreferences =
                         new AccountPreferences(context, account.getEmailAddress());
                 if (accountPreferences.areNotificationsEnabled()) {
@@ -707,7 +732,28 @@
                     notification.setTicker(null);
                 }
 
-                nm.notify(notificationId, notification.build());
+                nm.notify(notificationId, wearableNotification.build());
+
+                if (prevChildNotifications != null) {
+                    Set<Integer> currentNotificationIds = msgNotifications.keySet();
+                    for (Integer prevChildNotificationId : prevChildNotifications) {
+                        if (!currentNotificationIds.contains(prevChildNotificationId)) {
+                            nm.cancel(prevChildNotificationId);
+                            LogUtils.d(LOG_TAG, "canceling child notification %s",
+                                    prevChildNotificationId);
+                        }
+                    }
+                }
+
+                for (Map.Entry<Integer, WearableNotifications.Builder> entry
+                        : msgNotifications.entrySet()) {
+                    nm.notify(entry.getKey(), entry.getValue().build());
+                    LogUtils.d(LOG_TAG, "notifying child notification %s", entry.getKey());
+                }
+
+                Set<Integer> childNotificationIds = new HashSet<Integer>();
+                childNotificationIds.addAll(msgNotifications.keySet());
+                sChildNotificationsMap.put(notificationKey, childNotificationIds);
             } else {
                 LogUtils.i(LOG_TAG, "event info not configured - not notifying");
             }
@@ -718,6 +764,21 @@
         }
     }
 
+    private static PendingIntent createClickPendingIntent(Context context,
+            Intent notificationIntent) {
+        // Amend the click intent with a hint that its source was a notification,
+        // but remove the hint before it's used to generate notification action
+        // intents. This prevents the following sequence:
+        // 1. generate single notification
+        // 2. user clicks reply, then completes Compose activity
+        // 3. main activity launches, gets FROM_NOTIFICATION hint in intent
+        notificationIntent.putExtra(Utils.EXTRA_FROM_NOTIFICATION, true);
+        PendingIntent clickIntent = PendingIntent.getActivity(context, -1, notificationIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        notificationIntent.removeExtra(Utils.EXTRA_FROM_NOTIFICATION);
+        return clickIntent;
+    }
+
     /**
      * @return an {@link Intent} which, if launched, will display the corresponding conversation
      */
@@ -780,7 +841,10 @@
 
     private static void configureLatestEventInfoFromConversation(final Context context,
             final Account account, final FolderPreferences folderPreferences,
-            final NotificationCompat.Builder notification, final Cursor conversationCursor,
+            final NotificationCompat.Builder notification,
+            final WearableNotifications.Builder summaryWearNotif,
+            final Map<Integer, WearableNotifications.Builder> msgNotifications,
+            final int summaryNotificationId, final Cursor conversationCursor,
             final PendingIntent clickIntent, final Intent notificationIntent,
             final int unreadCount, final int unseenCount,
             final Folder folder, final long when) {
@@ -826,6 +890,12 @@
                 final NotificationCompat.InboxStyle digest =
                         new NotificationCompat.InboxStyle(notification);
 
+                // Group by account.
+                String notificationGroupKey =
+                        account.uri.toString() + "/" + folder.folderUri.fullUri;
+                summaryWearNotif.setGroup(notificationGroupKey,
+                        WearableNotifications.GROUP_ORDER_SUMMARY);
+
                 int numDigestItems = 0;
                 do {
                     final Conversation conversation = new Conversation(conversationCursor);
@@ -879,6 +949,30 @@
                                     conversation.getSnippet());
                             digest.addLine(digestLine);
                             numDigestItems++;
+
+                            // Adding child notification for Wear.
+                            NotificationCompat.Builder childNotif =
+                                    new NotificationCompat.Builder(context);
+                            childNotif.setSmallIcon(R.drawable.stat_notify_email);
+                            childNotif.setContentText(digestLine);
+                            Intent childNotificationIntent = createViewConversationIntent(context,
+                                    account, folder, conversationCursor);
+                            PendingIntent childClickIntent = createClickPendingIntent(context,
+                                    childNotificationIntent);
+                            childNotif.setContentIntent(childClickIntent);
+
+                            WearableNotifications.Builder childWearNotif =
+                                    new WearableNotifications.Builder(childNotif).setGroup(
+                                            notificationGroupKey, numDigestItems);
+                            int childNotificationId = getNotificationId(summaryNotificationId,
+                                    conversation.hashCode());
+
+                            configureNotifForOneConversation(context, account, folderPreferences,
+                                    childNotif, childWearNotif, conversationCursor,
+                                    notificationIntent, folder, when, res,
+                                    notificationAccountDisplayName, notificationAccountEmail,
+                                    isInbox, notificationLabelName, childNotificationId);
+                            msgNotifications.put(childNotificationId, childWearNotif);
                         } finally {
                             if (messageCursor != null) {
                                 messageCursor.close();
@@ -895,137 +989,17 @@
                         isInbox ? notificationAccountDisplayName : notificationLabelName);
             }
         } else {
-            // For notifications for a single new conversation, we want to get the information from
-            // the conversation
+            // For notifications for a single new conversation, we want to get the information
+            // from the conversation
 
             // Move the cursor to the most recent unread conversation
             seekToLatestUnreadConversation(conversationCursor);
 
-            final Conversation conversation = new Conversation(conversationCursor);
-
-            Cursor cursor = null;
-            MessageCursor messageCursor = null;
-            boolean multipleUnseenThread = false;
-            String from = null;
-            try {
-                final Uri uri = conversation.messageListUri.buildUpon().appendQueryParameter(
-                        UIProvider.LABEL_QUERY_PARAMETER, folder.persistentId).build();
-                cursor = context.getContentResolver().query(uri, UIProvider.MESSAGE_PROJECTION,
-                        null, null, null);
-                messageCursor = new MessageCursor(cursor);
-                // Use the information from the last sender in the conversation that triggered
-                // this notification.
-
-                String fromAddress = "";
-                if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) {
-                    final Message message = messageCursor.getMessage();
-                    fromAddress = message.getFrom();
-                    from = getDisplayableSender(fromAddress);
-                    notification.setLargeIcon(
-                            getContactIcon(context, from, getSenderAddress(fromAddress), folder));
-                }
-
-                // Assume that the last message in this conversation is unread
-                int firstUnseenMessagePos = messageCursor.getPosition();
-                while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) {
-                    final Message message = messageCursor.getMessage();
-                    final boolean unseen = !message.seen;
-                    if (unseen) {
-                        firstUnseenMessagePos = messageCursor.getPosition();
-                        if (!multipleUnseenThread
-                                && !fromAddress.contentEquals(message.getFrom())) {
-                            multipleUnseenThread = true;
-                        }
-                    }
-                }
-
-                // TODO(skennedy) Can we remove this check?
-                if (Utils.isRunningJellybeanOrLater()) {
-                    // For a new-style notification
-
-                    if (multipleUnseenThread) {
-                        // The title of a single conversation is the list of senders.
-                        int sendersLength = res.getInteger(R.integer.swipe_senders_length);
-
-                        final SpannableStringBuilder sendersBuilder = getStyledSenders(
-                                context, conversationCursor, sendersLength,
-                                notificationAccountEmail);
-
-                        notification.setContentTitle(sendersBuilder);
-                        // For a single new conversation, the ticker is based on the sender's name.
-                        notificationTicker = sendersBuilder.toString();
-                    } else {
-                        from = getWrappedFromString(from);
-                        // The title of a single message the sender.
-                        notification.setContentTitle(from);
-                        // For a single new conversation, the ticker is based on the sender's name.
-                        notificationTicker = from;
-                    }
-
-                    // The notification content will be the subject of the conversation.
-                    notification.setContentText(
-                            getSingleMessageLittleText(context, conversation.subject));
-
-                    // The notification subtext will be the subject of the conversation for inbox
-                    // notifications, or will based on the the label name for user label
-                    // notifications.
-                    notification.setSubText(isInbox ?
-                            notificationAccountDisplayName : notificationLabelName);
-
-                    if (multipleUnseenThread) {
-                        notification.setLargeIcon(
-                                getDefaultNotificationIcon(context, folder, true));
-                    }
-                    final NotificationCompat.BigTextStyle bigText =
-                            new NotificationCompat.BigTextStyle(notification);
-
-                    // Seek the message cursor to the first unread message
-                    final Message message;
-                    if (messageCursor.moveToPosition(firstUnseenMessagePos)) {
-                        message = messageCursor.getMessage();
-                        bigText.bigText(getSingleMessageBigText(context,
-                                conversation.subject, message));
-                    } else {
-                        LogUtils.e(LOG_TAG, "Failed to load message");
-                        message = null;
-                    }
-
-                    if (message != null) {
-                        final Set<String> notificationActions =
-                                folderPreferences.getNotificationActions(account);
-
-                        final int notificationId = getNotificationId(
-                                account.getAccountManagerAccount(), folder);
-
-                        NotificationActionUtils.addNotificationActions(context, notificationIntent,
-                                notification, account, conversation, message, folder,
-                                notificationId, when, notificationActions);
-                    }
-                } else {
-                    // For an old-style notification
-
-                    // The title of a single conversation notification is built from both the sender
-                    // and subject of the new message.
-                    notification.setContentTitle(getSingleMessageNotificationTitle(context,
-                            from, conversation.subject));
-
-                    // The notification content will be the subject of the conversation for inbox
-                    // notifications, or will based on the the label name for user label
-                    // notifications.
-                    notification.setContentText(
-                            isInbox ? notificationAccountDisplayName : notificationLabelName);
-
-                    // For a single new conversation, the ticker is based on the sender's name.
-                    notificationTicker = from;
-                }
-            } finally {
-                if (messageCursor != null) {
-                    messageCursor.close();
-                }
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
+            notificationTicker = configureNotifForOneConversation(context, account,
+                    folderPreferences, notification, summaryWearNotif, conversationCursor,
+                    notificationIntent, folder, when, res, notificationAccountDisplayName,
+                    notificationAccountEmail, isInbox, notificationLabelName,
+                    summaryNotificationId);
         }
 
         // Build the notification ticker
@@ -1048,6 +1022,144 @@
         notification.setContentIntent(clickIntent);
     }
 
+    /**
+     * Configure the notification for one conversation.  When there are multiple conversations,
+     * this method is used to configure bundled notification for Android Wear.
+     */
+    private static String configureNotifForOneConversation(Context context, Account account,
+            FolderPreferences folderPreferences, NotificationCompat.Builder notification,
+            WearableNotifications.Builder summaryWearNotif, Cursor conversationCursor,
+            Intent notificationIntent, Folder folder, long when, Resources res,
+            String notificationAccountDisplayName, String notificationAccountEmail, boolean isInbox,
+            String notificationLabelName, int notificationId) {
+
+        String notificationTicker;
+
+        final Conversation conversation = new Conversation(conversationCursor);
+
+        Cursor cursor = null;
+        MessageCursor messageCursor = null;
+        boolean multipleUnseenThread = false;
+        String from = null;
+        try {
+            final Uri uri = conversation.messageListUri.buildUpon().appendQueryParameter(
+                    UIProvider.LABEL_QUERY_PARAMETER, folder.persistentId).build();
+            cursor = context.getContentResolver().query(uri, UIProvider.MESSAGE_PROJECTION,
+                    null, null, null);
+            messageCursor = new MessageCursor(cursor);
+            // Use the information from the last sender in the conversation that triggered
+            // this notification.
+
+            String fromAddress = "";
+            if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) {
+                final Message message = messageCursor.getMessage();
+                fromAddress = message.getFrom();
+                from = getDisplayableSender(fromAddress);
+                notification.setLargeIcon(
+                        getContactIcon(context, from, getSenderAddress(fromAddress), folder));
+            }
+
+            // Assume that the last message in this conversation is unread
+            int firstUnseenMessagePos = messageCursor.getPosition();
+            while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) {
+                final Message message = messageCursor.getMessage();
+                final boolean unseen = !message.seen;
+                if (unseen) {
+                    firstUnseenMessagePos = messageCursor.getPosition();
+                    if (!multipleUnseenThread
+                            && !fromAddress.contentEquals(message.getFrom())) {
+                        multipleUnseenThread = true;
+                    }
+                }
+            }
+
+            // TODO(skennedy) Can we remove this check?
+            if (Utils.isRunningJellybeanOrLater()) {
+                // For a new-style notification
+
+                if (multipleUnseenThread) {
+                    // The title of a single conversation is the list of senders.
+                    int sendersLength = res.getInteger(R.integer.swipe_senders_length);
+
+                    final SpannableStringBuilder sendersBuilder = getStyledSenders(
+                            context, conversationCursor, sendersLength,
+                            notificationAccountEmail);
+
+                    notification.setContentTitle(sendersBuilder);
+                    // For a single new conversation, the ticker is based on the sender's name.
+                    notificationTicker = sendersBuilder.toString();
+                } else {
+                    from = getWrappedFromString(from);
+                    // The title of a single message the sender.
+                    notification.setContentTitle(from);
+                    // For a single new conversation, the ticker is based on the sender's name.
+                    notificationTicker = from;
+                }
+
+                // The notification content will be the subject of the conversation.
+                notification.setContentText(
+                        getSingleMessageLittleText(context, conversation.subject));
+
+                // The notification subtext will be the subject of the conversation for inbox
+                // notifications, or will based on the the label name for user label
+                // notifications.
+                notification.setSubText(isInbox ?
+                        notificationAccountDisplayName : notificationLabelName);
+
+                if (multipleUnseenThread) {
+                    notification.setLargeIcon(
+                            getDefaultNotificationIcon(context, folder, true));
+                }
+                final NotificationCompat.BigTextStyle bigText =
+                        new NotificationCompat.BigTextStyle(notification);
+
+                // Seek the message cursor to the first unread message
+                final Message message;
+                if (messageCursor.moveToPosition(firstUnseenMessagePos)) {
+                    message = messageCursor.getMessage();
+                    bigText.bigText(getSingleMessageBigText(context,
+                            conversation.subject, message));
+                } else {
+                    LogUtils.e(LOG_TAG, "Failed to load message");
+                    message = null;
+                }
+
+                if (message != null) {
+                    final Set<String> notificationActions =
+                            folderPreferences.getNotificationActions(account);
+
+                    NotificationActionUtils.addNotificationActions(context, notificationIntent,
+                            notification, summaryWearNotif, account, conversation, message,
+                            folder, notificationId, when, notificationActions);
+                }
+            } else {
+                // For an old-style notification
+
+                // The title of a single conversation notification is built from both the sender
+                // and subject of the new message.
+                notification.setContentTitle(getSingleMessageNotificationTitle(context,
+                        from, conversation.subject));
+
+                // The notification content will be the subject of the conversation for inbox
+                // notifications, or will based on the the label name for user label
+                // notifications.
+                notification.setContentText(
+                        isInbox ? notificationAccountDisplayName : notificationLabelName);
+
+                // For a single new conversation, the ticker is based on the sender's name.
+                notificationTicker = from;
+            }
+        } finally {
+            if (messageCursor != null) {
+                messageCursor.close();
+            }
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return notificationTicker;
+    }
+
     private static String getWrappedFromString(String from) {
         if (from == null) {
             LogUtils.e(LOG_TAG, "null from string in getWrappedFromString");
@@ -1311,10 +1423,12 @@
         notificationMap.remove(key);
         notificationMap.saveNotificationMap(context);
 
-        final NotificationManager notificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        final NotificationManagerCompat notificationManager =
+                NotificationManagerCompat.from(context);
         notificationManager.cancel(getNotificationId(account.getAccountManagerAccount(), folder));
 
+        cancelChildNotifications(key, notificationManager);
+
         if (markSeen) {
             markSeen(context, folder);
         }
@@ -1339,18 +1453,31 @@
 
         final List<NotificationKey> notificationKeys = keyBuilder.build();
 
-        final NotificationManager notificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        final NotificationManagerCompat notificationManager =
+                NotificationManagerCompat.from(context);
 
         for (final NotificationKey notificationKey : notificationKeys) {
             final Folder folder = notificationKey.folder;
             notificationManager.cancel(getNotificationId(account, folder));
             notificationMap.remove(notificationKey);
+
+            cancelChildNotifications(notificationKey, notificationManager);
         }
 
         notificationMap.saveNotificationMap(context);
     }
 
+    private static void cancelChildNotifications(NotificationKey key,
+            NotificationManagerCompat nm) {
+        Set<Integer> childNotifications = sChildNotificationsMap.get(key);
+        if (childNotifications != null) {
+            for (Integer childNotification : childNotifications) {
+                nm.cancel(childNotification);
+            }
+            sChildNotificationsMap.remove(key);
+        }
+    }
+
     private static ArrayList<Long> findContacts(Context context, Collection<String> addresses) {
         ArrayList<String> whereArgs = new ArrayList<String>();
         StringBuilder whereBuilder = new StringBuilder();
@@ -1516,6 +1643,10 @@
         return 1 ^ account.hashCode() ^ folder.hashCode();
     }
 
+    private static int getNotificationId(int summaryNotificationId, int childHashCode) {
+        return summaryNotificationId ^ childHashCode;
+    }
+
     private static class NotificationKey {
         public final Account account;
         public final Folder folder;
diff --git a/src/com/android/mail/utils/StyleUtils.java b/src/com/android/mail/utils/StyleUtils.java
new file mode 100644
index 0000000..5a0a38e
--- /dev/null
+++ b/src/com/android/mail/utils/StyleUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed 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 com.android.mail.utils;
+
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.style.URLSpan;
+import android.widget.TextView;
+
+import com.android.mail.browse.UrlSpan;
+
+/**
+ * Utility class for styling UI.
+ */
+public class StyleUtils {
+
+    /**
+     * Removes any {@link android.text.style.URLSpan}s from the text view
+     * and replaces them with their non-underline version {@link com.android.mail.browse.UrlSpan}.
+     */
+    public static void stripUnderlines(TextView textView) {
+        final Spannable spannable = (Spannable) textView.getText();
+        final URLSpan[] urls = textView.getUrls();
+
+        for (URLSpan span : urls) {
+            final int start = spannable.getSpanStart(span);
+            final int end = spannable.getSpanEnd(span);
+            spannable.removeSpan(span);
+            span = new UrlSpan(span.getURL());
+            spannable.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+    }
+}
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 258f9a1..6ca889c 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -16,7 +16,6 @@
 
 package com.android.mail.utils;
 
-import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -99,6 +98,8 @@
     public static final String EXTRA_COMPOSE_URI = "composeUri";
     public static final String EXTRA_CONVERSATION = "conversationUri";
     public static final String EXTRA_FROM_NOTIFICATION = "notification";
+    public static final String EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT =
+            "ignore-initial-conversation-limit";
 
     private static final String MAILTO_SCHEME = "mailto";
 
@@ -154,7 +155,8 @@
         if (isRunningKitkatOrLater()) {
             final ActivityManager am = (ActivityManager) context.getSystemService(
                     Context.ACTIVITY_SERVICE);
-            return am.isLowRamDevice();
+            // This will be null when running unit tests
+            return am != null && am.isLowRamDevice();
         } else {
             return false;
         }
@@ -1054,6 +1056,26 @@
     }
 
     /**
+     * Given a value and a set of upper-bounds to use as buckets, return the smallest upper-bound
+     * that is greater than the value.<br>
+     * <br>
+     * Useful for turning a continuous value into one of a set of discrete ones.
+     *
+     * @param value a value to bucketize
+     * @param upperBounds list of upper-bound buckets to clamp to, sorted from smallest-greatest
+     * @return the smallest upper-bound larger than the value, or -1 if the value is larger than
+     * all upper-bounds
+     */
+    public static long getUpperBound(long value, long[] upperBounds) {
+        for (long ub : upperBounds) {
+            if (value < ub) {
+                return ub;
+            }
+        }
+        return -1;
+    }
+
+    /**
      * Get the background color of Gmail's action bar.
      */
     public static int getActionBarBackgroundResource(final Context context) {
diff --git a/src/com/android/bitmap/AltBitmapCache.java b/src/com/android/oldbitmap/AltBitmapCache.java
similarity index 97%
rename from src/com/android/bitmap/AltBitmapCache.java
rename to src/com/android/oldbitmap/AltBitmapCache.java
index f519c7b..456ca03 100644
--- a/src/com/android/bitmap/AltBitmapCache.java
+++ b/src/com/android/oldbitmap/AltBitmapCache.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
-import com.android.bitmap.DecodeTask.Request;
-import com.android.bitmap.ReusableBitmap.NullReusableBitmap;
+import com.android.oldbitmap.DecodeTask.Request;
+import com.android.oldbitmap.ReusableBitmap.NullReusableBitmap;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.LruCache;
diff --git a/src/com/android/bitmap/AltPooledCache.java b/src/com/android/oldbitmap/AltPooledCache.java
similarity index 99%
rename from src/com/android/bitmap/AltPooledCache.java
rename to src/com/android/oldbitmap/AltPooledCache.java
index 03a6c50..e29a8cd 100644
--- a/src/com/android/bitmap/AltPooledCache.java
+++ b/src/com/android/oldbitmap/AltPooledCache.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.support.v4.util.LruCache;
 
diff --git a/src/com/android/bitmap/BitmapCache.java b/src/com/android/oldbitmap/BitmapCache.java
similarity index 96%
rename from src/com/android/bitmap/BitmapCache.java
rename to src/com/android/oldbitmap/BitmapCache.java
index fc76c3a..a613bfc 100644
--- a/src/com/android/bitmap/BitmapCache.java
+++ b/src/com/android/oldbitmap/BitmapCache.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 public interface BitmapCache extends PooledCache<DecodeTask.Request, ReusableBitmap> {
 
diff --git a/src/com/android/bitmap/BitmapUtils.java b/src/com/android/oldbitmap/BitmapUtils.java
similarity index 98%
rename from src/com/android/bitmap/BitmapUtils.java
rename to src/com/android/oldbitmap/BitmapUtils.java
index 8b6527f..8cac914 100644
--- a/src/com/android/bitmap/BitmapUtils.java
+++ b/src/com/android/oldbitmap/BitmapUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.graphics.Rect;
 
diff --git a/src/com/android/bitmap/ContiguousFIFOAggregator.java b/src/com/android/oldbitmap/ContiguousFIFOAggregator.java
similarity index 99%
rename from src/com/android/bitmap/ContiguousFIFOAggregator.java
rename to src/com/android/oldbitmap/ContiguousFIFOAggregator.java
index 165232d..ffc4405 100644
--- a/src/com/android/bitmap/ContiguousFIFOAggregator.java
+++ b/src/com/android/oldbitmap/ContiguousFIFOAggregator.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.util.SparseArray;
 
diff --git a/src/com/android/bitmap/DecodeAggregator.java b/src/com/android/oldbitmap/DecodeAggregator.java
similarity index 90%
rename from src/com/android/bitmap/DecodeAggregator.java
rename to src/com/android/oldbitmap/DecodeAggregator.java
index ec88efc..2e5533b 100644
--- a/src/com/android/bitmap/DecodeAggregator.java
+++ b/src/com/android/oldbitmap/DecodeAggregator.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
-import com.android.bitmap.DecodeTask.Request;
+import com.android.oldbitmap.DecodeTask.Request;
 
 public class DecodeAggregator extends ContiguousFIFOAggregator<Request> {
 
diff --git a/src/com/android/bitmap/DecodeTask.java b/src/com/android/oldbitmap/DecodeTask.java
similarity index 99%
rename from src/com/android/bitmap/DecodeTask.java
rename to src/com/android/oldbitmap/DecodeTask.java
index d7a3cab..704dbaa 100644
--- a/src/com/android/bitmap/DecodeTask.java
+++ b/src/com/android/oldbitmap/DecodeTask.java
@@ -1,4 +1,4 @@
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
diff --git a/src/com/android/bitmap/Poolable.java b/src/com/android/oldbitmap/Poolable.java
similarity index 95%
rename from src/com/android/bitmap/Poolable.java
rename to src/com/android/oldbitmap/Poolable.java
index c68a25c..d4de70b 100644
--- a/src/com/android/bitmap/Poolable.java
+++ b/src/com/android/oldbitmap/Poolable.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 public interface Poolable {
     void acquireReference();
diff --git a/src/com/android/bitmap/PooledCache.java b/src/com/android/oldbitmap/PooledCache.java
similarity index 97%
rename from src/com/android/bitmap/PooledCache.java
rename to src/com/android/oldbitmap/PooledCache.java
index 6d6684f..1d16646 100644
--- a/src/com/android/bitmap/PooledCache.java
+++ b/src/com/android/oldbitmap/PooledCache.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 public interface PooledCache<K, V> {
 
diff --git a/src/com/android/bitmap/ReusableBitmap.java b/src/com/android/oldbitmap/ReusableBitmap.java
similarity index 98%
rename from src/com/android/bitmap/ReusableBitmap.java
rename to src/com/android/oldbitmap/ReusableBitmap.java
index dde9bd1..5b62743 100644
--- a/src/com/android/bitmap/ReusableBitmap.java
+++ b/src/com/android/oldbitmap/ReusableBitmap.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.graphics.Bitmap;
 
diff --git a/src/com/android/bitmap/Trace.java b/src/com/android/oldbitmap/Trace.java
similarity index 97%
rename from src/com/android/bitmap/Trace.java
rename to src/com/android/oldbitmap/Trace.java
index 6c5a800..f9455f8 100644
--- a/src/com/android/bitmap/Trace.java
+++ b/src/com/android/oldbitmap/Trace.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.bitmap;
+package com.android.oldbitmap;
 
 import android.os.Build;
 
diff --git a/src/org/apache/commons/io/CopyUtils.java b/src/org/apache/commons/io/CopyUtils.java
index c259f70..e7e2185 100644
--- a/src/org/apache/commons/io/CopyUtils.java
+++ b/src/org/apache/commons/io/CopyUtils.java
@@ -1,333 +1,333 @@
-/*

- * 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.commons.io;

-

-import java.io.ByteArrayInputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.InputStreamReader;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.io.Reader;

-import java.io.StringReader;

-import java.io.Writer;

-

-/**

- * This class provides static utility methods for buffered

- * copying between sources (<code>InputStream</code>, <code>Reader</code>,

- * <code>String</code> and <code>byte[]</code>) and destinations

- * (<code>OutputStream</code>, <code>Writer</code>, <code>String</code> and

- * <code>byte[]</code>).

- * <p>

- * Unless otherwise noted, these <code>copy</code> methods do <em>not</em>

- * flush or close the streams. Often doing so would require making non-portable

- * assumptions about the streams' origin and further use. This means that both

- * streams' <code>close()</code> methods must be called after copying. if one

- * omits this step, then the stream resources (sockets, file descriptors) are

- * released when the associated Stream is garbage-collected. It is not a good

- * idea to rely on this mechanism. For a good overview of the distinction

- * between "memory management" and "resource management", see

- * <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this

- * UnixReview article</a>.

- * <p>

- * For byte-to-char methods, a <code>copy</code> variant allows the encoding

- * to be selected (otherwise the platform default is used). We would like to

- * encourage you to always specify the encoding because relying on the platform

- * default can lead to unexpected results.

- * <p

- * We don't provide special variants for the <code>copy</code> methods that

- * let you specify the buffer size because in modern VMs the impact on speed

- * seems to be minimal. We're using a default buffer size of 4 KB.

- * <p>

- * The <code>copy</code> methods use an internal buffer when copying. It is

- * therefore advisable <em>not</em> to deliberately wrap the stream arguments

- * to the <code>copy</code> methods in <code>Buffered*</code> streams. For

- * example, don't do the following:

- * <pre>

- *  copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );

- *  </pre>

- * The rationale is as follows:

- * <p>

- * Imagine that an InputStream's read() is a very expensive operation, which

- * would usually suggest wrapping in a BufferedInputStream. The

- * BufferedInputStream works by issuing infrequent

- * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the

- * underlying InputStream, to fill an internal buffer, from which further

- * <code>read</code> requests can inexpensively get their data (until the buffer

- * runs out).

- * <p>

- * However, the <code>copy</code> methods do the same thing, keeping an

- * internal buffer, populated by

- * {@link InputStream#read(byte[] b, int off, int len)} requests. Having two

- * buffers (or three if the destination stream is also buffered) is pointless,

- * and the unnecessary buffer management hurts performance slightly (about 3%,

- * according to some simple experiments).

- * <p>

- * Behold, intrepid explorers; a map of this class:

- * <pre>

- *       Method      Input               Output          Dependency

- *       ------      -----               ------          -------

- * 1     copy        InputStream         OutputStream    (primitive)

- * 2     copy        Reader              Writer          (primitive)

- *

- * 3     copy        InputStream         Writer          2

- *

- * 4     copy        Reader              OutputStream    2

- *

- * 5     copy        String              OutputStream    2

- * 6     copy        String              Writer          (trivial)

- *

- * 7     copy        byte[]              Writer          3

- * 8     copy        byte[]              OutputStream    (trivial)

- * </pre>

- * <p>

- * Note that only the first two methods shuffle bytes; the rest use these

- * two, or (if possible) copy using native Java copy methods. As there are

- * method variants to specify the encoding, each row may

- * correspond to up to 2 methods.

- * <p>

- * Origin of code: Excalibur.

- *

- * @author Peter Donald

- * @author Jeff Turner

- * @author Matthew Hawthorne

- * @version $Id: CopyUtils.java 437680 2006-08-28 11:57:00Z scolebourne $

- * @deprecated Use IOUtils. Will be removed in 2.0.

- *  Methods renamed to IOUtils.write() or IOUtils.copy().

- *  Null handling behaviour changed in IOUtils (null data does not

- *  throw NullPointerException).

- */

-@Deprecated

-public class CopyUtils {

-

-    /**

-     * The default size of the buffer.

-     */

-    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public CopyUtils() { }

-

-    // ----------------------------------------------------------------

-    // byte[] -> OutputStream

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.

-     * @param input the byte array to read from

-     * @param output the <code>OutputStream</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(byte[] input, OutputStream output)

-            throws IOException {

-        output.write(input);

-    }

-

-    // ----------------------------------------------------------------

-    // byte[] -> Writer

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy and convert bytes from a <code>byte[]</code> to chars on a

-     * <code>Writer</code>.

-     * The platform's default encoding is used for the byte-to-char conversion.

-     * @param input the byte array to read from

-     * @param output the <code>Writer</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(byte[] input, Writer output)

-            throws IOException {

-        ByteArrayInputStream in = new ByteArrayInputStream(input);

-        copy(in, output);

-    }

-

-

-    /**

-     * Copy and convert bytes from a <code>byte[]</code> to chars on a

-     * <code>Writer</code>, using the specified encoding.

-     * @param input the byte array to read from

-     * @param output the <code>Writer</code> to write to

-     * @param encoding The name of a supported character encoding. See the

-     * <a href="http://www.iana.org/assignments/character-sets">IANA

-     * Charset Registry</a> for a list of valid encoding types.

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(

-            byte[] input,

-            Writer output,

-            String encoding)

-                throws IOException {

-        ByteArrayInputStream in = new ByteArrayInputStream(input);

-        copy(in, output, encoding);

-    }

-

-

-    // ----------------------------------------------------------------

-    // Core copy methods

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy bytes from an <code>InputStream</code> to an

-     * <code>OutputStream</code>.

-     * @param input the <code>InputStream</code> to read from

-     * @param output the <code>OutputStream</code> to write to

-     * @return the number of bytes copied

-     * @throws IOException In case of an I/O problem

-     */

-    public static int copy(

-            InputStream input,

-            OutputStream output)

-                throws IOException {

-        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

-        int count = 0;

-        int n = 0;

-        while (-1 != (n = input.read(buffer))) {

-            output.write(buffer, 0, n);

-            count += n;

-        }

-        return count;

-    }

-

-    // ----------------------------------------------------------------

-    // Reader -> Writer

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.

-     * @param input the <code>Reader</code> to read from

-     * @param output the <code>Writer</code> to write to

-     * @return the number of characters copied

-     * @throws IOException In case of an I/O problem

-     */

-    public static int copy(

-            Reader input,

-            Writer output)

-                throws IOException {

-        char[] buffer = new char[DEFAULT_BUFFER_SIZE];

-        int count = 0;

-        int n = 0;

-        while (-1 != (n = input.read(buffer))) {

-            output.write(buffer, 0, n);

-            count += n;

-        }

-        return count;

-    }

-

-    // ----------------------------------------------------------------

-    // InputStream -> Writer

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy and convert bytes from an <code>InputStream</code> to chars on a

-     * <code>Writer</code>.

-     * The platform's default encoding is used for the byte-to-char conversion.

-     * @param input the <code>InputStream</code> to read from

-     * @param output the <code>Writer</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(

-            InputStream input,

-            Writer output)

-                throws IOException {

-        InputStreamReader in = new InputStreamReader(input);

-        copy(in, output);

-    }

-

-    /**

-     * Copy and convert bytes from an <code>InputStream</code> to chars on a

-     * <code>Writer</code>, using the specified encoding.

-     * @param input the <code>InputStream</code> to read from

-     * @param output the <code>Writer</code> to write to

-     * @param encoding The name of a supported character encoding. See the

-     * <a href="http://www.iana.org/assignments/character-sets">IANA

-     * Charset Registry</a> for a list of valid encoding types.

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(

-            InputStream input,

-            Writer output,

-            String encoding)

-                throws IOException {

-        InputStreamReader in = new InputStreamReader(input, encoding);

-        copy(in, output);

-    }

-

-

-    // ----------------------------------------------------------------

-    // Reader -> OutputStream

-    // ----------------------------------------------------------------

-

-    /**

-     * Serialize chars from a <code>Reader</code> to bytes on an

-     * <code>OutputStream</code>, and flush the <code>OutputStream</code>.

-     * @param input the <code>Reader</code> to read from

-     * @param output the <code>OutputStream</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(

-            Reader input,

-            OutputStream output)

-                throws IOException {

-        OutputStreamWriter out = new OutputStreamWriter(output);

-        copy(input, out);

-        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we

-        // have to flush here.

-        out.flush();

-    }

-

-    // ----------------------------------------------------------------

-    // String -> OutputStream

-    // ----------------------------------------------------------------

-

-    /**

-     * Serialize chars from a <code>String</code> to bytes on an

-     * <code>OutputStream</code>, and

-     * flush the <code>OutputStream</code>.

-     * @param input the <code>String</code> to read from

-     * @param output the <code>OutputStream</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(

-            String input,

-            OutputStream output)

-                throws IOException {

-        StringReader in = new StringReader(input);

-        OutputStreamWriter out = new OutputStreamWriter(output);

-        copy(in, out);

-        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we

-        // have to flush here.

-        out.flush();

-    }

-

-    // ----------------------------------------------------------------

-    // String -> Writer

-    // ----------------------------------------------------------------

-

-    /**

-     * Copy chars from a <code>String</code> to a <code>Writer</code>.

-     * @param input the <code>String</code> to read from

-     * @param output the <code>Writer</code> to write to

-     * @throws IOException In case of an I/O problem

-     */

-    public static void copy(String input, Writer output)

-                throws IOException {

-        output.write(input);

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+
+/**
+ * This class provides static utility methods for buffered
+ * copying between sources (<code>InputStream</code>, <code>Reader</code>,
+ * <code>String</code> and <code>byte[]</code>) and destinations
+ * (<code>OutputStream</code>, <code>Writer</code>, <code>String</code> and
+ * <code>byte[]</code>).
+ * <p>
+ * Unless otherwise noted, these <code>copy</code> methods do <em>not</em>
+ * flush or close the streams. Often doing so would require making non-portable
+ * assumptions about the streams' origin and further use. This means that both
+ * streams' <code>close()</code> methods must be called after copying. if one
+ * omits this step, then the stream resources (sockets, file descriptors) are
+ * released when the associated Stream is garbage-collected. It is not a good
+ * idea to rely on this mechanism. For a good overview of the distinction
+ * between "memory management" and "resource management", see
+ * <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this
+ * UnixReview article</a>.
+ * <p>
+ * For byte-to-char methods, a <code>copy</code> variant allows the encoding
+ * to be selected (otherwise the platform default is used). We would like to
+ * encourage you to always specify the encoding because relying on the platform
+ * default can lead to unexpected results.
+ * <p
+ * We don't provide special variants for the <code>copy</code> methods that
+ * let you specify the buffer size because in modern VMs the impact on speed
+ * seems to be minimal. We're using a default buffer size of 4 KB.
+ * <p>
+ * The <code>copy</code> methods use an internal buffer when copying. It is
+ * therefore advisable <em>not</em> to deliberately wrap the stream arguments
+ * to the <code>copy</code> methods in <code>Buffered*</code> streams. For
+ * example, don't do the following:
+ * <pre>
+ *  copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );
+ *  </pre>
+ * The rationale is as follows:
+ * <p>
+ * Imagine that an InputStream's read() is a very expensive operation, which
+ * would usually suggest wrapping in a BufferedInputStream. The
+ * BufferedInputStream works by issuing infrequent
+ * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the
+ * underlying InputStream, to fill an internal buffer, from which further
+ * <code>read</code> requests can inexpensively get their data (until the buffer
+ * runs out).
+ * <p>
+ * However, the <code>copy</code> methods do the same thing, keeping an
+ * internal buffer, populated by
+ * {@link InputStream#read(byte[] b, int off, int len)} requests. Having two
+ * buffers (or three if the destination stream is also buffered) is pointless,
+ * and the unnecessary buffer management hurts performance slightly (about 3%,
+ * according to some simple experiments).
+ * <p>
+ * Behold, intrepid explorers; a map of this class:
+ * <pre>
+ *       Method      Input               Output          Dependency
+ *       ------      -----               ------          -------
+ * 1     copy        InputStream         OutputStream    (primitive)
+ * 2     copy        Reader              Writer          (primitive)
+ *
+ * 3     copy        InputStream         Writer          2
+ *
+ * 4     copy        Reader              OutputStream    2
+ *
+ * 5     copy        String              OutputStream    2
+ * 6     copy        String              Writer          (trivial)
+ *
+ * 7     copy        byte[]              Writer          3
+ * 8     copy        byte[]              OutputStream    (trivial)
+ * </pre>
+ * <p>
+ * Note that only the first two methods shuffle bytes; the rest use these
+ * two, or (if possible) copy using native Java copy methods. As there are
+ * method variants to specify the encoding, each row may
+ * correspond to up to 2 methods.
+ * <p>
+ * Origin of code: Excalibur.
+ *
+ * @author Peter Donald
+ * @author Jeff Turner
+ * @author Matthew Hawthorne
+ * @version $Id: CopyUtils.java 437680 2006-08-28 11:57:00Z scolebourne $
+ * @deprecated Use IOUtils. Will be removed in 2.0.
+ *  Methods renamed to IOUtils.write() or IOUtils.copy().
+ *  Null handling behaviour changed in IOUtils (null data does not
+ *  throw NullPointerException).
+ */
+@Deprecated
+public class CopyUtils {
+
+    /**
+     * The default size of the buffer.
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public CopyUtils() { }
+
+    // ----------------------------------------------------------------
+    // byte[] -> OutputStream
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
+     * @param input the byte array to read from
+     * @param output the <code>OutputStream</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(byte[] input, OutputStream output)
+            throws IOException {
+        output.write(input);
+    }
+
+    // ----------------------------------------------------------------
+    // byte[] -> Writer
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param input the byte array to read from
+     * @param output the <code>Writer</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(byte[] input, Writer output)
+            throws IOException {
+        ByteArrayInputStream in = new ByteArrayInputStream(input);
+        copy(in, output);
+    }
+
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param input the byte array to read from
+     * @param output the <code>Writer</code> to write to
+     * @param encoding The name of a supported character encoding. See the
+     * <a href="http://www.iana.org/assignments/character-sets">IANA
+     * Charset Registry</a> for a list of valid encoding types.
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(
+            byte[] input,
+            Writer output,
+            String encoding)
+                throws IOException {
+        ByteArrayInputStream in = new ByteArrayInputStream(input);
+        copy(in, output, encoding);
+    }
+
+
+    // ----------------------------------------------------------------
+    // Core copy methods
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to an
+     * <code>OutputStream</code>.
+     * @param input the <code>InputStream</code> to read from
+     * @param output the <code>OutputStream</code> to write to
+     * @return the number of bytes copied
+     * @throws IOException In case of an I/O problem
+     */
+    public static int copy(
+            InputStream input,
+            OutputStream output)
+                throws IOException {
+        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+        int count = 0;
+        int n = 0;
+        while (-1 != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    // ----------------------------------------------------------------
+    // Reader -> Writer
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+     * @param input the <code>Reader</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @return the number of characters copied
+     * @throws IOException In case of an I/O problem
+     */
+    public static int copy(
+            Reader input,
+            Writer output)
+                throws IOException {
+        char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+        int count = 0;
+        int n = 0;
+        while (-1 != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    // ----------------------------------------------------------------
+    // InputStream -> Writer
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param input the <code>InputStream</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(
+            InputStream input,
+            Writer output)
+                throws IOException {
+        InputStreamReader in = new InputStreamReader(input);
+        copy(in, output);
+    }
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param input the <code>InputStream</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @param encoding The name of a supported character encoding. See the
+     * <a href="http://www.iana.org/assignments/character-sets">IANA
+     * Charset Registry</a> for a list of valid encoding types.
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(
+            InputStream input,
+            Writer output,
+            String encoding)
+                throws IOException {
+        InputStreamReader in = new InputStreamReader(input, encoding);
+        copy(in, output);
+    }
+
+
+    // ----------------------------------------------------------------
+    // Reader -> OutputStream
+    // ----------------------------------------------------------------
+
+    /**
+     * Serialize chars from a <code>Reader</code> to bytes on an
+     * <code>OutputStream</code>, and flush the <code>OutputStream</code>.
+     * @param input the <code>Reader</code> to read from
+     * @param output the <code>OutputStream</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(
+            Reader input,
+            OutputStream output)
+                throws IOException {
+        OutputStreamWriter out = new OutputStreamWriter(output);
+        copy(input, out);
+        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
+        // have to flush here.
+        out.flush();
+    }
+
+    // ----------------------------------------------------------------
+    // String -> OutputStream
+    // ----------------------------------------------------------------
+
+    /**
+     * Serialize chars from a <code>String</code> to bytes on an
+     * <code>OutputStream</code>, and
+     * flush the <code>OutputStream</code>.
+     * @param input the <code>String</code> to read from
+     * @param output the <code>OutputStream</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(
+            String input,
+            OutputStream output)
+                throws IOException {
+        StringReader in = new StringReader(input);
+        OutputStreamWriter out = new OutputStreamWriter(output);
+        copy(in, out);
+        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
+        // have to flush here.
+        out.flush();
+    }
+
+    // ----------------------------------------------------------------
+    // String -> Writer
+    // ----------------------------------------------------------------
+
+    /**
+     * Copy chars from a <code>String</code> to a <code>Writer</code>.
+     * @param input the <code>String</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public static void copy(String input, Writer output)
+                throws IOException {
+        output.write(input);
+    }
+
+}
diff --git a/src/org/apache/commons/io/DirectoryWalker.java b/src/org/apache/commons/io/DirectoryWalker.java
index 9e564ae..71a27fb 100644
--- a/src/org/apache/commons/io/DirectoryWalker.java
+++ b/src/org/apache/commons/io/DirectoryWalker.java
@@ -1,620 +1,620 @@
-/*

- * 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.commons.io;

-

-import java.io.File;

-import java.io.FileFilter;

-import java.io.IOException;

-import java.util.Collection;

-

-import org.apache.commons.io.filefilter.FileFilterUtils;

-import org.apache.commons.io.filefilter.IOFileFilter;

-import org.apache.commons.io.filefilter.TrueFileFilter;

-

-/**

- * Abstract class that walks through a directory hierarchy and provides

- * subclasses with convenient hooks to add specific behaviour.

- * <p>

- * This class operates with a {@link FileFilter} and maximum depth to

- * limit the files and direcories visited.

- * Commons IO supplies many common filter implementations in the 

- * <a href="filefilter/package-summary.html"> filefilter</a> package.

- * <p>

- * The following sections describe:

- *   <ul>

- *      <li><a href="#example">1. Example Implementation</a> - example

- *          <code>FileCleaner</code> implementation.</li>

- *      <li><a href="#filter">2. Filter Example</a> - using 

- *          {@link FileFilter}(s) with <code>DirectoryWalker</code>.</li>

- *      <li><a href="#cancel">3. Cancellation</a> - how to implement cancellation

- *          behaviour.</li>

- *   </ul>

- *

- * <a name="example"></a>

- * <h3>1. Example Implementation</h3>

- *

- * There are many possible extensions, for example, to delete all

- * files and '.svn' directories, and return a list of deleted files:

- * <pre>

- *  public class FileCleaner extends DirectoryWalker {

- *

- *    public FileCleaner() {

- *      super();

- *    }

- *

- *    public List clean(File startDirectory) {

- *      List results = new ArrayList();

- *      walk(startDirectory, results);

- *      return results;

- *    }

- *

- *    protected boolean handleDirectory(File directory, int depth, Collection results) {

- *      // delete svn directories and then skip

- *      if (".svn".equals(directory.getName())) {

- *        directory.delete();

- *        return false;

- *      } else {

- *        return true;

- *      }

- *

- *    }

- *

- *    protected void handleFile(File file, int depth, Collection results) {

- *      // delete file and add to list of deleted

- *      file.delete();

- *      results.add(file);

- *    }

- *  }

- * </pre>

- *

- * <a name="filter"></a>

- * <h3>2. Filter Example</h3>

- *

- * Choosing which directories and files to process can be a key aspect

- * of using this class. This information can be setup in three ways,

- * via three different constructors.

- * <p>

- * The first option is to visit all directories and files.

- * This is achieved via the no-args constructor.

- * <p>

- * The second constructor option is to supply a single {@link FileFilter}

- * that describes the files and directories to visit. Care must be taken

- * with this option as the same filter is used for both directories

- * and files.

- * <p>

- * For example, if you wanted all directories which are not hidden

- * and files which end in ".txt":

- * <pre>

- *  public class FooDirectoryWalker extends DirectoryWalker {

- *    public FooDirectoryWalker(FileFilter filter) {

- *      super(filter, -1);

- *    }

- *  }

- *  

- *  // Build up the filters and create the walker

- *    // Create a filter for Non-hidden directories

- *    IOFileFilter fooDirFilter = 

- *        FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,

- *                                      HiddenFileFilter.VISIBLE);

- *

- *    // Create a filter for Files ending in ".txt"

- *    IOFileFilter fooFileFilter = 

- *        FileFilterUtils.andFileFilter(FileFilterUtils.fileFileFilter,

- *                                      FileFilterUtils.suffixFileFilter(".txt"));

- *

- *    // Combine the directory and file filters using an OR condition

- *    java.io.FileFilter fooFilter = 

- *        FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);

- *

- *    // Use the filter to construct a DirectoryWalker implementation

- *    FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);

- * </pre>

- * <p>

- * The third constructor option is to specify separate filters, one for

- * directories and one for files. These are combined internally to form

- * the correct <code>FileFilter</code>, something which is very easy to

- * get wrong when attempted manually, particularly when trying to

- * express constructs like 'any file in directories named docs'.

- * <p>

- * For example, if you wanted all directories which are not hidden

- * and files which end in ".txt":

- * <pre>

- *  public class FooDirectoryWalker extends DirectoryWalker {

- *    public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {

- *      super(dirFilter, fileFilter, -1);

- *    }

- *  }

- *  

- *  // Use the filters to construct the walker

- *  FooDirectoryWalker walker = new FooDirectoryWalker(

- *    HiddenFileFilter.VISIBLE,

- *    FileFilterUtils.suffixFileFilter(".txt"),

- *  );

- * </pre>

- * This is much simpler than the previous example, and is why it is the preferred

- * option for filtering.

- *

- * <a name="cancel"></a>

- * <h3>3. Cancellation</h3>

- *

- * The DirectoryWalker contains some of the logic required for cancel processing.

- * Subclasses must complete the implementation.

- * <p>

- * What <code>DirectoryWalker</code> does provide for cancellation is:

- * <ul>

- *    <li>{@link CancelException} which can be thrown in any of the

- *        <i>lifecycle</i> methods to stop processing.</li>

- *    <li>The <code>walk()</code> method traps thrown {@link CancelException}

- *        and calls the <code>handleCancelled()</code> method, providing

- *        a place for custom cancel processing.</li>

- * </ul>

- * <p>

- * Implementations need to provide:

- * <ul>

- *    <li>The decision logic on whether to cancel processing or not.</li>

- *    <li>Constructing and throwing a {@link CancelException}.</li>

- *    <li>Custom cancel processing in the <code>handleCancelled()</code> method.

- * </ul>

- * <p>

- * Two possible scenarios are envisaged for cancellation:

- * <ul>

- *    <li><a href="#external">3.1 External / Mult-threaded</a> - cancellation being

- *        decided/initiated by an external process.</li>

- *    <li><a href="#internal">3.2 Internal</a> - cancellation being decided/initiated 

- *        from within a DirectoryWalker implementation.</li>

- * </ul>

- * <p>

- * The following sections provide example implementations for these two different

- * scenarios.

- *

- * <a name="external"></a>

- * <h4>3.1 External / Multi-threaded</h4>

- *

- * This example provides a public <code>cancel()</code> method that can be

- * called by another thread to stop the processing. A typical example use-case

- * would be a cancel button on a GUI. Calling this method sets a

- * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">

- * volatile</a> flag to ensure it will work properly in a multi-threaded environment.

- * The flag is returned by the <code>handleIsCancelled()</code> method, which

- * will cause the walk to stop immediately. The <code>handleCancelled()</code>

- * method will be the next, and last, callback method received once cancellation

- * has occurred.

- *

- * <pre>

- *  public class FooDirectoryWalker extends DirectoryWalker {

- *

- *    private volatile boolean cancelled = false;

- *

- *    public void cancel() {

- *        cancelled = true;

- *    }

- *

- *    private void handleIsCancelled(File file, int depth, Collection results) {

- *        return cancelled;

- *    }

- *

- *    protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {

- *        // implement processing required when a cancellation occurs

- *    }

- *  }

- * </pre>

- *

- * <a name="internal"></a>

- * <h4>3.2 Internal</h4>

- *

- * This shows an example of how internal cancellation processing could be implemented.

- * <b>Note</b> the decision logic and throwing a {@link CancelException} could be implemented

- * in any of the <i>lifecycle</i> methods. 

- *

- * <pre>

- *  public class BarDirectoryWalker extends DirectoryWalker {

- *

- *    protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {

- *        // cancel if hidden directory

- *        if (directory.isHidden()) {

- *            throw new CancelException(file, depth);

- *        }

- *        return true;

- *    }

- *

- *    protected void handleFile(File file, int depth, Collection results) throws IOException {

- *        // cancel if read-only file

- *        if (!file.canWrite()) {

- *            throw new CancelException(file, depth);

- *        }

- *        results.add(file);

- *    }

- *

- *    protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {

- *        // implement processing required when a cancellation occurs

- *    }

- *  }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 424748 $

- */

-public abstract class DirectoryWalker {

-

-    /**

-     * The file filter to use to filter files and directories.

-     */

-    private final FileFilter filter;

-    /**

-     * The limit on the directory depth to walk.

-     */

-    private final int depthLimit;

-

-    /**

-     * Construct an instance with no filtering and unlimited <i>depth</i>.

-     */

-    protected DirectoryWalker() {

-        this(null, -1);

-    }

-

-    /**

-     * Construct an instance with a filter and limit the <i>depth</i> navigated to.

-     * <p>

-     * The filter controls which files and directories will be navigated to as

-     * part of the walk. The {@link FileFilterUtils} class is useful for combining

-     * various filters together. A <code>null</code> filter means that no

-     * filtering should occur and all files and directories will be visited.

-     *

-     * @param filter  the filter to apply, null means visit all files

-     * @param depthLimit  controls how <i>deep</i> the hierarchy is

-     *  navigated to (less than 0 means unlimited)

-     */

-    protected DirectoryWalker(FileFilter filter, int depthLimit) {

-        this.filter = filter;

-        this.depthLimit = depthLimit;

-    }

-

-    /**

-     * Construct an instance with a directory and a file filter and an optional

-     * limit on the <i>depth</i> navigated to.

-     * <p>

-     * The filters control which files and directories will be navigated to as part

-     * of the walk. This constructor uses {@link FileFilterUtils#makeDirectoryOnly(IOFileFilter)}

-     * and {@link FileFilterUtils#makeFileOnly(IOFileFilter)} internally to combine the filters.

-     * A <code>null</code> filter means that no filtering should occur.

-     *

-     * @param directoryFilter  the filter to apply to directories, null means visit all directories

-     * @param fileFilter  the filter to apply to files, null means visit all files

-     * @param depthLimit  controls how <i>deep</i> the hierarchy is

-     *  navigated to (less than 0 means unlimited)

-     */

-    protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, int depthLimit) {

-        if (directoryFilter == null && fileFilter == null) {

-            this.filter = null;

-        } else {

-            directoryFilter = (directoryFilter != null ? directoryFilter : TrueFileFilter.TRUE);

-            fileFilter = (fileFilter != null ? fileFilter : TrueFileFilter.TRUE);

-            directoryFilter = FileFilterUtils.makeDirectoryOnly(directoryFilter);

-            fileFilter = FileFilterUtils.makeFileOnly(fileFilter);

-            this.filter = FileFilterUtils.orFileFilter(directoryFilter, fileFilter);

-        }

-        this.depthLimit = depthLimit;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Internal method that walks the directory hierarchy in a depth-first manner.

-     * <p>

-     * Users of this class do not need to call this method. This method will

-     * be called automatically by another (public) method on the specific subclass.

-     * <p>

-     * Writers of subclasses should call this method to start the directory walk.

-     * Once called, this method will emit events as it walks the hierarchy.

-     * The event methods have the prefix <code>handle</code>.

-     *

-     * @param startDirectory  the directory to start from, not null

-     * @param results  the collection of result objects, may be updated

-     * @throws NullPointerException if the start directory is null

-     * @throws IOException if an I/O Error occurs

-     */

-    protected final void walk(File startDirectory, Collection results) throws IOException {

-        if (startDirectory == null) {

-            throw new NullPointerException("Start Directory is null");

-        }

-        try {

-            handleStart(startDirectory, results);

-            walk(startDirectory, 0, results);

-            handleEnd(results);

-        } catch(CancelException cancel) {

-            handleCancelled(startDirectory, results, cancel);

-        }

-    }

-

-    /**

-     * Main recursive method to examine the directory hierarchy.

-     *

-     * @param directory  the directory to examine, not null

-     * @param depth  the directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    private void walk(File directory, int depth, Collection results) throws IOException {

-        checkIfCancelled(directory, depth, results);

-        if (handleDirectory(directory, depth, results)) {

-            handleDirectoryStart(directory, depth, results);

-            int childDepth = depth + 1;

-            if (depthLimit < 0 || childDepth <= depthLimit) {

-                checkIfCancelled(directory, depth, results);

-                File[] childFiles = (filter == null ? directory.listFiles() : directory.listFiles(filter));

-                if (childFiles == null) {

-                    handleRestricted(directory, childDepth, results);

-                } else {

-                    for (int i = 0; i < childFiles.length; i++) {

-                        File childFile = childFiles[i];

-                        if (childFile.isDirectory()) {

-                            walk(childFile, childDepth, results);

-                        } else {

-                            checkIfCancelled(childFile, childDepth, results);

-                            handleFile(childFile, childDepth, results);

-                            checkIfCancelled(childFile, childDepth, results);

-                        }

-                    }

-                }

-            }

-            handleDirectoryEnd(directory, depth, results);

-        }

-        checkIfCancelled(directory, depth, results);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},

-     * throwing a <code>CancelException</code> if it has.

-     * <p>

-     * Writers of subclasses should not normally call this method as it is called

-     * automatically by the walk of the tree. However, sometimes a single method,

-     * typically {@link #handleFile}, may take a long time to run. In that case,

-     * you may wish to check for cancellation by calling this method.

-     * 

-     * @param file  the current file being processed

-     * @param depth  the current file level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected final void checkIfCancelled(File file, int depth, Collection results) throws IOException {

-        if (handleIsCancelled(file, depth, results)) {

-            throw new CancelException(file, depth);

-        }

-    }

-

-    /**

-     * Overridable callback method invoked to determine if the entire walk

-     * operation should be immediately cancelled.

-     * <p>

-     * This method should be implemented by those subclasses that want to

-     * provide a public <code>cancel()</code> method available from another

-     * thread. The design pattern for the subclass should be as follows:

-     * <pre>

-     *  public class FooDirectoryWalker extends DirectoryWalker {

-     *    private volatile boolean cancelled = false;

-     *

-     *    public void cancel() {

-     *        cancelled = true;

-     *    }

-     *    private void handleIsCancelled(File file, int depth, Collection results) {

-     *        return cancelled;

-     *    }

-     *    protected void handleCancelled(File startDirectory,

-     *              Collection results, CancelException cancel) {

-     *        // implement processing required when a cancellation occurs

-     *    }

-     *  }

-     * </pre>

-     * <p>

-     * If this method returns true, then the directory walk is immediately

-     * cancelled. The next callback method will be {@link #handleCancelled}.

-     * <p>

-     * This implementation returns false.

-     *

-     * @param file  the file or directory being processed

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @return true if the walk has been cancelled

-     * @throws IOException if an I/O Error occurs

-     */

-    protected boolean handleIsCancelled(

-            File file, int depth, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-        return false;  // not cancelled

-    }

-

-    /**

-     * Overridable callback method invoked when the operation is cancelled.

-     * The file being processed when the cancellation occurred can be

-     * obtained from the exception.

-     * <p>

-     * This implementation just re-throws the {@link CancelException}.

-     *

-     * @param startDirectory  the directory that the walk started from

-     * @param results  the collection of result objects, may be updated

-     * @param cancel  the exception throw to cancel further processing

-     * containing details at the point of cancellation. 

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleCancelled(File startDirectory, Collection results,

-                       CancelException cancel) throws IOException {

-        // re-throw exception - overridable by subclass

-        throw cancel;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Overridable callback method invoked at the start of processing.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param startDirectory  the directory to start from

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleStart(File startDirectory, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Overridable callback method invoked to determine if a directory should be processed.

-     * <p>

-     * This method returns a boolean to indicate if the directory should be examined or not.

-     * If you return false, the entire directory and any subdirectories will be skipped.

-     * Note that this functionality is in addition to the filtering by file filter.

-     * <p>

-     * This implementation does nothing and returns true.

-     *

-     * @param directory  the current directory being processed

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @return true to process this directory, false to skip this directory

-     * @throws IOException if an I/O Error occurs

-     */

-    protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-        return true;  // process directory

-    }

-

-    /**

-     * Overridable callback method invoked at the start of processing each directory.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param directory  the current directory being processed

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleDirectoryStart(File directory, int depth, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Overridable callback method invoked for each (non-directory) file.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param file  the current file being processed

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleFile(File file, int depth, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Overridable callback method invoked for each restricted directory.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param directory  the restricted directory

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleRestricted(File directory, int depth, Collection results) throws IOException  {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Overridable callback method invoked at the end of processing each directory.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param directory  the directory being processed

-     * @param depth  the current directory level (starting directory = 0)

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleDirectoryEnd(File directory, int depth, Collection results) throws IOException {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Overridable callback method invoked at the end of processing.

-     * <p>

-     * This implementation does nothing.

-     *

-     * @param results  the collection of result objects, may be updated

-     * @throws IOException if an I/O Error occurs

-     */

-    protected void handleEnd(Collection results) throws IOException {

-        // do nothing - overridable by subclass

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * CancelException is thrown in DirectoryWalker to cancel the current

-     * processing.

-     */

-    public static class CancelException extends IOException {

-

-        /** Serialization id. */

-        private static final long serialVersionUID = 1347339620135041008L;

-        

-        /** The file being processed when the exception was thrown. */

-        private File file;

-        /** The file depth when the exception was thrown. */

-        private int depth = -1;

-

-        /**

-         * Constructs a <code>CancelException</code> with

-         * the file and depth when cancellation occurred.

-         *

-         * @param file  the file when the operation was cancelled, may be null

-         * @param depth  the depth when the operation was cancelled, may be null

-         */

-        public CancelException(File file, int depth) {

-            this("Operation Cancelled", file, depth);

-        }

-

-        /**

-         * Constructs a <code>CancelException</code> with

-         * an appropriate message and the file and depth when

-         * cancellation occurred.

-         *

-         * @param message  the detail message

-         * @param file  the file when the operation was cancelled

-         * @param depth  the depth when the operation was cancelled

-         */

-        public CancelException(String message, File file, int depth) {

-            super(message);

-            this.file = file;

-            this.depth = depth;

-        }

-

-        /**

-         * Return the file when the operation was cancelled.

-         *

-         * @return the file when the operation was cancelled

-         */

-        public File getFile() {

-            return file;

-        }

-

-        /**

-         * Return the depth when the operation was cancelled.

-         *

-         * @return the depth when the operation was cancelled

-         */

-        public int getDepth() {

-            return depth;

-        }

-    }

-}

+/*
+ * 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.commons.io;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+
+/**
+ * Abstract class that walks through a directory hierarchy and provides
+ * subclasses with convenient hooks to add specific behaviour.
+ * <p>
+ * This class operates with a {@link FileFilter} and maximum depth to
+ * limit the files and direcories visited.
+ * Commons IO supplies many common filter implementations in the 
+ * <a href="filefilter/package-summary.html"> filefilter</a> package.
+ * <p>
+ * The following sections describe:
+ *   <ul>
+ *      <li><a href="#example">1. Example Implementation</a> - example
+ *          <code>FileCleaner</code> implementation.</li>
+ *      <li><a href="#filter">2. Filter Example</a> - using 
+ *          {@link FileFilter}(s) with <code>DirectoryWalker</code>.</li>
+ *      <li><a href="#cancel">3. Cancellation</a> - how to implement cancellation
+ *          behaviour.</li>
+ *   </ul>
+ *
+ * <a name="example"></a>
+ * <h3>1. Example Implementation</h3>
+ *
+ * There are many possible extensions, for example, to delete all
+ * files and '.svn' directories, and return a list of deleted files:
+ * <pre>
+ *  public class FileCleaner extends DirectoryWalker {
+ *
+ *    public FileCleaner() {
+ *      super();
+ *    }
+ *
+ *    public List clean(File startDirectory) {
+ *      List results = new ArrayList();
+ *      walk(startDirectory, results);
+ *      return results;
+ *    }
+ *
+ *    protected boolean handleDirectory(File directory, int depth, Collection results) {
+ *      // delete svn directories and then skip
+ *      if (".svn".equals(directory.getName())) {
+ *        directory.delete();
+ *        return false;
+ *      } else {
+ *        return true;
+ *      }
+ *
+ *    }
+ *
+ *    protected void handleFile(File file, int depth, Collection results) {
+ *      // delete file and add to list of deleted
+ *      file.delete();
+ *      results.add(file);
+ *    }
+ *  }
+ * </pre>
+ *
+ * <a name="filter"></a>
+ * <h3>2. Filter Example</h3>
+ *
+ * Choosing which directories and files to process can be a key aspect
+ * of using this class. This information can be setup in three ways,
+ * via three different constructors.
+ * <p>
+ * The first option is to visit all directories and files.
+ * This is achieved via the no-args constructor.
+ * <p>
+ * The second constructor option is to supply a single {@link FileFilter}
+ * that describes the files and directories to visit. Care must be taken
+ * with this option as the same filter is used for both directories
+ * and files.
+ * <p>
+ * For example, if you wanted all directories which are not hidden
+ * and files which end in ".txt":
+ * <pre>
+ *  public class FooDirectoryWalker extends DirectoryWalker {
+ *    public FooDirectoryWalker(FileFilter filter) {
+ *      super(filter, -1);
+ *    }
+ *  }
+ *  
+ *  // Build up the filters and create the walker
+ *    // Create a filter for Non-hidden directories
+ *    IOFileFilter fooDirFilter = 
+ *        FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,
+ *                                      HiddenFileFilter.VISIBLE);
+ *
+ *    // Create a filter for Files ending in ".txt"
+ *    IOFileFilter fooFileFilter = 
+ *        FileFilterUtils.andFileFilter(FileFilterUtils.fileFileFilter,
+ *                                      FileFilterUtils.suffixFileFilter(".txt"));
+ *
+ *    // Combine the directory and file filters using an OR condition
+ *    java.io.FileFilter fooFilter = 
+ *        FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);
+ *
+ *    // Use the filter to construct a DirectoryWalker implementation
+ *    FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);
+ * </pre>
+ * <p>
+ * The third constructor option is to specify separate filters, one for
+ * directories and one for files. These are combined internally to form
+ * the correct <code>FileFilter</code>, something which is very easy to
+ * get wrong when attempted manually, particularly when trying to
+ * express constructs like 'any file in directories named docs'.
+ * <p>
+ * For example, if you wanted all directories which are not hidden
+ * and files which end in ".txt":
+ * <pre>
+ *  public class FooDirectoryWalker extends DirectoryWalker {
+ *    public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {
+ *      super(dirFilter, fileFilter, -1);
+ *    }
+ *  }
+ *  
+ *  // Use the filters to construct the walker
+ *  FooDirectoryWalker walker = new FooDirectoryWalker(
+ *    HiddenFileFilter.VISIBLE,
+ *    FileFilterUtils.suffixFileFilter(".txt"),
+ *  );
+ * </pre>
+ * This is much simpler than the previous example, and is why it is the preferred
+ * option for filtering.
+ *
+ * <a name="cancel"></a>
+ * <h3>3. Cancellation</h3>
+ *
+ * The DirectoryWalker contains some of the logic required for cancel processing.
+ * Subclasses must complete the implementation.
+ * <p>
+ * What <code>DirectoryWalker</code> does provide for cancellation is:
+ * <ul>
+ *    <li>{@link CancelException} which can be thrown in any of the
+ *        <i>lifecycle</i> methods to stop processing.</li>
+ *    <li>The <code>walk()</code> method traps thrown {@link CancelException}
+ *        and calls the <code>handleCancelled()</code> method, providing
+ *        a place for custom cancel processing.</li>
+ * </ul>
+ * <p>
+ * Implementations need to provide:
+ * <ul>
+ *    <li>The decision logic on whether to cancel processing or not.</li>
+ *    <li>Constructing and throwing a {@link CancelException}.</li>
+ *    <li>Custom cancel processing in the <code>handleCancelled()</code> method.
+ * </ul>
+ * <p>
+ * Two possible scenarios are envisaged for cancellation:
+ * <ul>
+ *    <li><a href="#external">3.1 External / Mult-threaded</a> - cancellation being
+ *        decided/initiated by an external process.</li>
+ *    <li><a href="#internal">3.2 Internal</a> - cancellation being decided/initiated 
+ *        from within a DirectoryWalker implementation.</li>
+ * </ul>
+ * <p>
+ * The following sections provide example implementations for these two different
+ * scenarios.
+ *
+ * <a name="external"></a>
+ * <h4>3.1 External / Multi-threaded</h4>
+ *
+ * This example provides a public <code>cancel()</code> method that can be
+ * called by another thread to stop the processing. A typical example use-case
+ * would be a cancel button on a GUI. Calling this method sets a
+ * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">
+ * volatile</a> flag to ensure it will work properly in a multi-threaded environment.
+ * The flag is returned by the <code>handleIsCancelled()</code> method, which
+ * will cause the walk to stop immediately. The <code>handleCancelled()</code>
+ * method will be the next, and last, callback method received once cancellation
+ * has occurred.
+ *
+ * <pre>
+ *  public class FooDirectoryWalker extends DirectoryWalker {
+ *
+ *    private volatile boolean cancelled = false;
+ *
+ *    public void cancel() {
+ *        cancelled = true;
+ *    }
+ *
+ *    private void handleIsCancelled(File file, int depth, Collection results) {
+ *        return cancelled;
+ *    }
+ *
+ *    protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
+ *        // implement processing required when a cancellation occurs
+ *    }
+ *  }
+ * </pre>
+ *
+ * <a name="internal"></a>
+ * <h4>3.2 Internal</h4>
+ *
+ * This shows an example of how internal cancellation processing could be implemented.
+ * <b>Note</b> the decision logic and throwing a {@link CancelException} could be implemented
+ * in any of the <i>lifecycle</i> methods. 
+ *
+ * <pre>
+ *  public class BarDirectoryWalker extends DirectoryWalker {
+ *
+ *    protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
+ *        // cancel if hidden directory
+ *        if (directory.isHidden()) {
+ *            throw new CancelException(file, depth);
+ *        }
+ *        return true;
+ *    }
+ *
+ *    protected void handleFile(File file, int depth, Collection results) throws IOException {
+ *        // cancel if read-only file
+ *        if (!file.canWrite()) {
+ *            throw new CancelException(file, depth);
+ *        }
+ *        results.add(file);
+ *    }
+ *
+ *    protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
+ *        // implement processing required when a cancellation occurs
+ *    }
+ *  }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 424748 $
+ */
+public abstract class DirectoryWalker {
+
+    /**
+     * The file filter to use to filter files and directories.
+     */
+    private final FileFilter filter;
+    /**
+     * The limit on the directory depth to walk.
+     */
+    private final int depthLimit;
+
+    /**
+     * Construct an instance with no filtering and unlimited <i>depth</i>.
+     */
+    protected DirectoryWalker() {
+        this(null, -1);
+    }
+
+    /**
+     * Construct an instance with a filter and limit the <i>depth</i> navigated to.
+     * <p>
+     * The filter controls which files and directories will be navigated to as
+     * part of the walk. The {@link FileFilterUtils} class is useful for combining
+     * various filters together. A <code>null</code> filter means that no
+     * filtering should occur and all files and directories will be visited.
+     *
+     * @param filter  the filter to apply, null means visit all files
+     * @param depthLimit  controls how <i>deep</i> the hierarchy is
+     *  navigated to (less than 0 means unlimited)
+     */
+    protected DirectoryWalker(FileFilter filter, int depthLimit) {
+        this.filter = filter;
+        this.depthLimit = depthLimit;
+    }
+
+    /**
+     * Construct an instance with a directory and a file filter and an optional
+     * limit on the <i>depth</i> navigated to.
+     * <p>
+     * The filters control which files and directories will be navigated to as part
+     * of the walk. This constructor uses {@link FileFilterUtils#makeDirectoryOnly(IOFileFilter)}
+     * and {@link FileFilterUtils#makeFileOnly(IOFileFilter)} internally to combine the filters.
+     * A <code>null</code> filter means that no filtering should occur.
+     *
+     * @param directoryFilter  the filter to apply to directories, null means visit all directories
+     * @param fileFilter  the filter to apply to files, null means visit all files
+     * @param depthLimit  controls how <i>deep</i> the hierarchy is
+     *  navigated to (less than 0 means unlimited)
+     */
+    protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, int depthLimit) {
+        if (directoryFilter == null && fileFilter == null) {
+            this.filter = null;
+        } else {
+            directoryFilter = (directoryFilter != null ? directoryFilter : TrueFileFilter.TRUE);
+            fileFilter = (fileFilter != null ? fileFilter : TrueFileFilter.TRUE);
+            directoryFilter = FileFilterUtils.makeDirectoryOnly(directoryFilter);
+            fileFilter = FileFilterUtils.makeFileOnly(fileFilter);
+            this.filter = FileFilterUtils.orFileFilter(directoryFilter, fileFilter);
+        }
+        this.depthLimit = depthLimit;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Internal method that walks the directory hierarchy in a depth-first manner.
+     * <p>
+     * Users of this class do not need to call this method. This method will
+     * be called automatically by another (public) method on the specific subclass.
+     * <p>
+     * Writers of subclasses should call this method to start the directory walk.
+     * Once called, this method will emit events as it walks the hierarchy.
+     * The event methods have the prefix <code>handle</code>.
+     *
+     * @param startDirectory  the directory to start from, not null
+     * @param results  the collection of result objects, may be updated
+     * @throws NullPointerException if the start directory is null
+     * @throws IOException if an I/O Error occurs
+     */
+    protected final void walk(File startDirectory, Collection results) throws IOException {
+        if (startDirectory == null) {
+            throw new NullPointerException("Start Directory is null");
+        }
+        try {
+            handleStart(startDirectory, results);
+            walk(startDirectory, 0, results);
+            handleEnd(results);
+        } catch(CancelException cancel) {
+            handleCancelled(startDirectory, results, cancel);
+        }
+    }
+
+    /**
+     * Main recursive method to examine the directory hierarchy.
+     *
+     * @param directory  the directory to examine, not null
+     * @param depth  the directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    private void walk(File directory, int depth, Collection results) throws IOException {
+        checkIfCancelled(directory, depth, results);
+        if (handleDirectory(directory, depth, results)) {
+            handleDirectoryStart(directory, depth, results);
+            int childDepth = depth + 1;
+            if (depthLimit < 0 || childDepth <= depthLimit) {
+                checkIfCancelled(directory, depth, results);
+                File[] childFiles = (filter == null ? directory.listFiles() : directory.listFiles(filter));
+                if (childFiles == null) {
+                    handleRestricted(directory, childDepth, results);
+                } else {
+                    for (int i = 0; i < childFiles.length; i++) {
+                        File childFile = childFiles[i];
+                        if (childFile.isDirectory()) {
+                            walk(childFile, childDepth, results);
+                        } else {
+                            checkIfCancelled(childFile, childDepth, results);
+                            handleFile(childFile, childDepth, results);
+                            checkIfCancelled(childFile, childDepth, results);
+                        }
+                    }
+                }
+            }
+            handleDirectoryEnd(directory, depth, results);
+        }
+        checkIfCancelled(directory, depth, results);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},
+     * throwing a <code>CancelException</code> if it has.
+     * <p>
+     * Writers of subclasses should not normally call this method as it is called
+     * automatically by the walk of the tree. However, sometimes a single method,
+     * typically {@link #handleFile}, may take a long time to run. In that case,
+     * you may wish to check for cancellation by calling this method.
+     * 
+     * @param file  the current file being processed
+     * @param depth  the current file level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected final void checkIfCancelled(File file, int depth, Collection results) throws IOException {
+        if (handleIsCancelled(file, depth, results)) {
+            throw new CancelException(file, depth);
+        }
+    }
+
+    /**
+     * Overridable callback method invoked to determine if the entire walk
+     * operation should be immediately cancelled.
+     * <p>
+     * This method should be implemented by those subclasses that want to
+     * provide a public <code>cancel()</code> method available from another
+     * thread. The design pattern for the subclass should be as follows:
+     * <pre>
+     *  public class FooDirectoryWalker extends DirectoryWalker {
+     *    private volatile boolean cancelled = false;
+     *
+     *    public void cancel() {
+     *        cancelled = true;
+     *    }
+     *    private void handleIsCancelled(File file, int depth, Collection results) {
+     *        return cancelled;
+     *    }
+     *    protected void handleCancelled(File startDirectory,
+     *              Collection results, CancelException cancel) {
+     *        // implement processing required when a cancellation occurs
+     *    }
+     *  }
+     * </pre>
+     * <p>
+     * If this method returns true, then the directory walk is immediately
+     * cancelled. The next callback method will be {@link #handleCancelled}.
+     * <p>
+     * This implementation returns false.
+     *
+     * @param file  the file or directory being processed
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @return true if the walk has been cancelled
+     * @throws IOException if an I/O Error occurs
+     */
+    protected boolean handleIsCancelled(
+            File file, int depth, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+        return false;  // not cancelled
+    }
+
+    /**
+     * Overridable callback method invoked when the operation is cancelled.
+     * The file being processed when the cancellation occurred can be
+     * obtained from the exception.
+     * <p>
+     * This implementation just re-throws the {@link CancelException}.
+     *
+     * @param startDirectory  the directory that the walk started from
+     * @param results  the collection of result objects, may be updated
+     * @param cancel  the exception throw to cancel further processing
+     * containing details at the point of cancellation. 
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleCancelled(File startDirectory, Collection results,
+                       CancelException cancel) throws IOException {
+        // re-throw exception - overridable by subclass
+        throw cancel;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Overridable callback method invoked at the start of processing.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param startDirectory  the directory to start from
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleStart(File startDirectory, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked to determine if a directory should be processed.
+     * <p>
+     * This method returns a boolean to indicate if the directory should be examined or not.
+     * If you return false, the entire directory and any subdirectories will be skipped.
+     * Note that this functionality is in addition to the filtering by file filter.
+     * <p>
+     * This implementation does nothing and returns true.
+     *
+     * @param directory  the current directory being processed
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @return true to process this directory, false to skip this directory
+     * @throws IOException if an I/O Error occurs
+     */
+    protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+        return true;  // process directory
+    }
+
+    /**
+     * Overridable callback method invoked at the start of processing each directory.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param directory  the current directory being processed
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleDirectoryStart(File directory, int depth, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked for each (non-directory) file.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param file  the current file being processed
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleFile(File file, int depth, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked for each restricted directory.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param directory  the restricted directory
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleRestricted(File directory, int depth, Collection results) throws IOException  {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked at the end of processing each directory.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param directory  the directory being processed
+     * @param depth  the current directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleDirectoryEnd(File directory, int depth, Collection results) throws IOException {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked at the end of processing.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    protected void handleEnd(Collection results) throws IOException {
+        // do nothing - overridable by subclass
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * CancelException is thrown in DirectoryWalker to cancel the current
+     * processing.
+     */
+    public static class CancelException extends IOException {
+
+        /** Serialization id. */
+        private static final long serialVersionUID = 1347339620135041008L;
+        
+        /** The file being processed when the exception was thrown. */
+        private File file;
+        /** The file depth when the exception was thrown. */
+        private int depth = -1;
+
+        /**
+         * Constructs a <code>CancelException</code> with
+         * the file and depth when cancellation occurred.
+         *
+         * @param file  the file when the operation was cancelled, may be null
+         * @param depth  the depth when the operation was cancelled, may be null
+         */
+        public CancelException(File file, int depth) {
+            this("Operation Cancelled", file, depth);
+        }
+
+        /**
+         * Constructs a <code>CancelException</code> with
+         * an appropriate message and the file and depth when
+         * cancellation occurred.
+         *
+         * @param message  the detail message
+         * @param file  the file when the operation was cancelled
+         * @param depth  the depth when the operation was cancelled
+         */
+        public CancelException(String message, File file, int depth) {
+            super(message);
+            this.file = file;
+            this.depth = depth;
+        }
+
+        /**
+         * Return the file when the operation was cancelled.
+         *
+         * @return the file when the operation was cancelled
+         */
+        public File getFile() {
+            return file;
+        }
+
+        /**
+         * Return the depth when the operation was cancelled.
+         *
+         * @return the depth when the operation was cancelled
+         */
+        public int getDepth() {
+            return depth;
+        }
+    }
+}
diff --git a/src/org/apache/commons/io/EndianUtils.java b/src/org/apache/commons/io/EndianUtils.java
index 810feac..e7f1eee 100644
--- a/src/org/apache/commons/io/EndianUtils.java
+++ b/src/org/apache/commons/io/EndianUtils.java
@@ -1,489 +1,489 @@
-/*

- * 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.commons.io;

-

-import java.io.EOFException;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-/**

- * Utility code for dealing with different endian systems.

- * <p>

- * Different computer architectures adopt different conventions for

- * byte ordering. In so-called "Little Endian" architectures (eg Intel),

- * the low-order byte is stored in memory at the lowest address, and

- * subsequent bytes at higher addresses. For "Big Endian" architectures

- * (eg Motorola), the situation is reversed.

- * This class helps you solve this incompatability.

- * <p>

- * Origin of code: Excalibur

- *

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @version $Id: EndianUtils.java 539632 2007-05-18 23:37:59Z bayard $

- * @see org.apache.commons.io.input.SwappedDataInputStream

- */

-public class EndianUtils {

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public EndianUtils() {

-        super();

-    }

-

-    // ========================================== Swapping routines

-

-    /**

-     * Converts a "short" value between endian systems.

-     * @param value value to convert

-     * @return the converted value

-     */

-    public static short swapShort(short value) {

-        return (short) ( ( ( ( value >> 0 ) & 0xff ) << 8 ) +

-            ( ( ( value >> 8 ) & 0xff ) << 0 ) );

-    }

-

-    /**

-     * Converts a "int" value between endian systems.

-     * @param value value to convert

-     * @return the converted value

-     */

-    public static int swapInteger(int value) {

-        return

-            ( ( ( value >> 0 ) & 0xff ) << 24 ) +

-            ( ( ( value >> 8 ) & 0xff ) << 16 ) +

-            ( ( ( value >> 16 ) & 0xff ) << 8 ) +

-            ( ( ( value >> 24 ) & 0xff ) << 0 );

-    }

-

-    /**

-     * Converts a "long" value between endian systems.

-     * @param value value to convert

-     * @return the converted value

-     */

-    public static long swapLong(long value) {

-        return

-            ( ( ( value >> 0 ) & 0xff ) << 56 ) +

-            ( ( ( value >> 8 ) & 0xff ) << 48 ) +

-            ( ( ( value >> 16 ) & 0xff ) << 40 ) +

-            ( ( ( value >> 24 ) & 0xff ) << 32 ) +

-            ( ( ( value >> 32 ) & 0xff ) << 24 ) +

-            ( ( ( value >> 40 ) & 0xff ) << 16 ) +

-            ( ( ( value >> 48 ) & 0xff ) << 8 ) +

-            ( ( ( value >> 56 ) & 0xff ) << 0 );

-    }

-

-    /**

-     * Converts a "float" value between endian systems.

-     * @param value value to convert

-     * @return the converted value

-     */

-    public static float swapFloat(float value) {

-        return Float.intBitsToFloat( swapInteger( Float.floatToIntBits( value ) ) );

-    }

-

-    /**

-     * Converts a "double" value between endian systems.

-     * @param value value to convert

-     * @return the converted value

-     */

-    public static double swapDouble(double value) {

-        return Double.longBitsToDouble( swapLong( Double.doubleToLongBits( value ) ) );

-    }

-

-    // ========================================== Swapping read/write routines

-

-    /**

-     * Writes a "short" value to a byte array at a given offset. The value is

-     * converted to the opposed endian system while writing.

-     * @param data target byte array

-     * @param offset starting offset in the byte array

-     * @param value value to write

-     */

-    public static void writeSwappedShort(byte[] data, int offset, short value) {

-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );

-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );

-    }

-

-    /**

-     * Reads a "short" value from a byte array at a given offset. The value is

-     * converted to the opposed endian system while reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static short readSwappedShort(byte[] data, int offset) {

-        return (short)( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +

-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );

-    }

-

-    /**

-     * Reads an unsigned short (16-bit) value from a byte array at a given

-     * offset. The value is converted to the opposed endian system while

-     * reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static int readSwappedUnsignedShort(byte[] data, int offset) {

-        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +

-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );

-    }

-

-    /**

-     * Writes a "int" value to a byte array at a given offset. The value is

-     * converted to the opposed endian system while writing.

-     * @param data target byte array

-     * @param offset starting offset in the byte array

-     * @param value value to write

-     */

-    public static void writeSwappedInteger(byte[] data, int offset, int value) {

-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );

-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );

-        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );

-        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );

-    }

-

-    /**

-     * Reads a "int" value from a byte array at a given offset. The value is

-     * converted to the opposed endian system while reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static int readSwappedInteger(byte[] data, int offset) {

-        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +

-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +

-            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +

-            ( ( data[ offset + 3 ] & 0xff ) << 24 ) );

-    }

-

-    /**

-     * Reads an unsigned integer (32-bit) value from a byte array at a given

-     * offset. The value is converted to the opposed endian system while

-     * reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static long readSwappedUnsignedInteger(byte[] data, int offset) {

-        long low = ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +

-                     ( ( data[ offset + 1 ] & 0xff ) << 8 ) +

-                     ( ( data[ offset + 2 ] & 0xff ) << 16 ) );

-

-        long high = data[ offset + 3 ] & 0xff;

-

-        return (high << 24) + (0xffffffffL & low); 

-    }

-

-    /**

-     * Writes a "long" value to a byte array at a given offset. The value is

-     * converted to the opposed endian system while writing.

-     * @param data target byte array

-     * @param offset starting offset in the byte array

-     * @param value value to write

-     */

-    public static void writeSwappedLong(byte[] data, int offset, long value) {

-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );

-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );

-        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );

-        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );

-        data[ offset + 4 ] = (byte)( ( value >> 32 ) & 0xff );

-        data[ offset + 5 ] = (byte)( ( value >> 40 ) & 0xff );

-        data[ offset + 6 ] = (byte)( ( value >> 48 ) & 0xff );

-        data[ offset + 7 ] = (byte)( ( value >> 56 ) & 0xff );

-    }

-

-    /**

-     * Reads a "long" value from a byte array at a given offset. The value is

-     * converted to the opposed endian system while reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static long readSwappedLong(byte[] data, int offset) {

-        long low = 

-            ( ( data[ offset + 0 ] & 0xff ) << 0 ) +

-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +

-            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +

-            ( ( data[ offset + 3 ] & 0xff ) << 24 );

-        long high = 

-            ( ( data[ offset + 4 ] & 0xff ) << 0 ) +

-            ( ( data[ offset + 5 ] & 0xff ) << 8 ) +

-            ( ( data[ offset + 6 ] & 0xff ) << 16 ) +

-            ( ( data[ offset + 7 ] & 0xff ) << 24 );

-        return (high << 32) + (0xffffffffL & low); 

-    }

-

-    /**

-     * Writes a "float" value to a byte array at a given offset. The value is

-     * converted to the opposed endian system while writing.

-     * @param data target byte array

-     * @param offset starting offset in the byte array

-     * @param value value to write

-     */

-    public static void writeSwappedFloat(byte[] data, int offset, float value) {

-        writeSwappedInteger( data, offset, Float.floatToIntBits( value ) );

-    }

-

-    /**

-     * Reads a "float" value from a byte array at a given offset. The value is

-     * converted to the opposed endian system while reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static float readSwappedFloat(byte[] data, int offset) {

-        return Float.intBitsToFloat( readSwappedInteger( data, offset ) );

-    }

-

-    /**

-     * Writes a "double" value to a byte array at a given offset. The value is

-     * converted to the opposed endian system while writing.

-     * @param data target byte array

-     * @param offset starting offset in the byte array

-     * @param value value to write

-     */

-    public static void writeSwappedDouble(byte[] data, int offset, double value) {

-        writeSwappedLong( data, offset, Double.doubleToLongBits( value ) );

-    }

-

-    /**

-     * Reads a "double" value from a byte array at a given offset. The value is

-     * converted to the opposed endian system while reading.

-     * @param data source byte array

-     * @param offset starting offset in the byte array

-     * @return the value read

-     */

-    public static double readSwappedDouble(byte[] data, int offset) {

-        return Double.longBitsToDouble( readSwappedLong( data, offset ) );

-    }

-

-    /**

-     * Writes a "short" value to an OutputStream. The value is

-     * converted to the opposed endian system while writing.

-     * @param output target OutputStream

-     * @param value value to write

-     * @throws IOException in case of an I/O problem

-     */

-    public static void writeSwappedShort(OutputStream output, short value)

-        throws IOException

-    {

-        output.write( (byte)( ( value >> 0 ) & 0xff ) );

-        output.write( (byte)( ( value >> 8 ) & 0xff ) );

-    }

-

-    /**

-     * Reads a "short" value from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static short readSwappedShort(InputStream input)

-        throws IOException

-    {

-        return (short)( ( ( read( input ) & 0xff ) << 0 ) +

-            ( ( read( input ) & 0xff ) << 8 ) );

-    }

-

-    /**

-     * Reads a unsigned short (16-bit) from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static int readSwappedUnsignedShort(InputStream input)

-        throws IOException

-    {

-        int value1 = read( input );

-        int value2 = read( input );

-

-        return ( ( ( value1 & 0xff ) << 0 ) +

-            ( ( value2 & 0xff ) << 8 ) );

-    }

-

-    /**

-     * Writes a "int" value to an OutputStream. The value is

-     * converted to the opposed endian system while writing.

-     * @param output target OutputStream

-     * @param value value to write

-     * @throws IOException in case of an I/O problem

-     */

-    public static void writeSwappedInteger(OutputStream output, int value)

-        throws IOException

-    {

-        output.write( (byte)( ( value >> 0 ) & 0xff ) );

-        output.write( (byte)( ( value >> 8 ) & 0xff ) );

-        output.write( (byte)( ( value >> 16 ) & 0xff ) );

-        output.write( (byte)( ( value >> 24 ) & 0xff ) );

-    }

-

-    /**

-     * Reads a "int" value from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static int readSwappedInteger(InputStream input)

-        throws IOException

-    {

-        int value1 = read( input );

-        int value2 = read( input );

-        int value3 = read( input );

-        int value4 = read( input );

-

-        return ( ( value1 & 0xff ) << 0 ) +

-            ( ( value2 & 0xff ) << 8 ) +

-            ( ( value3 & 0xff ) << 16 ) +

-            ( ( value4 & 0xff ) << 24 );

-    }

-

-    /**

-     * Reads a unsigned integer (32-bit) from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static long readSwappedUnsignedInteger(InputStream input)

-        throws IOException

-    {

-        int value1 = read( input );

-        int value2 = read( input );

-        int value3 = read( input );

-        int value4 = read( input );

-

-        long low = ( ( ( value1 & 0xff ) << 0 ) +

-                     ( ( value2 & 0xff ) << 8 ) +

-                     ( ( value3 & 0xff ) << 16 ) );

-

-        long high = value4 & 0xff;

-

-        return (high << 24) + (0xffffffffL & low); 

-    }

-

-    /**

-     * Writes a "long" value to an OutputStream. The value is

-     * converted to the opposed endian system while writing.

-     * @param output target OutputStream

-     * @param value value to write

-     * @throws IOException in case of an I/O problem

-     */

-    public static void writeSwappedLong(OutputStream output, long value)

-        throws IOException

-    {

-        output.write( (byte)( ( value >> 0 ) & 0xff ) );

-        output.write( (byte)( ( value >> 8 ) & 0xff ) );

-        output.write( (byte)( ( value >> 16 ) & 0xff ) );

-        output.write( (byte)( ( value >> 24 ) & 0xff ) );

-        output.write( (byte)( ( value >> 32 ) & 0xff ) );

-        output.write( (byte)( ( value >> 40 ) & 0xff ) );

-        output.write( (byte)( ( value >> 48 ) & 0xff ) );

-        output.write( (byte)( ( value >> 56 ) & 0xff ) );

-    }

-

-    /**

-     * Reads a "long" value from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static long readSwappedLong(InputStream input)

-        throws IOException

-    {

-        byte[] bytes = new byte[8];

-        for ( int i=0; i<8; i++ ) {

-            bytes[i] = (byte) read( input );

-        }

-        return readSwappedLong( bytes, 0 );

-    }

-

-    /**

-     * Writes a "float" value to an OutputStream. The value is

-     * converted to the opposed endian system while writing.

-     * @param output target OutputStream

-     * @param value value to write

-     * @throws IOException in case of an I/O problem

-     */

-    public static void writeSwappedFloat(OutputStream output, float value)

-        throws IOException

-    {

-        writeSwappedInteger( output, Float.floatToIntBits( value ) );

-    }

-

-    /**

-     * Reads a "float" value from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static float readSwappedFloat(InputStream input)

-        throws IOException

-    {

-        return Float.intBitsToFloat( readSwappedInteger( input ) );

-    }

-

-    /**

-     * Writes a "double" value to an OutputStream. The value is

-     * converted to the opposed endian system while writing.

-     * @param output target OutputStream

-     * @param value value to write

-     * @throws IOException in case of an I/O problem

-     */

-    public static void writeSwappedDouble(OutputStream output, double value)

-        throws IOException

-    {

-        writeSwappedLong( output, Double.doubleToLongBits( value ) );

-    }

-

-    /**

-     * Reads a "double" value from an InputStream. The value is

-     * converted to the opposed endian system while reading.

-     * @param input source InputStream

-     * @return the value just read

-     * @throws IOException in case of an I/O problem

-     */

-    public static double readSwappedDouble(InputStream input)

-        throws IOException

-    {

-        return Double.longBitsToDouble( readSwappedLong( input ) );

-    }

-

-    /**

-     * Reads the next byte from the input stream.

-     * @param input  the stream

-     * @return the byte

-     * @throws IOException if the end of file is reached

-     */

-    private static int read(InputStream input)

-        throws IOException

-    {

-        int value = input.read();

-

-        if( -1 == value ) {

-            throw new EOFException( "Unexpected EOF reached" );

-        }

-

-        return value;

-    }

-}

+/*
+ * 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.commons.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Utility code for dealing with different endian systems.
+ * <p>
+ * Different computer architectures adopt different conventions for
+ * byte ordering. In so-called "Little Endian" architectures (eg Intel),
+ * the low-order byte is stored in memory at the lowest address, and
+ * subsequent bytes at higher addresses. For "Big Endian" architectures
+ * (eg Motorola), the situation is reversed.
+ * This class helps you solve this incompatability.
+ * <p>
+ * Origin of code: Excalibur
+ *
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Id: EndianUtils.java 539632 2007-05-18 23:37:59Z bayard $
+ * @see org.apache.commons.io.input.SwappedDataInputStream
+ */
+public class EndianUtils {
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public EndianUtils() {
+        super();
+    }
+
+    // ========================================== Swapping routines
+
+    /**
+     * Converts a "short" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static short swapShort(short value) {
+        return (short) ( ( ( ( value >> 0 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 0 ) );
+    }
+
+    /**
+     * Converts a "int" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static int swapInteger(int value) {
+        return
+            ( ( ( value >> 0 ) & 0xff ) << 24 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 16 ) +
+            ( ( ( value >> 16 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 24 ) & 0xff ) << 0 );
+    }
+
+    /**
+     * Converts a "long" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static long swapLong(long value) {
+        return
+            ( ( ( value >> 0 ) & 0xff ) << 56 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 48 ) +
+            ( ( ( value >> 16 ) & 0xff ) << 40 ) +
+            ( ( ( value >> 24 ) & 0xff ) << 32 ) +
+            ( ( ( value >> 32 ) & 0xff ) << 24 ) +
+            ( ( ( value >> 40 ) & 0xff ) << 16 ) +
+            ( ( ( value >> 48 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 56 ) & 0xff ) << 0 );
+    }
+
+    /**
+     * Converts a "float" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static float swapFloat(float value) {
+        return Float.intBitsToFloat( swapInteger( Float.floatToIntBits( value ) ) );
+    }
+
+    /**
+     * Converts a "double" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static double swapDouble(double value) {
+        return Double.longBitsToDouble( swapLong( Double.doubleToLongBits( value ) ) );
+    }
+
+    // ========================================== Swapping read/write routines
+
+    /**
+     * Writes a "short" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
+     */
+    public static void writeSwappedShort(byte[] data, int offset, short value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+    }
+
+    /**
+     * Reads a "short" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static short readSwappedShort(byte[] data, int offset) {
+        return (short)( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
+    }
+
+    /**
+     * Reads an unsigned short (16-bit) value from a byte array at a given
+     * offset. The value is converted to the opposed endian system while
+     * reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static int readSwappedUnsignedShort(byte[] data, int offset) {
+        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
+    }
+
+    /**
+     * Writes a "int" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
+     */
+    public static void writeSwappedInteger(byte[] data, int offset, int value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
+        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
+    }
+
+    /**
+     * Reads a "int" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static int readSwappedInteger(byte[] data, int offset) {
+        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +
+            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +
+            ( ( data[ offset + 3 ] & 0xff ) << 24 ) );
+    }
+
+    /**
+     * Reads an unsigned integer (32-bit) value from a byte array at a given
+     * offset. The value is converted to the opposed endian system while
+     * reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static long readSwappedUnsignedInteger(byte[] data, int offset) {
+        long low = ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+                     ( ( data[ offset + 1 ] & 0xff ) << 8 ) +
+                     ( ( data[ offset + 2 ] & 0xff ) << 16 ) );
+
+        long high = data[ offset + 3 ] & 0xff;
+
+        return (high << 24) + (0xffffffffL & low); 
+    }
+
+    /**
+     * Writes a "long" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
+     */
+    public static void writeSwappedLong(byte[] data, int offset, long value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
+        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
+        data[ offset + 4 ] = (byte)( ( value >> 32 ) & 0xff );
+        data[ offset + 5 ] = (byte)( ( value >> 40 ) & 0xff );
+        data[ offset + 6 ] = (byte)( ( value >> 48 ) & 0xff );
+        data[ offset + 7 ] = (byte)( ( value >> 56 ) & 0xff );
+    }
+
+    /**
+     * Reads a "long" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static long readSwappedLong(byte[] data, int offset) {
+        long low = 
+            ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +
+            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +
+            ( ( data[ offset + 3 ] & 0xff ) << 24 );
+        long high = 
+            ( ( data[ offset + 4 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 5 ] & 0xff ) << 8 ) +
+            ( ( data[ offset + 6 ] & 0xff ) << 16 ) +
+            ( ( data[ offset + 7 ] & 0xff ) << 24 );
+        return (high << 32) + (0xffffffffL & low); 
+    }
+
+    /**
+     * Writes a "float" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
+     */
+    public static void writeSwappedFloat(byte[] data, int offset, float value) {
+        writeSwappedInteger( data, offset, Float.floatToIntBits( value ) );
+    }
+
+    /**
+     * Reads a "float" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static float readSwappedFloat(byte[] data, int offset) {
+        return Float.intBitsToFloat( readSwappedInteger( data, offset ) );
+    }
+
+    /**
+     * Writes a "double" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
+     */
+    public static void writeSwappedDouble(byte[] data, int offset, double value) {
+        writeSwappedLong( data, offset, Double.doubleToLongBits( value ) );
+    }
+
+    /**
+     * Reads a "double" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
+     */
+    public static double readSwappedDouble(byte[] data, int offset) {
+        return Double.longBitsToDouble( readSwappedLong( data, offset ) );
+    }
+
+    /**
+     * Writes a "short" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
+     */
+    public static void writeSwappedShort(OutputStream output, short value)
+        throws IOException
+    {
+        output.write( (byte)( ( value >> 0 ) & 0xff ) );
+        output.write( (byte)( ( value >> 8 ) & 0xff ) );
+    }
+
+    /**
+     * Reads a "short" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static short readSwappedShort(InputStream input)
+        throws IOException
+    {
+        return (short)( ( ( read( input ) & 0xff ) << 0 ) +
+            ( ( read( input ) & 0xff ) << 8 ) );
+    }
+
+    /**
+     * Reads a unsigned short (16-bit) from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static int readSwappedUnsignedShort(InputStream input)
+        throws IOException
+    {
+        int value1 = read( input );
+        int value2 = read( input );
+
+        return ( ( ( value1 & 0xff ) << 0 ) +
+            ( ( value2 & 0xff ) << 8 ) );
+    }
+
+    /**
+     * Writes a "int" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
+     */
+    public static void writeSwappedInteger(OutputStream output, int value)
+        throws IOException
+    {
+        output.write( (byte)( ( value >> 0 ) & 0xff ) );
+        output.write( (byte)( ( value >> 8 ) & 0xff ) );
+        output.write( (byte)( ( value >> 16 ) & 0xff ) );
+        output.write( (byte)( ( value >> 24 ) & 0xff ) );
+    }
+
+    /**
+     * Reads a "int" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static int readSwappedInteger(InputStream input)
+        throws IOException
+    {
+        int value1 = read( input );
+        int value2 = read( input );
+        int value3 = read( input );
+        int value4 = read( input );
+
+        return ( ( value1 & 0xff ) << 0 ) +
+            ( ( value2 & 0xff ) << 8 ) +
+            ( ( value3 & 0xff ) << 16 ) +
+            ( ( value4 & 0xff ) << 24 );
+    }
+
+    /**
+     * Reads a unsigned integer (32-bit) from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static long readSwappedUnsignedInteger(InputStream input)
+        throws IOException
+    {
+        int value1 = read( input );
+        int value2 = read( input );
+        int value3 = read( input );
+        int value4 = read( input );
+
+        long low = ( ( ( value1 & 0xff ) << 0 ) +
+                     ( ( value2 & 0xff ) << 8 ) +
+                     ( ( value3 & 0xff ) << 16 ) );
+
+        long high = value4 & 0xff;
+
+        return (high << 24) + (0xffffffffL & low); 
+    }
+
+    /**
+     * Writes a "long" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
+     */
+    public static void writeSwappedLong(OutputStream output, long value)
+        throws IOException
+    {
+        output.write( (byte)( ( value >> 0 ) & 0xff ) );
+        output.write( (byte)( ( value >> 8 ) & 0xff ) );
+        output.write( (byte)( ( value >> 16 ) & 0xff ) );
+        output.write( (byte)( ( value >> 24 ) & 0xff ) );
+        output.write( (byte)( ( value >> 32 ) & 0xff ) );
+        output.write( (byte)( ( value >> 40 ) & 0xff ) );
+        output.write( (byte)( ( value >> 48 ) & 0xff ) );
+        output.write( (byte)( ( value >> 56 ) & 0xff ) );
+    }
+
+    /**
+     * Reads a "long" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static long readSwappedLong(InputStream input)
+        throws IOException
+    {
+        byte[] bytes = new byte[8];
+        for ( int i=0; i<8; i++ ) {
+            bytes[i] = (byte) read( input );
+        }
+        return readSwappedLong( bytes, 0 );
+    }
+
+    /**
+     * Writes a "float" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
+     */
+    public static void writeSwappedFloat(OutputStream output, float value)
+        throws IOException
+    {
+        writeSwappedInteger( output, Float.floatToIntBits( value ) );
+    }
+
+    /**
+     * Reads a "float" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static float readSwappedFloat(InputStream input)
+        throws IOException
+    {
+        return Float.intBitsToFloat( readSwappedInteger( input ) );
+    }
+
+    /**
+     * Writes a "double" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
+     */
+    public static void writeSwappedDouble(OutputStream output, double value)
+        throws IOException
+    {
+        writeSwappedLong( output, Double.doubleToLongBits( value ) );
+    }
+
+    /**
+     * Reads a "double" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
+     */
+    public static double readSwappedDouble(InputStream input)
+        throws IOException
+    {
+        return Double.longBitsToDouble( readSwappedLong( input ) );
+    }
+
+    /**
+     * Reads the next byte from the input stream.
+     * @param input  the stream
+     * @return the byte
+     * @throws IOException if the end of file is reached
+     */
+    private static int read(InputStream input)
+        throws IOException
+    {
+        int value = input.read();
+
+        if( -1 == value ) {
+            throw new EOFException( "Unexpected EOF reached" );
+        }
+
+        return value;
+    }
+}
diff --git a/src/org/apache/commons/io/FileCleaner.java b/src/org/apache/commons/io/FileCleaner.java
index 5f18996..71f0c92 100644
--- a/src/org/apache/commons/io/FileCleaner.java
+++ b/src/org/apache/commons/io/FileCleaner.java
@@ -1,161 +1,161 @@
-/*

- * 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.commons.io;

-

-import java.io.File;

-

-/**

- * Keeps track of files awaiting deletion, and deletes them when an associated

- * marker object is reclaimed by the garbage collector.

- * <p>

- * This utility creates a background thread to handle file deletion.

- * Each file to be deleted is registered with a handler object.

- * When the handler object is garbage collected, the file is deleted.

- * <p>

- * In an environment with multiple class loaders (a servlet container, for

- * example), you should consider stopping the background thread if it is no

- * longer needed. This is done by invoking the method

- * {@link #exitWhenFinished}, typically in

- * {@link javax.servlet.ServletContextListener#contextDestroyed} or similar.

- *

- * @author Noel Bergman

- * @author Martin Cooper

- * @version $Id: FileCleaner.java 553012 2007-07-03 23:01:07Z ggregory $

- * @deprecated Use {@link FileCleaningTracker}

- */

-@Deprecated

-public class FileCleaner {

-    /**

-     * The instance to use for the deprecated, static methods.

-     */

-    static final FileCleaningTracker theInstance = new FileCleaningTracker();

-

-    //-----------------------------------------------------------------------

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.

-     *

-     * @param file  the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @throws NullPointerException if the file is null

-     * @deprecated Use {@link FileCleaningTracker#track(File, Object)}.

-     */

-    @Deprecated

-    public static void track(File file, Object marker) {

-        theInstance.track(file, marker);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The speified deletion strategy is used.

-     *

-     * @param file  the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @param deleteStrategy  the strategy to delete the file, null means normal

-     * @throws NullPointerException if the file is null

-     * @deprecated Use {@link FileCleaningTracker#track(File, Object, FileDeleteStrategy)}.

-     */

-    @Deprecated

-    public static void track(File file, Object marker, FileDeleteStrategy deleteStrategy) {

-        theInstance.track(file, marker, deleteStrategy);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.

-     *

-     * @param path  the full path to the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @throws NullPointerException if the path is null

-     * @deprecated Use {@link FileCleaningTracker#track(String, Object)}.

-     */

-    @Deprecated

-    public static void track(String path, Object marker) {

-        theInstance.track(path, marker);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The speified deletion strategy is used.

-     *

-     * @param path  the full path to the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @param deleteStrategy  the strategy to delete the file, null means normal

-     * @throws NullPointerException if the path is null

-     * @deprecated Use {@link FileCleaningTracker#track(String, Object, FileDeleteStrategy)}.

-     */

-    @Deprecated

-    public static void track(String path, Object marker, FileDeleteStrategy deleteStrategy) {

-        theInstance.track(path, marker, deleteStrategy);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Retrieve the number of files currently being tracked, and therefore

-     * awaiting deletion.

-     *

-     * @return the number of files being tracked

-     * @deprecated Use {@link FileCleaningTracker#getTrackCount()}.

-     */

-    @Deprecated

-    public static int getTrackCount() {

-        return theInstance.getTrackCount();

-    }

-

-    /**

-     * Call this method to cause the file cleaner thread to terminate when

-     * there are no more objects being tracked for deletion.

-     * <p>

-     * In a simple environment, you don't need this method as the file cleaner

-     * thread will simply exit when the JVM exits. In a more complex environment,

-     * with multiple class loaders (such as an application server), you should be

-     * aware that the file cleaner thread will continue running even if the class

-     * loader it was started from terminates. This can consitute a memory leak.

-     * <p>

-     * For example, suppose that you have developed a web application, which

-     * contains the commons-io jar file in your WEB-INF/lib directory. In other

-     * words, the FileCleaner class is loaded through the class loader of your

-     * web application. If the web application is terminated, but the servlet

-     * container is still running, then the file cleaner thread will still exist,

-     * posing a memory leak.

-     * <p>

-     * This method allows the thread to be terminated. Simply call this method

-     * in the resource cleanup code, such as {@link javax.servlet.ServletContextListener#contextDestroyed}.

-     * One called, no new objects can be tracked by the file cleaner.

-     * @deprecated Use {@link FileCleaningTracker#exitWhenFinished()}.

-     */

-    @Deprecated

-    public static synchronized void exitWhenFinished() {

-        theInstance.exitWhenFinished();

-    }

-

-    /**

-     * Returns the singleton instance, which is used by the deprecated, static methods.

-     * This is mainly useful for code, which wants to support the new

-     * {@link FileCleaningTracker} class while maintain compatibility with the

-     * deprecated {@link FileCleaner}.

-     * 

-     * @return the singleton instance

-     */

-    public static FileCleaningTracker getInstance() {

-        return theInstance;

-    }

-}

+/*
+ * 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.commons.io;
+
+import java.io.File;
+
+/**
+ * Keeps track of files awaiting deletion, and deletes them when an associated
+ * marker object is reclaimed by the garbage collector.
+ * <p>
+ * This utility creates a background thread to handle file deletion.
+ * Each file to be deleted is registered with a handler object.
+ * When the handler object is garbage collected, the file is deleted.
+ * <p>
+ * In an environment with multiple class loaders (a servlet container, for
+ * example), you should consider stopping the background thread if it is no
+ * longer needed. This is done by invoking the method
+ * {@link #exitWhenFinished}, typically in
+ * {@link javax.servlet.ServletContextListener#contextDestroyed} or similar.
+ *
+ * @author Noel Bergman
+ * @author Martin Cooper
+ * @version $Id: FileCleaner.java 553012 2007-07-03 23:01:07Z ggregory $
+ * @deprecated Use {@link FileCleaningTracker}
+ */
+@Deprecated
+public class FileCleaner {
+    /**
+     * The instance to use for the deprecated, static methods.
+     */
+    static final FileCleaningTracker theInstance = new FileCleaningTracker();
+
+    //-----------------------------------------------------------------------
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the file is null
+     * @deprecated Use {@link FileCleaningTracker#track(File, Object)}.
+     */
+    @Deprecated
+    public static void track(File file, Object marker) {
+        theInstance.track(file, marker);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The speified deletion strategy is used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the file is null
+     * @deprecated Use {@link FileCleaningTracker#track(File, Object, FileDeleteStrategy)}.
+     */
+    @Deprecated
+    public static void track(File file, Object marker, FileDeleteStrategy deleteStrategy) {
+        theInstance.track(file, marker, deleteStrategy);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the path is null
+     * @deprecated Use {@link FileCleaningTracker#track(String, Object)}.
+     */
+    @Deprecated
+    public static void track(String path, Object marker) {
+        theInstance.track(path, marker);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The speified deletion strategy is used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the path is null
+     * @deprecated Use {@link FileCleaningTracker#track(String, Object, FileDeleteStrategy)}.
+     */
+    @Deprecated
+    public static void track(String path, Object marker, FileDeleteStrategy deleteStrategy) {
+        theInstance.track(path, marker, deleteStrategy);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Retrieve the number of files currently being tracked, and therefore
+     * awaiting deletion.
+     *
+     * @return the number of files being tracked
+     * @deprecated Use {@link FileCleaningTracker#getTrackCount()}.
+     */
+    @Deprecated
+    public static int getTrackCount() {
+        return theInstance.getTrackCount();
+    }
+
+    /**
+     * Call this method to cause the file cleaner thread to terminate when
+     * there are no more objects being tracked for deletion.
+     * <p>
+     * In a simple environment, you don't need this method as the file cleaner
+     * thread will simply exit when the JVM exits. In a more complex environment,
+     * with multiple class loaders (such as an application server), you should be
+     * aware that the file cleaner thread will continue running even if the class
+     * loader it was started from terminates. This can consitute a memory leak.
+     * <p>
+     * For example, suppose that you have developed a web application, which
+     * contains the commons-io jar file in your WEB-INF/lib directory. In other
+     * words, the FileCleaner class is loaded through the class loader of your
+     * web application. If the web application is terminated, but the servlet
+     * container is still running, then the file cleaner thread will still exist,
+     * posing a memory leak.
+     * <p>
+     * This method allows the thread to be terminated. Simply call this method
+     * in the resource cleanup code, such as {@link javax.servlet.ServletContextListener#contextDestroyed}.
+     * One called, no new objects can be tracked by the file cleaner.
+     * @deprecated Use {@link FileCleaningTracker#exitWhenFinished()}.
+     */
+    @Deprecated
+    public static synchronized void exitWhenFinished() {
+        theInstance.exitWhenFinished();
+    }
+
+    /**
+     * Returns the singleton instance, which is used by the deprecated, static methods.
+     * This is mainly useful for code, which wants to support the new
+     * {@link FileCleaningTracker} class while maintain compatibility with the
+     * deprecated {@link FileCleaner}.
+     * 
+     * @return the singleton instance
+     */
+    public static FileCleaningTracker getInstance() {
+        return theInstance;
+    }
+}
diff --git a/src/org/apache/commons/io/FileCleaningTracker.java b/src/org/apache/commons/io/FileCleaningTracker.java
index 4074e6a..b5fb806 100644
--- a/src/org/apache/commons/io/FileCleaningTracker.java
+++ b/src/org/apache/commons/io/FileCleaningTracker.java
@@ -1,259 +1,259 @@
-/*

- * 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.commons.io;

-

-import java.io.File;

-import java.lang.ref.PhantomReference;

-import java.lang.ref.ReferenceQueue;

-import java.util.Collection;

-import java.util.Vector;

-

-/**

- * Keeps track of files awaiting deletion, and deletes them when an associated

- * marker object is reclaimed by the garbage collector.

- * <p>

- * This utility creates a background thread to handle file deletion.

- * Each file to be deleted is registered with a handler object.

- * When the handler object is garbage collected, the file is deleted.

- * <p>

- * In an environment with multiple class loaders (a servlet container, for

- * example), you should consider stopping the background thread if it is no

- * longer needed. This is done by invoking the method

- * {@link #exitWhenFinished}, typically in

- * {@link javax.servlet.ServletContextListener#contextDestroyed} or similar.

- *

- * @author Noel Bergman

- * @author Martin Cooper

- * @version $Id: FileCleaner.java 490987 2006-12-29 12:11:48Z scolebourne $

- */

-public class FileCleaningTracker {

-    /**

-     * Queue of <code>Tracker</code> instances being watched.

-     */

-    ReferenceQueue<Object> /* Tracker */ q = new ReferenceQueue<Object>();

-    /**

-     * Collection of <code>Tracker</code> instances in existence.

-     */

-    final Collection<Tracker> /* Tracker */ trackers = new Vector<Tracker>();  // synchronized

-    /**

-     * Whether to terminate the thread when the tracking is complete.

-     */

-    volatile boolean exitWhenFinished = false;

-    /**

-     * The thread that will clean up registered files.

-     */

-    Thread reaper;

-

-    //-----------------------------------------------------------------------

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.

-     *

-     * @param file  the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @throws NullPointerException if the file is null

-     */

-    public void track(File file, Object marker) {

-        track(file, marker, (FileDeleteStrategy) null);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The speified deletion strategy is used.

-     *

-     * @param file  the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @param deleteStrategy  the strategy to delete the file, null means normal

-     * @throws NullPointerException if the file is null

-     */

-    public void track(File file, Object marker, FileDeleteStrategy deleteStrategy) {

-        if (file == null) {

-            throw new NullPointerException("The file must not be null");

-        }

-        addTracker(file.getPath(), marker, deleteStrategy);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.

-     *

-     * @param path  the full path to the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @throws NullPointerException if the path is null

-     */

-    public void track(String path, Object marker) {

-        track(path, marker, (FileDeleteStrategy) null);

-    }

-

-    /**

-     * Track the specified file, using the provided marker, deleting the file

-     * when the marker instance is garbage collected.

-     * The speified deletion strategy is used.

-     *

-     * @param path  the full path to the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @param deleteStrategy  the strategy to delete the file, null means normal

-     * @throws NullPointerException if the path is null

-     */

-    public void track(String path, Object marker, FileDeleteStrategy deleteStrategy) {

-        if (path == null) {

-            throw new NullPointerException("The path must not be null");

-        }

-        addTracker(path, marker, deleteStrategy);

-    }

-

-    /**

-     * Adds a tracker to the list of trackers.

-     *

-     * @param path  the full path to the file to be tracked, not null

-     * @param marker  the marker object used to track the file, not null

-     * @param deleteStrategy  the strategy to delete the file, null means normal

-     */

-    private synchronized void addTracker(String path, Object marker, FileDeleteStrategy deleteStrategy) {

-        // synchronized block protects reaper

-        if (exitWhenFinished) {

-            throw new IllegalStateException("No new trackers can be added once exitWhenFinished() is called");

-        }

-        if (reaper == null) {

-            reaper = new Reaper();

-            reaper.start();

-        }

-        trackers.add(new Tracker(path, deleteStrategy, marker, q));

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Retrieve the number of files currently being tracked, and therefore

-     * awaiting deletion.

-     *

-     * @return the number of files being tracked

-     */

-    public int getTrackCount() {

-        return trackers.size();

-    }

-

-    /**

-     * Call this method to cause the file cleaner thread to terminate when

-     * there are no more objects being tracked for deletion.

-     * <p>

-     * In a simple environment, you don't need this method as the file cleaner

-     * thread will simply exit when the JVM exits. In a more complex environment,

-     * with multiple class loaders (such as an application server), you should be

-     * aware that the file cleaner thread will continue running even if the class

-     * loader it was started from terminates. This can consitute a memory leak.

-     * <p>

-     * For example, suppose that you have developed a web application, which

-     * contains the commons-io jar file in your WEB-INF/lib directory. In other

-     * words, the FileCleaner class is loaded through the class loader of your

-     * web application. If the web application is terminated, but the servlet

-     * container is still running, then the file cleaner thread will still exist,

-     * posing a memory leak.

-     * <p>

-     * This method allows the thread to be terminated. Simply call this method

-     * in the resource cleanup code, such as {@link javax.servlet.ServletContextListener#contextDestroyed}.

-     * One called, no new objects can be tracked by the file cleaner.

-     */

-    public synchronized void exitWhenFinished() {

-        // synchronized block protects reaper

-        exitWhenFinished = true;

-        if (reaper != null) {

-            synchronized (reaper) {

-                reaper.interrupt();

-            }

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * The reaper thread.

-     */

-    private final class Reaper extends Thread {

-        /** Construct a new Reaper */

-        Reaper() {

-            super("File Reaper");

-            setPriority(Thread.MAX_PRIORITY);

-            setDaemon(true);

-        }

-

-        /**

-         * Run the reaper thread that will delete files as their associated

-         * marker objects are reclaimed by the garbage collector.

-         */

-        @Override

-        public void run() {

-            // thread exits when exitWhenFinished is true and there are no more tracked objects

-            while (exitWhenFinished == false || trackers.size() > 0) {

-                Tracker tracker = null;

-                try {

-                    // Wait for a tracker to remove.

-                    tracker = (Tracker) q.remove();

-                } catch (Exception e) {

-                    continue;

-                }

-                if (tracker != null) {

-                    tracker.delete();

-                    tracker.clear();

-                    trackers.remove(tracker);

-                }

-            }

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Inner class which acts as the reference for a file pending deletion.

-     */

-    private static final class Tracker extends PhantomReference<Object> {

-

-        /**

-         * The full path to the file being tracked.

-         */

-        private final String path;

-        /**

-         * The strategy for deleting files.

-         */

-        private final FileDeleteStrategy deleteStrategy;

-

-        /**

-         * Constructs an instance of this class from the supplied parameters.

-         *

-         * @param path  the full path to the file to be tracked, not null

-         * @param deleteStrategy  the strategy to delete the file, null means normal

-         * @param marker  the marker object used to track the file, not null

-         * @param queue  the queue on to which the tracker will be pushed, not null

-         */

-        Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker, ReferenceQueue<Object> queue) {

-            super(marker, queue);

-            this.path = path;

-            this.deleteStrategy = (deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy);

-        }

-

-        /**

-         * Deletes the file associated with this tracker instance.

-         *

-         * @return <code>true</code> if the file was deleted successfully;

-         *         <code>false</code> otherwise.

-         */

-        public boolean delete() {

-            return deleteStrategy.deleteQuietly(new File(path));

-        }

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.File;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Collection;
+import java.util.Vector;
+
+/**
+ * Keeps track of files awaiting deletion, and deletes them when an associated
+ * marker object is reclaimed by the garbage collector.
+ * <p>
+ * This utility creates a background thread to handle file deletion.
+ * Each file to be deleted is registered with a handler object.
+ * When the handler object is garbage collected, the file is deleted.
+ * <p>
+ * In an environment with multiple class loaders (a servlet container, for
+ * example), you should consider stopping the background thread if it is no
+ * longer needed. This is done by invoking the method
+ * {@link #exitWhenFinished}, typically in
+ * {@link javax.servlet.ServletContextListener#contextDestroyed} or similar.
+ *
+ * @author Noel Bergman
+ * @author Martin Cooper
+ * @version $Id: FileCleaner.java 490987 2006-12-29 12:11:48Z scolebourne $
+ */
+public class FileCleaningTracker {
+    /**
+     * Queue of <code>Tracker</code> instances being watched.
+     */
+    ReferenceQueue<Object> /* Tracker */ q = new ReferenceQueue<Object>();
+    /**
+     * Collection of <code>Tracker</code> instances in existence.
+     */
+    final Collection<Tracker> /* Tracker */ trackers = new Vector<Tracker>();  // synchronized
+    /**
+     * Whether to terminate the thread when the tracking is complete.
+     */
+    volatile boolean exitWhenFinished = false;
+    /**
+     * The thread that will clean up registered files.
+     */
+    Thread reaper;
+
+    //-----------------------------------------------------------------------
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the file is null
+     */
+    public void track(File file, Object marker) {
+        track(file, marker, (FileDeleteStrategy) null);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The speified deletion strategy is used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the file is null
+     */
+    public void track(File file, Object marker, FileDeleteStrategy deleteStrategy) {
+        if (file == null) {
+            throw new NullPointerException("The file must not be null");
+        }
+        addTracker(file.getPath(), marker, deleteStrategy);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the path is null
+     */
+    public void track(String path, Object marker) {
+        track(path, marker, (FileDeleteStrategy) null);
+    }
+
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The speified deletion strategy is used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the path is null
+     */
+    public void track(String path, Object marker, FileDeleteStrategy deleteStrategy) {
+        if (path == null) {
+            throw new NullPointerException("The path must not be null");
+        }
+        addTracker(path, marker, deleteStrategy);
+    }
+
+    /**
+     * Adds a tracker to the list of trackers.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     */
+    private synchronized void addTracker(String path, Object marker, FileDeleteStrategy deleteStrategy) {
+        // synchronized block protects reaper
+        if (exitWhenFinished) {
+            throw new IllegalStateException("No new trackers can be added once exitWhenFinished() is called");
+        }
+        if (reaper == null) {
+            reaper = new Reaper();
+            reaper.start();
+        }
+        trackers.add(new Tracker(path, deleteStrategy, marker, q));
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Retrieve the number of files currently being tracked, and therefore
+     * awaiting deletion.
+     *
+     * @return the number of files being tracked
+     */
+    public int getTrackCount() {
+        return trackers.size();
+    }
+
+    /**
+     * Call this method to cause the file cleaner thread to terminate when
+     * there are no more objects being tracked for deletion.
+     * <p>
+     * In a simple environment, you don't need this method as the file cleaner
+     * thread will simply exit when the JVM exits. In a more complex environment,
+     * with multiple class loaders (such as an application server), you should be
+     * aware that the file cleaner thread will continue running even if the class
+     * loader it was started from terminates. This can consitute a memory leak.
+     * <p>
+     * For example, suppose that you have developed a web application, which
+     * contains the commons-io jar file in your WEB-INF/lib directory. In other
+     * words, the FileCleaner class is loaded through the class loader of your
+     * web application. If the web application is terminated, but the servlet
+     * container is still running, then the file cleaner thread will still exist,
+     * posing a memory leak.
+     * <p>
+     * This method allows the thread to be terminated. Simply call this method
+     * in the resource cleanup code, such as {@link javax.servlet.ServletContextListener#contextDestroyed}.
+     * One called, no new objects can be tracked by the file cleaner.
+     */
+    public synchronized void exitWhenFinished() {
+        // synchronized block protects reaper
+        exitWhenFinished = true;
+        if (reaper != null) {
+            synchronized (reaper) {
+                reaper.interrupt();
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * The reaper thread.
+     */
+    private final class Reaper extends Thread {
+        /** Construct a new Reaper */
+        Reaper() {
+            super("File Reaper");
+            setPriority(Thread.MAX_PRIORITY);
+            setDaemon(true);
+        }
+
+        /**
+         * Run the reaper thread that will delete files as their associated
+         * marker objects are reclaimed by the garbage collector.
+         */
+        @Override
+        public void run() {
+            // thread exits when exitWhenFinished is true and there are no more tracked objects
+            while (exitWhenFinished == false || trackers.size() > 0) {
+                Tracker tracker = null;
+                try {
+                    // Wait for a tracker to remove.
+                    tracker = (Tracker) q.remove();
+                } catch (Exception e) {
+                    continue;
+                }
+                if (tracker != null) {
+                    tracker.delete();
+                    tracker.clear();
+                    trackers.remove(tracker);
+                }
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Inner class which acts as the reference for a file pending deletion.
+     */
+    private static final class Tracker extends PhantomReference<Object> {
+
+        /**
+         * The full path to the file being tracked.
+         */
+        private final String path;
+        /**
+         * The strategy for deleting files.
+         */
+        private final FileDeleteStrategy deleteStrategy;
+
+        /**
+         * Constructs an instance of this class from the supplied parameters.
+         *
+         * @param path  the full path to the file to be tracked, not null
+         * @param deleteStrategy  the strategy to delete the file, null means normal
+         * @param marker  the marker object used to track the file, not null
+         * @param queue  the queue on to which the tracker will be pushed, not null
+         */
+        Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker, ReferenceQueue<Object> queue) {
+            super(marker, queue);
+            this.path = path;
+            this.deleteStrategy = (deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy);
+        }
+
+        /**
+         * Deletes the file associated with this tracker instance.
+         *
+         * @return <code>true</code> if the file was deleted successfully;
+         *         <code>false</code> otherwise.
+         */
+        public boolean delete() {
+            return deleteStrategy.deleteQuietly(new File(path));
+        }
+    }
+
+}
diff --git a/src/org/apache/commons/io/FileDeleteStrategy.java b/src/org/apache/commons/io/FileDeleteStrategy.java
index 8b6b4b9..3518df1 100644
--- a/src/org/apache/commons/io/FileDeleteStrategy.java
+++ b/src/org/apache/commons/io/FileDeleteStrategy.java
@@ -1,156 +1,156 @@
-/*

- * 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.commons.io;

-

-import java.io.File;

-import java.io.IOException;

-

-/**

- * Strategy for deleting files.

- * <p>

- * There is more than one way to delete a file.

- * You may want to limit access to certain directories, to only delete

- * directories if they are empty, or maybe to force deletion.

- * <p>

- * This class captures the strategy to use and is designed for user subclassing.

- *

- * @author Stephen Colebourne

- * @version $Id: FileDeleteStrategy.java 453903 2006-10-07 13:47:06Z scolebourne $

- * @since Commons IO 1.3

- */

-public class FileDeleteStrategy {

-

-    /**

-     * The singleton instance for normal file deletion, which does not permit

-     * the deletion of directories that are not empty.

-     */

-    public static final FileDeleteStrategy NORMAL = new FileDeleteStrategy("Normal");

-    /**

-     * The singleton instance for forced file deletion, which always deletes,

-     * even if the file represents a non-empty directory.

-     */

-    public static final FileDeleteStrategy FORCE = new ForceFileDeleteStrategy();

-

-    /** The name of the strategy. */

-    private final String name;

-

-    //-----------------------------------------------------------------------

-    /**

-     * Restricted constructor.

-     *

-     * @param name  the name by which the strategy is known

-     */

-    protected FileDeleteStrategy(String name) {

-        this.name = name;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Deletes the file object, which may be a file or a directory.

-     * All <code>IOException</code>s are caught and false returned instead.

-     * If the file does not exist or is null, true is returned.

-     * <p>

-     * Subclass writers should override {@link #doDelete(File)}, not this method.

-     *

-     * @param fileToDelete  the file to delete, null returns true

-     * @return true if the file was deleted, or there was no such file

-     */

-    public boolean deleteQuietly(File fileToDelete) {

-        if (fileToDelete == null || fileToDelete.exists() == false) {

-            return true;

-        }

-        try {

-            return doDelete(fileToDelete);

-        } catch (IOException ex) {

-            return false;

-        }

-    }

-

-    /**

-     * Deletes the file object, which may be a file or a directory.

-     * If the file does not exist, the method just returns.

-     * <p>

-     * Subclass writers should override {@link #doDelete(File)}, not this method.

-     *

-     * @param fileToDelete  the file to delete, not null

-     * @throws NullPointerException if the file is null

-     * @throws IOException if an error occurs during file deletion

-     */

-    public void delete(File fileToDelete) throws IOException {

-        if (fileToDelete.exists() && doDelete(fileToDelete) == false) {

-            throw new IOException("Deletion failed: " + fileToDelete);

-        }

-    }

-

-    /**

-     * Actually deletes the file object, which may be a file or a directory.

-     * <p>

-     * This method is designed for subclasses to override.

-     * The implementation may return either false or an <code>IOException</code>

-     * when deletion fails. The {@link #delete(File)} and {@link #deleteQuietly(File)}

-     * methods will handle either response appropriately.

-     * A check has been made to ensure that the file will exist.

-     * <p>

-     * This implementation uses {@link File#delete()}.

-     *

-     * @param fileToDelete  the file to delete, exists, not null

-     * @return true if the file was deleteds

-     * @throws NullPointerException if the file is null

-     * @throws IOException if an error occurs during file deletion

-     */

-    protected boolean doDelete(File fileToDelete) throws IOException {

-        return fileToDelete.delete();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Gets a string describing the delete strategy.

-     *

-     * @return a string describing the delete strategy

-     */

-    public String toString() {

-        return "FileDeleteStrategy[" + name + "]";

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Force file deletion strategy.

-     */

-    static class ForceFileDeleteStrategy extends FileDeleteStrategy {

-        /** Default Constructor */

-        ForceFileDeleteStrategy() {

-            super("Force");

-        }

-

-        /**

-         * Deletes the file object.

-         * <p>

-         * This implementation uses <code>FileUtils.forceDelete() <code>

-         * if the file exists.

-         *

-         * @param fileToDelete  the file to delete, not null

-         * @return Always returns <code>true</code>

-         * @throws NullPointerException if the file is null

-         * @throws IOException if an error occurs during file deletion

-         */

-        protected boolean doDelete(File fileToDelete) throws IOException {

-            FileUtils.forceDelete(fileToDelete);

-            return true;

-        }

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Strategy for deleting files.
+ * <p>
+ * There is more than one way to delete a file.
+ * You may want to limit access to certain directories, to only delete
+ * directories if they are empty, or maybe to force deletion.
+ * <p>
+ * This class captures the strategy to use and is designed for user subclassing.
+ *
+ * @author Stephen Colebourne
+ * @version $Id: FileDeleteStrategy.java 453903 2006-10-07 13:47:06Z scolebourne $
+ * @since Commons IO 1.3
+ */
+public class FileDeleteStrategy {
+
+    /**
+     * The singleton instance for normal file deletion, which does not permit
+     * the deletion of directories that are not empty.
+     */
+    public static final FileDeleteStrategy NORMAL = new FileDeleteStrategy("Normal");
+    /**
+     * The singleton instance for forced file deletion, which always deletes,
+     * even if the file represents a non-empty directory.
+     */
+    public static final FileDeleteStrategy FORCE = new ForceFileDeleteStrategy();
+
+    /** The name of the strategy. */
+    private final String name;
+
+    //-----------------------------------------------------------------------
+    /**
+     * Restricted constructor.
+     *
+     * @param name  the name by which the strategy is known
+     */
+    protected FileDeleteStrategy(String name) {
+        this.name = name;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Deletes the file object, which may be a file or a directory.
+     * All <code>IOException</code>s are caught and false returned instead.
+     * If the file does not exist or is null, true is returned.
+     * <p>
+     * Subclass writers should override {@link #doDelete(File)}, not this method.
+     *
+     * @param fileToDelete  the file to delete, null returns true
+     * @return true if the file was deleted, or there was no such file
+     */
+    public boolean deleteQuietly(File fileToDelete) {
+        if (fileToDelete == null || fileToDelete.exists() == false) {
+            return true;
+        }
+        try {
+            return doDelete(fileToDelete);
+        } catch (IOException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Deletes the file object, which may be a file or a directory.
+     * If the file does not exist, the method just returns.
+     * <p>
+     * Subclass writers should override {@link #doDelete(File)}, not this method.
+     *
+     * @param fileToDelete  the file to delete, not null
+     * @throws NullPointerException if the file is null
+     * @throws IOException if an error occurs during file deletion
+     */
+    public void delete(File fileToDelete) throws IOException {
+        if (fileToDelete.exists() && doDelete(fileToDelete) == false) {
+            throw new IOException("Deletion failed: " + fileToDelete);
+        }
+    }
+
+    /**
+     * Actually deletes the file object, which may be a file or a directory.
+     * <p>
+     * This method is designed for subclasses to override.
+     * The implementation may return either false or an <code>IOException</code>
+     * when deletion fails. The {@link #delete(File)} and {@link #deleteQuietly(File)}
+     * methods will handle either response appropriately.
+     * A check has been made to ensure that the file will exist.
+     * <p>
+     * This implementation uses {@link File#delete()}.
+     *
+     * @param fileToDelete  the file to delete, exists, not null
+     * @return true if the file was deleteds
+     * @throws NullPointerException if the file is null
+     * @throws IOException if an error occurs during file deletion
+     */
+    protected boolean doDelete(File fileToDelete) throws IOException {
+        return fileToDelete.delete();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets a string describing the delete strategy.
+     *
+     * @return a string describing the delete strategy
+     */
+    public String toString() {
+        return "FileDeleteStrategy[" + name + "]";
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Force file deletion strategy.
+     */
+    static class ForceFileDeleteStrategy extends FileDeleteStrategy {
+        /** Default Constructor */
+        ForceFileDeleteStrategy() {
+            super("Force");
+        }
+
+        /**
+         * Deletes the file object.
+         * <p>
+         * This implementation uses <code>FileUtils.forceDelete() <code>
+         * if the file exists.
+         *
+         * @param fileToDelete  the file to delete, not null
+         * @return Always returns <code>true</code>
+         * @throws NullPointerException if the file is null
+         * @throws IOException if an error occurs during file deletion
+         */
+        protected boolean doDelete(File fileToDelete) throws IOException {
+            FileUtils.forceDelete(fileToDelete);
+            return true;
+        }
+    }
+
+}
diff --git a/src/org/apache/commons/io/FileSystemUtils.java b/src/org/apache/commons/io/FileSystemUtils.java
index a01c6e5..ee4707c 100644
--- a/src/org/apache/commons/io/FileSystemUtils.java
+++ b/src/org/apache/commons/io/FileSystemUtils.java
@@ -1,458 +1,458 @@
-/*

- * 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.commons.io;

-

-import java.io.BufferedReader;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.InputStreamReader;

-import java.io.OutputStream;

-import java.util.ArrayList;

-import java.util.Arrays;

-import java.util.List;

-import java.util.StringTokenizer;

-

-/**

- * General File System utilities.

- * <p>

- * This class provides static utility methods for general file system

- * functions not provided via the JDK {@link java.io.File File} class.

- * <p>

- * The current functions provided are:

- * <ul>

- * <li>Get the free space on a drive

- * </ul>

- *

- * @author Frank W. Zammetti

- * @author Stephen Colebourne

- * @author Thomas Ledoux

- * @author James Urie

- * @author Magnus Grimsell

- * @author Thomas Ledoux

- * @version $Id: FileSystemUtils.java 453889 2006-10-07 11:56:25Z scolebourne $

- * @since Commons IO 1.1

- */

-public class FileSystemUtils {

-

-    /** Singleton instance, used mainly for testing. */

-    private static final FileSystemUtils INSTANCE = new FileSystemUtils();

-

-    /** Operating system state flag for error. */

-    private static final int INIT_PROBLEM = -1;

-    /** Operating system state flag for neither Unix nor Windows. */

-    private static final int OTHER = 0;

-    /** Operating system state flag for Windows. */

-    private static final int WINDOWS = 1;

-    /** Operating system state flag for Unix. */

-    private static final int UNIX = 2;

-    /** Operating system state flag for Posix flavour Unix. */

-    private static final int POSIX_UNIX = 3;

-

-    /** The operating system flag. */

-    private static final int OS;

-    static {

-        int os = OTHER;

-        try {

-            String osName = System.getProperty("os.name");

-            if (osName == null) {

-                throw new IOException("os.name not found");

-            }

-            osName = osName.toLowerCase();

-            // match

-            if (osName.indexOf("windows") != -1) {

-                os = WINDOWS;

-            } else if (osName.indexOf("linux") != -1 ||

-                osName.indexOf("sun os") != -1 ||

-                osName.indexOf("sunos") != -1 ||

-                osName.indexOf("solaris") != -1 ||

-                osName.indexOf("mpe/ix") != -1 ||

-                osName.indexOf("freebsd") != -1 ||

-                osName.indexOf("irix") != -1 ||

-                osName.indexOf("digital unix") != -1 ||

-                osName.indexOf("unix") != -1 ||

-                osName.indexOf("mac os x") != -1) {

-                os = UNIX;

-            } else if (osName.indexOf("hp-ux") != -1 ||

-                osName.indexOf("aix") != -1) {

-                os = POSIX_UNIX;

-            } else {

-                os = OTHER;

-            }

-

-        } catch (Exception ex) {

-            os = INIT_PROBLEM;

-        }

-        OS = os;

-    }

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public FileSystemUtils() {

-        super();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns the free space on a drive or volume by invoking

-     * the command line.

-     * This method does not normalize the result, and typically returns

-     * bytes on Windows, 512 byte units on OS X and kilobytes on Unix.

-     * As this is not very useful, this method is deprecated in favour

-     * of {@link #freeSpaceKb(String)} which returns a result in kilobytes.

-     * <p>

-     * Note that some OS's are NOT currently supported, including OS/390,

-     * OpenVMS and and SunOS 5. (SunOS is supported by <code>freeSpaceKb</code>.)

-     * <pre>

-     * FileSystemUtils.freeSpace("C:");       // Windows

-     * FileSystemUtils.freeSpace("/volume");  // *nix

-     * </pre>

-     * The free space is calculated via the command line.

-     * It uses 'dir /-c' on Windows and 'df' on *nix.

-     *

-     * @param path  the path to get free space for, not null, not empty on Unix

-     * @return the amount of free drive space on the drive or volume

-     * @throws IllegalArgumentException if the path is invalid

-     * @throws IllegalStateException if an error occurred in initialisation

-     * @throws IOException if an error occurs when finding the free space

-     * @since Commons IO 1.1, enhanced OS support in 1.2 and 1.3

-     * @deprecated Use freeSpaceKb(String)

-     *  Deprecated from 1.3, may be removed in 2.0

-     */

-    @Deprecated

-    public static long freeSpace(String path) throws IOException {

-        return INSTANCE.freeSpaceOS(path, OS, false);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns the free space on a drive or volume in kilobytes by invoking

-     * the command line.

-     * <pre>

-     * FileSystemUtils.freeSpaceKb("C:");       // Windows

-     * FileSystemUtils.freeSpaceKb("/volume");  // *nix

-     * </pre>

-     * The free space is calculated via the command line.

-     * It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.

-     * <p>

-     * In order to work, you must be running Windows, or have a implementation of

-     * Unix df that supports GNU format when passed -k (or -kP). If you are going

-     * to rely on this code, please check that it works on your OS by running

-     * some simple tests to compare the command line with the output from this class.

-     * If your operating system isn't supported, please raise a JIRA call detailing

-     * the exact result from df -k and as much other detail as possible, thanks.

-     *

-     * @param path  the path to get free space for, not null, not empty on Unix

-     * @return the amount of free drive space on the drive or volume in kilobytes

-     * @throws IllegalArgumentException if the path is invalid

-     * @throws IllegalStateException if an error occurred in initialisation

-     * @throws IOException if an error occurs when finding the free space

-     * @since Commons IO 1.2, enhanced OS support in 1.3

-     */

-    public static long freeSpaceKb(String path) throws IOException {

-        return INSTANCE.freeSpaceOS(path, OS, true);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns the free space on a drive or volume in a cross-platform manner.

-     * Note that some OS's are NOT currently supported, including OS/390.

-     * <pre>

-     * FileSystemUtils.freeSpace("C:");  // Windows

-     * FileSystemUtils.freeSpace("/volume");  // *nix

-     * </pre>

-     * The free space is calculated via the command line.

-     * It uses 'dir /-c' on Windows and 'df' on *nix.

-     *

-     * @param path  the path to get free space for, not null, not empty on Unix

-     * @param os  the operating system code

-     * @param kb  whether to normalize to kilobytes

-     * @return the amount of free drive space on the drive or volume

-     * @throws IllegalArgumentException if the path is invalid

-     * @throws IllegalStateException if an error occurred in initialisation

-     * @throws IOException if an error occurs when finding the free space

-     */

-    long freeSpaceOS(String path, int os, boolean kb) throws IOException {

-        if (path == null) {

-            throw new IllegalArgumentException("Path must not be empty");

-        }

-        switch (os) {

-            case WINDOWS:

-                return (kb ? freeSpaceWindows(path) / 1024 : freeSpaceWindows(path));

-            case UNIX:

-                return freeSpaceUnix(path, kb, false);

-            case POSIX_UNIX:

-                return freeSpaceUnix(path, kb, true);

-            case OTHER:

-                throw new IllegalStateException("Unsupported operating system");

-            default:

-                throw new IllegalStateException(

-                  "Exception caught when determining operating system");

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Find free space on the Windows platform using the 'dir' command.

-     *

-     * @param path  the path to get free space for, including the colon

-     * @return the amount of free drive space on the drive

-     * @throws IOException if an error occurs

-     */

-    long freeSpaceWindows(String path) throws IOException {

-        path = FilenameUtils.normalize(path);

-        if (path.length() > 2 && path.charAt(1) == ':') {

-            path = path.substring(0, 2);  // seems to make it work

-        }

-

-        // build and run the 'dir' command

-        String[] cmdAttribs = new String[] {"cmd.exe", "/C", "dir /-c " + path};

-

-        // read in the output of the command to an ArrayList

-        List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE);

-

-        // now iterate over the lines we just read and find the LAST

-        // non-empty line (the free space bytes should be in the last element

-        // of the ArrayList anyway, but this will ensure it works even if it's

-        // not, still assuming it is on the last non-blank line)

-        for (int i = lines.size() - 1; i >= 0; i--) {

-            String line = lines.get(i);

-            if (line.length() > 0) {

-                return parseDir(line, path);

-            }

-        }

-        // all lines are blank

-        throw new IOException(

-                "Command line 'dir /-c' did not return any info " +

-                "for path '" + path + "'");

-    }

-

-    /**

-     * Parses the Windows dir response last line

-     *

-     * @param line  the line to parse

-     * @param path  the path that was sent

-     * @return the number of bytes

-     * @throws IOException if an error occurs

-     */

-    long parseDir(String line, String path) throws IOException {

-        // read from the end of the line to find the last numeric

-        // character on the line, then continue until we find the first

-        // non-numeric character, and everything between that and the last

-        // numeric character inclusive is our free space bytes count

-        int bytesStart = 0;

-        int bytesEnd = 0;

-        int j = line.length() - 1;

-        innerLoop1: while (j >= 0) {

-            char c = line.charAt(j);

-            if (Character.isDigit(c)) {

-              // found the last numeric character, this is the end of

-              // the free space bytes count

-              bytesEnd = j + 1;

-              break innerLoop1;

-            }

-            j--;

-        }

-        innerLoop2: while (j >= 0) {

-            char c = line.charAt(j);

-            if (!Character.isDigit(c) && c != ',' && c != '.') {

-              // found the next non-numeric character, this is the

-              // beginning of the free space bytes count

-              bytesStart = j + 1;

-              break innerLoop2;

-            }

-            j--;

-        }

-        if (j < 0) {

-            throw new IOException(

-                    "Command line 'dir /-c' did not return valid info " +

-                    "for path '" + path + "'");

-        }

-

-        // remove commas and dots in the bytes count

-        StringBuffer buf = new StringBuffer(line.substring(bytesStart, bytesEnd));

-        for (int k = 0; k < buf.length(); k++) {

-            if (buf.charAt(k) == ',' || buf.charAt(k) == '.') {

-                buf.deleteCharAt(k--);

-            }

-        }

-        return parseBytes(buf.toString(), path);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Find free space on the *nix platform using the 'df' command.

-     *

-     * @param path  the path to get free space for

-     * @param kb  whether to normalize to kilobytes

-     * @param posix  whether to use the posix standard format flag

-     * @return the amount of free drive space on the volume

-     * @throws IOException if an error occurs

-     */

-    long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {

-        if (path.length() == 0) {

-            throw new IllegalArgumentException("Path must not be empty");

-        }

-        path = FilenameUtils.normalize(path);

-

-        // build and run the 'dir' command

-        String flags = "-";

-        if (kb) {

-            flags += "k";

-        }

-        if (posix) {

-            flags += "P";

-        }

-        String[] cmdAttribs =

-            (flags.length() > 1 ? new String[] {"df", flags, path} : new String[] {"df", path});

-

-        // perform the command, asking for up to 3 lines (header, interesting, overflow)

-        List<String> lines = performCommand(cmdAttribs, 3);

-        if (lines.size() < 2) {

-            // unknown problem, throw exception

-            throw new IOException(

-                    "Command line 'df' did not return info as expected " +

-                    "for path '" + path + "'- response was " + lines);

-        }

-        String line2 = lines.get(1); // the line we're interested in

-

-        // Now, we tokenize the string. The fourth element is what we want.

-        StringTokenizer tok = new StringTokenizer(line2, " ");

-        if (tok.countTokens() < 4) {

-            // could be long Filesystem, thus data on third line

-            if (tok.countTokens() == 1 && lines.size() >= 3) {

-                String line3 = lines.get(2); // the line may be interested in

-                tok = new StringTokenizer(line3, " ");

-            } else {

-                throw new IOException(

-                        "Command line 'df' did not return data as expected " +

-                        "for path '" + path + "'- check path is valid");

-            }

-        } else {

-            tok.nextToken(); // Ignore Filesystem

-        }

-        tok.nextToken(); // Ignore 1K-blocks

-        tok.nextToken(); // Ignore Used

-        String freeSpace = tok.nextToken();

-        return parseBytes(freeSpace, path);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Parses the bytes from a string.

-     *

-     * @param freeSpace  the free space string

-     * @param path  the path

-     * @return the number of bytes

-     * @throws IOException if an error occurs

-     */

-    long parseBytes(String freeSpace, String path) throws IOException {

-        try {

-            long bytes = Long.parseLong(freeSpace);

-            if (bytes < 0) {

-                throw new IOException(

-                        "Command line 'df' did not find free space in response " +

-                        "for path '" + path + "'- check path is valid");

-            }

-            return bytes;

-

-        } catch (NumberFormatException ex) {

-            throw new IOException(

-                    "Command line 'df' did not return numeric data as expected " +

-                    "for path '" + path + "'- check path is valid");

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Performs the os command.

-     *

-     * @param cmdAttribs  the command line parameters

-     * @param max The maximum limit for the lines returned

-     * @return the parsed data

-     * @throws IOException if an error occurs

-     */

-    List<String> performCommand(String[] cmdAttribs, int max) throws IOException {

-        // this method does what it can to avoid the 'Too many open files' error

-        // based on trial and error and these links:

-        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692

-        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4801027

-        // http://forum.java.sun.com/thread.jspa?threadID=533029&messageID=2572018

-        // however, its still not perfect as the JDK support is so poor

-        // (see commond-exec or ant for a better multi-threaded multi-os solution)

-

-        List<String> lines = new ArrayList<String>(20);

-        Process proc = null;

-        InputStream in = null;

-        OutputStream out = null;

-        InputStream err = null;

-        BufferedReader inr = null;

-        try {

-            proc = openProcess(cmdAttribs);

-            in = proc.getInputStream();

-            out = proc.getOutputStream();

-            err = proc.getErrorStream();

-            inr = new BufferedReader(new InputStreamReader(in));

-            String line = inr.readLine();

-            while (line != null && lines.size() < max) {

-                line = line.toLowerCase().trim();

-                lines.add(line);

-                line = inr.readLine();

-            }

-

-            proc.waitFor();

-            if (proc.exitValue() != 0) {

-                // os command problem, throw exception

-                throw new IOException(

-                        "Command line returned OS error code '" + proc.exitValue() +

-                        "' for command " + Arrays.asList(cmdAttribs));

-            }

-            if (lines.size() == 0) {

-                // unknown problem, throw exception

-                throw new IOException(

-                        "Command line did not return any info " +

-                        "for command " + Arrays.asList(cmdAttribs));

-            }

-            return lines;

-

-        } catch (InterruptedException ex) {

-            throw new IOException(

-                    "Command line threw an InterruptedException '" + ex.getMessage() +

-                    "' for command " + Arrays.asList(cmdAttribs));

-        } finally {

-            IOUtils.closeQuietly(in);

-            IOUtils.closeQuietly(out);

-            IOUtils.closeQuietly(err);

-            IOUtils.closeQuietly(inr);

-            if (proc != null) {

-                proc.destroy();

-            }

-        }

-    }

-

-    /**

-     * Opens the process to the operating system.

-     *

-     * @param cmdAttribs  the command line parameters

-     * @return the process

-     * @throws IOException if an error occurs

-     */

-    Process openProcess(String[] cmdAttribs) throws IOException {

-        return Runtime.getRuntime().exec(cmdAttribs);

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * General File System utilities.
+ * <p>
+ * This class provides static utility methods for general file system
+ * functions not provided via the JDK {@link java.io.File File} class.
+ * <p>
+ * The current functions provided are:
+ * <ul>
+ * <li>Get the free space on a drive
+ * </ul>
+ *
+ * @author Frank W. Zammetti
+ * @author Stephen Colebourne
+ * @author Thomas Ledoux
+ * @author James Urie
+ * @author Magnus Grimsell
+ * @author Thomas Ledoux
+ * @version $Id: FileSystemUtils.java 453889 2006-10-07 11:56:25Z scolebourne $
+ * @since Commons IO 1.1
+ */
+public class FileSystemUtils {
+
+    /** Singleton instance, used mainly for testing. */
+    private static final FileSystemUtils INSTANCE = new FileSystemUtils();
+
+    /** Operating system state flag for error. */
+    private static final int INIT_PROBLEM = -1;
+    /** Operating system state flag for neither Unix nor Windows. */
+    private static final int OTHER = 0;
+    /** Operating system state flag for Windows. */
+    private static final int WINDOWS = 1;
+    /** Operating system state flag for Unix. */
+    private static final int UNIX = 2;
+    /** Operating system state flag for Posix flavour Unix. */
+    private static final int POSIX_UNIX = 3;
+
+    /** The operating system flag. */
+    private static final int OS;
+    static {
+        int os = OTHER;
+        try {
+            String osName = System.getProperty("os.name");
+            if (osName == null) {
+                throw new IOException("os.name not found");
+            }
+            osName = osName.toLowerCase();
+            // match
+            if (osName.indexOf("windows") != -1) {
+                os = WINDOWS;
+            } else if (osName.indexOf("linux") != -1 ||
+                osName.indexOf("sun os") != -1 ||
+                osName.indexOf("sunos") != -1 ||
+                osName.indexOf("solaris") != -1 ||
+                osName.indexOf("mpe/ix") != -1 ||
+                osName.indexOf("freebsd") != -1 ||
+                osName.indexOf("irix") != -1 ||
+                osName.indexOf("digital unix") != -1 ||
+                osName.indexOf("unix") != -1 ||
+                osName.indexOf("mac os x") != -1) {
+                os = UNIX;
+            } else if (osName.indexOf("hp-ux") != -1 ||
+                osName.indexOf("aix") != -1) {
+                os = POSIX_UNIX;
+            } else {
+                os = OTHER;
+            }
+
+        } catch (Exception ex) {
+            os = INIT_PROBLEM;
+        }
+        OS = os;
+    }
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public FileSystemUtils() {
+        super();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the free space on a drive or volume by invoking
+     * the command line.
+     * This method does not normalize the result, and typically returns
+     * bytes on Windows, 512 byte units on OS X and kilobytes on Unix.
+     * As this is not very useful, this method is deprecated in favour
+     * of {@link #freeSpaceKb(String)} which returns a result in kilobytes.
+     * <p>
+     * Note that some OS's are NOT currently supported, including OS/390,
+     * OpenVMS and and SunOS 5. (SunOS is supported by <code>freeSpaceKb</code>.)
+     * <pre>
+     * FileSystemUtils.freeSpace("C:");       // Windows
+     * FileSystemUtils.freeSpace("/volume");  // *nix
+     * </pre>
+     * The free space is calculated via the command line.
+     * It uses 'dir /-c' on Windows and 'df' on *nix.
+     *
+     * @param path  the path to get free space for, not null, not empty on Unix
+     * @return the amount of free drive space on the drive or volume
+     * @throws IllegalArgumentException if the path is invalid
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since Commons IO 1.1, enhanced OS support in 1.2 and 1.3
+     * @deprecated Use freeSpaceKb(String)
+     *  Deprecated from 1.3, may be removed in 2.0
+     */
+    @Deprecated
+    public static long freeSpace(String path) throws IOException {
+        return INSTANCE.freeSpaceOS(path, OS, false);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the free space on a drive or volume in kilobytes by invoking
+     * the command line.
+     * <pre>
+     * FileSystemUtils.freeSpaceKb("C:");       // Windows
+     * FileSystemUtils.freeSpaceKb("/volume");  // *nix
+     * </pre>
+     * The free space is calculated via the command line.
+     * It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
+     * <p>
+     * In order to work, you must be running Windows, or have a implementation of
+     * Unix df that supports GNU format when passed -k (or -kP). If you are going
+     * to rely on this code, please check that it works on your OS by running
+     * some simple tests to compare the command line with the output from this class.
+     * If your operating system isn't supported, please raise a JIRA call detailing
+     * the exact result from df -k and as much other detail as possible, thanks.
+     *
+     * @param path  the path to get free space for, not null, not empty on Unix
+     * @return the amount of free drive space on the drive or volume in kilobytes
+     * @throws IllegalArgumentException if the path is invalid
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since Commons IO 1.2, enhanced OS support in 1.3
+     */
+    public static long freeSpaceKb(String path) throws IOException {
+        return INSTANCE.freeSpaceOS(path, OS, true);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the free space on a drive or volume in a cross-platform manner.
+     * Note that some OS's are NOT currently supported, including OS/390.
+     * <pre>
+     * FileSystemUtils.freeSpace("C:");  // Windows
+     * FileSystemUtils.freeSpace("/volume");  // *nix
+     * </pre>
+     * The free space is calculated via the command line.
+     * It uses 'dir /-c' on Windows and 'df' on *nix.
+     *
+     * @param path  the path to get free space for, not null, not empty on Unix
+     * @param os  the operating system code
+     * @param kb  whether to normalize to kilobytes
+     * @return the amount of free drive space on the drive or volume
+     * @throws IllegalArgumentException if the path is invalid
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     */
+    long freeSpaceOS(String path, int os, boolean kb) throws IOException {
+        if (path == null) {
+            throw new IllegalArgumentException("Path must not be empty");
+        }
+        switch (os) {
+            case WINDOWS:
+                return (kb ? freeSpaceWindows(path) / 1024 : freeSpaceWindows(path));
+            case UNIX:
+                return freeSpaceUnix(path, kb, false);
+            case POSIX_UNIX:
+                return freeSpaceUnix(path, kb, true);
+            case OTHER:
+                throw new IllegalStateException("Unsupported operating system");
+            default:
+                throw new IllegalStateException(
+                  "Exception caught when determining operating system");
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Find free space on the Windows platform using the 'dir' command.
+     *
+     * @param path  the path to get free space for, including the colon
+     * @return the amount of free drive space on the drive
+     * @throws IOException if an error occurs
+     */
+    long freeSpaceWindows(String path) throws IOException {
+        path = FilenameUtils.normalize(path);
+        if (path.length() > 2 && path.charAt(1) == ':') {
+            path = path.substring(0, 2);  // seems to make it work
+        }
+
+        // build and run the 'dir' command
+        String[] cmdAttribs = new String[] {"cmd.exe", "/C", "dir /-c " + path};
+
+        // read in the output of the command to an ArrayList
+        List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE);
+
+        // now iterate over the lines we just read and find the LAST
+        // non-empty line (the free space bytes should be in the last element
+        // of the ArrayList anyway, but this will ensure it works even if it's
+        // not, still assuming it is on the last non-blank line)
+        for (int i = lines.size() - 1; i >= 0; i--) {
+            String line = lines.get(i);
+            if (line.length() > 0) {
+                return parseDir(line, path);
+            }
+        }
+        // all lines are blank
+        throw new IOException(
+                "Command line 'dir /-c' did not return any info " +
+                "for path '" + path + "'");
+    }
+
+    /**
+     * Parses the Windows dir response last line
+     *
+     * @param line  the line to parse
+     * @param path  the path that was sent
+     * @return the number of bytes
+     * @throws IOException if an error occurs
+     */
+    long parseDir(String line, String path) throws IOException {
+        // read from the end of the line to find the last numeric
+        // character on the line, then continue until we find the first
+        // non-numeric character, and everything between that and the last
+        // numeric character inclusive is our free space bytes count
+        int bytesStart = 0;
+        int bytesEnd = 0;
+        int j = line.length() - 1;
+        innerLoop1: while (j >= 0) {
+            char c = line.charAt(j);
+            if (Character.isDigit(c)) {
+              // found the last numeric character, this is the end of
+              // the free space bytes count
+              bytesEnd = j + 1;
+              break innerLoop1;
+            }
+            j--;
+        }
+        innerLoop2: while (j >= 0) {
+            char c = line.charAt(j);
+            if (!Character.isDigit(c) && c != ',' && c != '.') {
+              // found the next non-numeric character, this is the
+              // beginning of the free space bytes count
+              bytesStart = j + 1;
+              break innerLoop2;
+            }
+            j--;
+        }
+        if (j < 0) {
+            throw new IOException(
+                    "Command line 'dir /-c' did not return valid info " +
+                    "for path '" + path + "'");
+        }
+
+        // remove commas and dots in the bytes count
+        StringBuffer buf = new StringBuffer(line.substring(bytesStart, bytesEnd));
+        for (int k = 0; k < buf.length(); k++) {
+            if (buf.charAt(k) == ',' || buf.charAt(k) == '.') {
+                buf.deleteCharAt(k--);
+            }
+        }
+        return parseBytes(buf.toString(), path);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Find free space on the *nix platform using the 'df' command.
+     *
+     * @param path  the path to get free space for
+     * @param kb  whether to normalize to kilobytes
+     * @param posix  whether to use the posix standard format flag
+     * @return the amount of free drive space on the volume
+     * @throws IOException if an error occurs
+     */
+    long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {
+        if (path.length() == 0) {
+            throw new IllegalArgumentException("Path must not be empty");
+        }
+        path = FilenameUtils.normalize(path);
+
+        // build and run the 'dir' command
+        String flags = "-";
+        if (kb) {
+            flags += "k";
+        }
+        if (posix) {
+            flags += "P";
+        }
+        String[] cmdAttribs =
+            (flags.length() > 1 ? new String[] {"df", flags, path} : new String[] {"df", path});
+
+        // perform the command, asking for up to 3 lines (header, interesting, overflow)
+        List<String> lines = performCommand(cmdAttribs, 3);
+        if (lines.size() < 2) {
+            // unknown problem, throw exception
+            throw new IOException(
+                    "Command line 'df' did not return info as expected " +
+                    "for path '" + path + "'- response was " + lines);
+        }
+        String line2 = lines.get(1); // the line we're interested in
+
+        // Now, we tokenize the string. The fourth element is what we want.
+        StringTokenizer tok = new StringTokenizer(line2, " ");
+        if (tok.countTokens() < 4) {
+            // could be long Filesystem, thus data on third line
+            if (tok.countTokens() == 1 && lines.size() >= 3) {
+                String line3 = lines.get(2); // the line may be interested in
+                tok = new StringTokenizer(line3, " ");
+            } else {
+                throw new IOException(
+                        "Command line 'df' did not return data as expected " +
+                        "for path '" + path + "'- check path is valid");
+            }
+        } else {
+            tok.nextToken(); // Ignore Filesystem
+        }
+        tok.nextToken(); // Ignore 1K-blocks
+        tok.nextToken(); // Ignore Used
+        String freeSpace = tok.nextToken();
+        return parseBytes(freeSpace, path);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Parses the bytes from a string.
+     *
+     * @param freeSpace  the free space string
+     * @param path  the path
+     * @return the number of bytes
+     * @throws IOException if an error occurs
+     */
+    long parseBytes(String freeSpace, String path) throws IOException {
+        try {
+            long bytes = Long.parseLong(freeSpace);
+            if (bytes < 0) {
+                throw new IOException(
+                        "Command line 'df' did not find free space in response " +
+                        "for path '" + path + "'- check path is valid");
+            }
+            return bytes;
+
+        } catch (NumberFormatException ex) {
+            throw new IOException(
+                    "Command line 'df' did not return numeric data as expected " +
+                    "for path '" + path + "'- check path is valid");
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Performs the os command.
+     *
+     * @param cmdAttribs  the command line parameters
+     * @param max The maximum limit for the lines returned
+     * @return the parsed data
+     * @throws IOException if an error occurs
+     */
+    List<String> performCommand(String[] cmdAttribs, int max) throws IOException {
+        // this method does what it can to avoid the 'Too many open files' error
+        // based on trial and error and these links:
+        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
+        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4801027
+        // http://forum.java.sun.com/thread.jspa?threadID=533029&messageID=2572018
+        // however, its still not perfect as the JDK support is so poor
+        // (see commond-exec or ant for a better multi-threaded multi-os solution)
+
+        List<String> lines = new ArrayList<String>(20);
+        Process proc = null;
+        InputStream in = null;
+        OutputStream out = null;
+        InputStream err = null;
+        BufferedReader inr = null;
+        try {
+            proc = openProcess(cmdAttribs);
+            in = proc.getInputStream();
+            out = proc.getOutputStream();
+            err = proc.getErrorStream();
+            inr = new BufferedReader(new InputStreamReader(in));
+            String line = inr.readLine();
+            while (line != null && lines.size() < max) {
+                line = line.toLowerCase().trim();
+                lines.add(line);
+                line = inr.readLine();
+            }
+
+            proc.waitFor();
+            if (proc.exitValue() != 0) {
+                // os command problem, throw exception
+                throw new IOException(
+                        "Command line returned OS error code '" + proc.exitValue() +
+                        "' for command " + Arrays.asList(cmdAttribs));
+            }
+            if (lines.size() == 0) {
+                // unknown problem, throw exception
+                throw new IOException(
+                        "Command line did not return any info " +
+                        "for command " + Arrays.asList(cmdAttribs));
+            }
+            return lines;
+
+        } catch (InterruptedException ex) {
+            throw new IOException(
+                    "Command line threw an InterruptedException '" + ex.getMessage() +
+                    "' for command " + Arrays.asList(cmdAttribs));
+        } finally {
+            IOUtils.closeQuietly(in);
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(err);
+            IOUtils.closeQuietly(inr);
+            if (proc != null) {
+                proc.destroy();
+            }
+        }
+    }
+
+    /**
+     * Opens the process to the operating system.
+     *
+     * @param cmdAttribs  the command line parameters
+     * @return the process
+     * @throws IOException if an error occurs
+     */
+    Process openProcess(String[] cmdAttribs) throws IOException {
+        return Runtime.getRuntime().exec(cmdAttribs);
+    }
+
+}
diff --git a/src/org/apache/commons/io/FileUtils.java b/src/org/apache/commons/io/FileUtils.java
index 8715eea..adc6f11 100644
--- a/src/org/apache/commons/io/FileUtils.java
+++ b/src/org/apache/commons/io/FileUtils.java
@@ -1,1894 +1,1894 @@
-/*

- * 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.commons.io;

-

-import com.google.common.annotations.VisibleForTesting;

-

-import java.io.File;

-import java.io.FileFilter;

-import java.io.FileInputStream;

-import java.io.FileNotFoundException;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.net.URL;

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Date;

-import java.util.Iterator;

-import java.util.List;

-import java.util.zip.CRC32;

-import java.util.zip.CheckedInputStream;

-import java.util.zip.Checksum;

-

-import org.apache.commons.io.filefilter.DirectoryFileFilter;

-import org.apache.commons.io.filefilter.FalseFileFilter;

-import org.apache.commons.io.filefilter.FileFilterUtils;

-import org.apache.commons.io.filefilter.IOFileFilter;

-import org.apache.commons.io.filefilter.SuffixFileFilter;

-import org.apache.commons.io.filefilter.TrueFileFilter;

-import org.apache.commons.io.output.NullOutputStream;

-

-/**

- * General file manipulation utilities.

- * <p>

- * Facilities are provided in the following areas:

- * <ul>

- * <li>writing to a file

- * <li>reading from a file

- * <li>make a directory including parent directories

- * <li>copying files and directories

- * <li>deleting files and directories

- * <li>converting to and from a URL

- * <li>listing files and directories by filter and extension

- * <li>comparing file content

- * <li>file last changed date

- * <li>calculating a checksum

- * </ul>

- * <p>

- * Origin of code: Excalibur, Alexandria, Commons-Utils

- *

- * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>

- * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>

- * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>

- * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>

- * @author Matthew Hawthorne

- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>

- * @author Stephen Colebourne

- * @author Ian Springer

- * @author Chris Eldredge

- * @author Jim Harrington

- * @author Niall Pemberton

- * @author Sandy McArthur

- * @version $Id: FileUtils.java 610810 2008-01-10 15:04:49Z niallp $

- */

-@VisibleForTesting

-public class FileUtils {

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public FileUtils() {

-        super();

-    }

-

-    /**

-     * The number of bytes in a kilobyte.

-     */

-    public static final long ONE_KB = 1024;

-

-    /**

-     * The number of bytes in a megabyte.

-     */

-    public static final long ONE_MB = ONE_KB * ONE_KB;

-

-    /**

-     * The number of bytes in a gigabyte.

-     */

-    public static final long ONE_GB = ONE_KB * ONE_MB;

-

-    /**

-     * An empty array of type <code>File</code>.

-     */

-    public static final File[] EMPTY_FILE_ARRAY = new File[0];

-

-    //-----------------------------------------------------------------------

-    /**

-     * Opens a {@link FileInputStream} for the specified file, providing better

-     * error messages than simply calling <code>new FileInputStream(file)</code>.

-     * <p>

-     * At the end of the method either the stream will be successfully opened,

-     * or an exception will have been thrown.

-     * <p>

-     * An exception is thrown if the file does not exist.

-     * An exception is thrown if the file object exists but is a directory.

-     * An exception is thrown if the file exists but cannot be read.

-     *

-     * @param file  the file to open for input, must not be <code>null</code>

-     * @return a new {@link FileInputStream} for the specified file

-     * @throws FileNotFoundException if the file does not exist

-     * @throws IOException if the file object is a directory

-     * @throws IOException if the file cannot be read

-     * @since Commons IO 1.3

-     */

-    public static FileInputStream openInputStream(File file) throws IOException {

-        if (file.exists()) {

-            if (file.isDirectory()) {

-                throw new IOException("File '" + file + "' exists but is a directory");

-            }

-            if (file.canRead() == false) {

-                throw new IOException("File '" + file + "' cannot be read");

-            }

-        } else {

-            throw new FileNotFoundException("File '" + file + "' does not exist");

-        }

-        return new FileInputStream(file);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Opens a {@link FileOutputStream} for the specified file, checking and

-     * creating the parent directory if it does not exist.

-     * <p>

-     * At the end of the method either the stream will be successfully opened,

-     * or an exception will have been thrown.

-     * <p>

-     * The parent directory will be created if it does not exist.

-     * The file will be created if it does not exist.

-     * An exception is thrown if the file object exists but is a directory.

-     * An exception is thrown if the file exists but cannot be written to.

-     * An exception is thrown if the parent directory cannot be created.

-     *

-     * @param file  the file to open for output, must not be <code>null</code>

-     * @return a new {@link FileOutputStream} for the specified file

-     * @throws IOException if the file object is a directory

-     * @throws IOException if the file cannot be written to

-     * @throws IOException if a parent directory needs creating but that fails

-     * @since Commons IO 1.3

-     */

-    public static FileOutputStream openOutputStream(File file) throws IOException {

-        if (file.exists()) {

-            if (file.isDirectory()) {

-                throw new IOException("File '" + file + "' exists but is a directory");

-            }

-            if (file.canWrite() == false) {

-                throw new IOException("File '" + file + "' cannot be written to");

-            }

-        } else {

-            File parent = file.getParentFile();

-            if (parent != null && parent.exists() == false) {

-                if (parent.mkdirs() == false) {

-                    throw new IOException("File '" + file + "' could not be created");

-                }

-            }

-        }

-        return new FileOutputStream(file);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a human-readable version of the file size, where the input

-     * represents a specific number of bytes.

-     *

-     * @param size  the number of bytes

-     * @return a human-readable display value (includes units)

-     */

-    public static String byteCountToDisplaySize(long size) {

-        String displaySize;

-

-        if (size / ONE_GB > 0) {

-            displaySize = String.valueOf(size / ONE_GB) + " GB";

-        } else if (size / ONE_MB > 0) {

-            displaySize = String.valueOf(size / ONE_MB) + " MB";

-        } else if (size / ONE_KB > 0) {

-            displaySize = String.valueOf(size / ONE_KB) + " KB";

-        } else {

-            displaySize = String.valueOf(size) + " bytes";

-        }

-        return displaySize;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Implements the same behaviour as the "touch" utility on Unix. It creates

-     * a new file with size 0 or, if the file exists already, it is opened and

-     * closed without modifying it, but updating the file date and time.

-     * <p>

-     * NOTE: As from v1.3, this method throws an IOException if the last

-     * modified date of the file cannot be set. Also, as from v1.3 this method

-     * creates parent directories if they do not exist.

-     *

-     * @param file  the File to touch

-     * @throws IOException If an I/O problem occurs

-     */

-    public static void touch(File file) throws IOException {

-        if (!file.exists()) {

-            OutputStream out = openOutputStream(file);

-            IOUtils.closeQuietly(out);

-        }

-        boolean success = file.setLastModified(System.currentTimeMillis());

-        if (!success) {

-            throw new IOException("Unable to set the last modification time for " + file);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Converts a Collection containing java.io.File instanced into array

-     * representation. This is to account for the difference between

-     * File.listFiles() and FileUtils.listFiles().

-     *

-     * @param files  a Collection containing java.io.File instances

-     * @return an array of java.io.File

-     */

-    public static File[] convertFileCollectionToFileArray(Collection<File> files) {

-         return files.toArray(new File[files.size()]);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Finds files within a given directory (and optionally its

-     * subdirectories). All files found are filtered by an IOFileFilter.

-     *

-     * @param files the collection of files found.

-     * @param directory the directory to search in.

-     * @param filter the filter to apply to files and directories.

-     */

-    private static void innerListFiles(Collection<File> files, File directory,

-            IOFileFilter filter) {

-        File[] found = directory.listFiles((FileFilter) filter);

-        if (found != null) {

-            for (int i = 0; i < found.length; i++) {

-                if (found[i].isDirectory()) {

-                    innerListFiles(files, found[i], filter);

-                } else {

-                    files.add(found[i]);

-                }

-            }

-        }

-    }

-

-    /**

-     * Finds files within a given directory (and optionally its

-     * subdirectories). All files found are filtered by an IOFileFilter.

-     * <p>

-     * If your search should recurse into subdirectories you can pass in

-     * an IOFileFilter for directories. You don't need to bind a

-     * DirectoryFileFilter (via logical AND) to this filter. This method does

-     * that for you.

-     * <p>

-     * An example: If you want to search through all directories called

-     * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>

-     * <p>

-     * Another common usage of this method is find files in a directory

-     * tree but ignoring the directories generated CVS. You can simply pass

-     * in <code>FileFilterUtils.makeCVSAware(null)</code>.

-     *

-     * @param directory  the directory to search in

-     * @param fileFilter  filter to apply when finding files.

-     * @param dirFilter  optional filter to apply when finding subdirectories.

-     * If this parameter is <code>null</code>, subdirectories will not be included in the

-     * search. Use TrueFileFilter.INSTANCE to match all directories.

-     * @return an collection of java.io.File with the matching files

-     * @see org.apache.commons.io.filefilter.FileFilterUtils

-     * @see org.apache.commons.io.filefilter.NameFileFilter

-     */

-    public static Collection<File> listFiles(

-            File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {

-        if (!directory.isDirectory()) {

-            throw new IllegalArgumentException(

-                    "Parameter 'directory' is not a directory");

-        }

-        if (fileFilter == null) {

-            throw new NullPointerException("Parameter 'fileFilter' is null");

-        }

-

-        //Setup effective file filter

-        IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,

-            FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));

-

-        //Setup effective directory filter

-        IOFileFilter effDirFilter;

-        if (dirFilter == null) {

-            effDirFilter = FalseFileFilter.INSTANCE;

-        } else {

-            effDirFilter = FileFilterUtils.andFileFilter(dirFilter,

-                DirectoryFileFilter.INSTANCE);

-        }

-

-        //Find files

-        Collection<File> files = new java.util.LinkedList<File>();

-        innerListFiles(files, directory,

-            FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));

-        return files;

-    }

-

-    /**

-     * Allows iteration over the files in given directory (and optionally

-     * its subdirectories).

-     * <p>

-     * All files found are filtered by an IOFileFilter. This method is

-     * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.

-     *

-     * @param directory  the directory to search in

-     * @param fileFilter  filter to apply when finding files.

-     * @param dirFilter  optional filter to apply when finding subdirectories.

-     * If this parameter is <code>null</code>, subdirectories will not be included in the

-     * search. Use TrueFileFilter.INSTANCE to match all directories.

-     * @return an iterator of java.io.File for the matching files

-     * @see org.apache.commons.io.filefilter.FileFilterUtils

-     * @see org.apache.commons.io.filefilter.NameFileFilter

-     * @since Commons IO 1.2

-     */

-    public static Iterator<File> iterateFiles(

-            File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {

-        return listFiles(directory, fileFilter, dirFilter).iterator();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Converts an array of file extensions to suffixes for use

-     * with IOFileFilters.

-     *

-     * @param extensions  an array of extensions. Format: {"java", "xml"}

-     * @return an array of suffixes. Format: {".java", ".xml"}

-     */

-    private static String[] toSuffixes(String[] extensions) {

-        String[] suffixes = new String[extensions.length];

-        for (int i = 0; i < extensions.length; i++) {

-            suffixes[i] = "." + extensions[i];

-        }

-        return suffixes;

-    }

-

-

-    /**

-     * Finds files within a given directory (and optionally its subdirectories)

-     * which match an array of extensions.

-     *

-     * @param directory  the directory to search in

-     * @param extensions  an array of extensions, ex. {"java","xml"}. If this

-     * parameter is <code>null</code>, all files are returned.

-     * @param recursive  if true all subdirectories are searched as well

-     * @return an collection of java.io.File with the matching files

-     */

-    public static Collection<File> listFiles(

-            File directory, String[] extensions, boolean recursive) {

-        IOFileFilter filter;

-        if (extensions == null) {

-            filter = TrueFileFilter.INSTANCE;

-        } else {

-            String[] suffixes = toSuffixes(extensions);

-            filter = new SuffixFileFilter(suffixes);

-        }

-        return listFiles(directory, filter,

-            (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));

-    }

-

-    /**

-     * Allows iteration over the files in a given directory (and optionally

-     * its subdirectories) which match an array of extensions. This method

-     * is based on {@link #listFiles(File, String[], boolean)}.

-     *

-     * @param directory  the directory to search in

-     * @param extensions  an array of extensions, ex. {"java","xml"}. If this

-     * parameter is <code>null</code>, all files are returned.

-     * @param recursive  if true all subdirectories are searched as well

-     * @return an iterator of java.io.File with the matching files

-     * @since Commons IO 1.2

-     */

-    public static Iterator<File> iterateFiles(

-            File directory, String[] extensions, boolean recursive) {

-        return listFiles(directory, extensions, recursive).iterator();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Compares the contents of two files to determine if they are equal or not.

-     * <p>

-     * This method checks to see if the two files are different lengths

-     * or if they point to the same file, before resorting to byte-by-byte

-     * comparison of the contents.

-     * <p>

-     * Code origin: Avalon

-     *

-     * @param file1  the first file

-     * @param file2  the second file

-     * @return true if the content of the files are equal or they both don't

-     * exist, false otherwise

-     * @throws IOException in case of an I/O error

-     */

-    public static boolean contentEquals(File file1, File file2) throws IOException {

-        boolean file1Exists = file1.exists();

-        if (file1Exists != file2.exists()) {

-            return false;

-        }

-

-        if (!file1Exists) {

-            // two not existing files are equal

-            return true;

-        }

-

-        if (file1.isDirectory() || file2.isDirectory()) {

-            // don't want to compare directory contents

-            throw new IOException("Can't compare directories, only files");

-        }

-

-        if (file1.length() != file2.length()) {

-            // lengths differ, cannot be equal

-            return false;

-        }

-

-        if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {

-            // same file

-            return true;

-        }

-

-        InputStream input1 = null;

-        InputStream input2 = null;

-        try {

-            input1 = new FileInputStream(file1);

-            input2 = new FileInputStream(file2);

-            return IOUtils.contentEquals(input1, input2);

-

-        } finally {

-            IOUtils.closeQuietly(input1);

-            IOUtils.closeQuietly(input2);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Convert from a <code>URL</code> to a <code>File</code>.

-     * <p>

-     * From version 1.1 this method will decode the URL.

-     * Syntax such as <code>file:///my%20docs/file.txt</code> will be

-     * correctly decoded to <code>/my docs/file.txt</code>.

-     *

-     * @param url  the file URL to convert, <code>null</code> returns <code>null</code>

-     * @return the equivalent <code>File</code> object, or <code>null</code>

-     *  if the URL's protocol is not <code>file</code>

-     * @throws IllegalArgumentException if the file is incorrectly encoded

-     */

-    public static File toFile(URL url) {

-        if (url == null || !url.getProtocol().equals("file")) {

-            return null;

-        } else {

-            String filename = url.getFile().replace('/', File.separatorChar);

-            int pos =0;

-            while ((pos = filename.indexOf('%', pos)) >= 0) {

-                if (pos + 2 < filename.length()) {

-                    String hexStr = filename.substring(pos + 1, pos + 3);

-                    char ch = (char) Integer.parseInt(hexStr, 16);

-                    filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);

-                }

-            }

-            return new File(filename);

-        }

-    }

-

-    /**

-     * Converts each of an array of <code>URL</code> to a <code>File</code>.

-     * <p>

-     * Returns an array of the same size as the input.

-     * If the input is <code>null</code>, an empty array is returned.

-     * If the input contains <code>null</code>, the output array contains <code>null</code> at the same

-     * index.

-     * <p>

-     * This method will decode the URL.

-     * Syntax such as <code>file:///my%20docs/file.txt</code> will be

-     * correctly decoded to <code>/my docs/file.txt</code>.

-     *

-     * @param urls  the file URLs to convert, <code>null</code> returns empty array

-     * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item

-     *  if there was a <code>null</code> at that index in the input array

-     * @throws IllegalArgumentException if any file is not a URL file

-     * @throws IllegalArgumentException if any file is incorrectly encoded

-     * @since Commons IO 1.1

-     */

-    public static File[] toFiles(URL[] urls) {

-        if (urls == null || urls.length == 0) {

-            return EMPTY_FILE_ARRAY;

-        }

-        File[] files = new File[urls.length];

-        for (int i = 0; i < urls.length; i++) {

-            URL url = urls[i];

-            if (url != null) {

-                if (url.getProtocol().equals("file") == false) {

-                    throw new IllegalArgumentException(

-                            "URL could not be converted to a File: " + url);

-                }

-                files[i] = toFile(url);

-            }

-        }

-        return files;

-    }

-

-    /**

-     * Converts each of an array of <code>File</code> to a <code>URL</code>.

-     * <p>

-     * Returns an array of the same size as the input.

-     *

-     * @param files  the files to convert

-     * @return an array of URLs matching the input

-     * @throws IOException if a file cannot be converted

-     */

-    public static URL[] toURLs(File[] files) throws IOException {

-        URL[] urls = new URL[files.length];

-

-        for (int i = 0; i < urls.length; i++) {

-            urls[i] = files[i].toURI().toURL();

-        }

-

-        return urls;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Copies a file to a directory preserving the file date.

-     * <p>

-     * This method copies the contents of the specified source file

-     * to a file of the same name in the specified destination directory.

-     * The destination directory is created if it does not exist.

-     * If the destination file exists, then this method will overwrite it.

-     *

-     * @param srcFile  an existing file to copy, must not be <code>null</code>

-     * @param destDir  the directory to place the copy in, must not be <code>null</code>

-     *

-     * @throws NullPointerException if source or destination is null

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @see #copyFile(File, File, boolean)

-     */

-    public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {

-        copyFileToDirectory(srcFile, destDir, true);

-    }

-

-    /**

-     * Copies a file to a directory optionally preserving the file date.

-     * <p>

-     * This method copies the contents of the specified source file

-     * to a file of the same name in the specified destination directory.

-     * The destination directory is created if it does not exist.

-     * If the destination file exists, then this method will overwrite it.

-     *

-     * @param srcFile  an existing file to copy, must not be <code>null</code>

-     * @param destDir  the directory to place the copy in, must not be <code>null</code>

-     * @param preserveFileDate  true if the file date of the copy

-     *  should be the same as the original

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @see #copyFile(File, File, boolean)

-     * @since Commons IO 1.3

-     */

-    public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {

-        if (destDir == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (destDir.exists() && destDir.isDirectory() == false) {

-            throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");

-        }

-        copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);

-    }

-

-    /**

-     * Copies a file to a new location preserving the file date.

-     * <p>

-     * This method copies the contents of the specified source file to the

-     * specified destination file. The directory holding the destination file is

-     * created if it does not exist. If the destination file exists, then this

-     * method will overwrite it.

-     *

-     * @param srcFile  an existing file to copy, must not be <code>null</code>

-     * @param destFile  the new file, must not be <code>null</code>

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @see #copyFileToDirectory(File, File)

-     */

-    public static void copyFile(File srcFile, File destFile) throws IOException {

-        copyFile(srcFile, destFile, true);

-    }

-

-    /**

-     * Copies a file to a new location.

-     * <p>

-     * This method copies the contents of the specified source file

-     * to the specified destination file.

-     * The directory holding the destination file is created if it does not exist.

-     * If the destination file exists, then this method will overwrite it.

-     *

-     * @param srcFile  an existing file to copy, must not be <code>null</code>

-     * @param destFile  the new file, must not be <code>null</code>

-     * @param preserveFileDate  true if the file date of the copy

-     *  should be the same as the original

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @see #copyFileToDirectory(File, File, boolean)

-     */

-    public static void copyFile(File srcFile, File destFile,

-            boolean preserveFileDate) throws IOException {

-        if (srcFile == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destFile == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (srcFile.exists() == false) {

-            throw new FileNotFoundException("Source '" + srcFile + "' does not exist");

-        }

-        if (srcFile.isDirectory()) {

-            throw new IOException("Source '" + srcFile + "' exists but is a directory");

-        }

-        if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {

-            throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");

-        }

-        if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {

-            if (destFile.getParentFile().mkdirs() == false) {

-                throw new IOException("Destination '" + destFile + "' directory cannot be created");

-            }

-        }

-        if (destFile.exists() && destFile.canWrite() == false) {

-            throw new IOException("Destination '" + destFile + "' exists but is read-only");

-        }

-        doCopyFile(srcFile, destFile, preserveFileDate);

-    }

-

-    /**

-     * Internal copy file method.

-     *

-     * @param srcFile  the validated source file, must not be <code>null</code>

-     * @param destFile  the validated destination file, must not be <code>null</code>

-     * @param preserveFileDate  whether to preserve the file date

-     * @throws IOException if an error occurs

-     */

-    private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {

-        if (destFile.exists() && destFile.isDirectory()) {

-            throw new IOException("Destination '" + destFile + "' exists but is a directory");

-        }

-

-        FileInputStream input = new FileInputStream(srcFile);

-        try {

-            FileOutputStream output = new FileOutputStream(destFile);

-            try {

-                IOUtils.copy(input, output);

-            } finally {

-                IOUtils.closeQuietly(output);

-            }

-        } finally {

-            IOUtils.closeQuietly(input);

-        }

-

-        if (srcFile.length() != destFile.length()) {

-            throw new IOException("Failed to copy full contents from '" +

-                    srcFile + "' to '" + destFile + "'");

-        }

-        if (preserveFileDate) {

-            destFile.setLastModified(srcFile.lastModified());

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Copies a directory to within another directory preserving the file dates.

-     * <p>

-     * This method copies the source directory and all its contents to a

-     * directory of the same name in the specified destination directory.

-     * <p>

-     * The destination directory is created if it does not exist.

-     * If the destination directory did exist, then this method merges

-     * the source with the destination, with the source taking precedence.

-     *

-     * @param srcDir  an existing directory to copy, must not be <code>null</code>

-     * @param destDir  the directory to place the copy in, must not be <code>null</code>

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @since Commons IO 1.2

-     */

-    public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {

-        if (srcDir == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (srcDir.exists() && srcDir.isDirectory() == false) {

-            throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (destDir.exists() && destDir.isDirectory() == false) {

-            throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");

-        }

-        copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);

-    }

-

-    /**

-     * Copies a whole directory to a new location preserving the file dates.

-     * <p>

-     * This method copies the specified directory and all its child

-     * directories and files to the specified destination.

-     * The destination is the new location and name of the directory.

-     * <p>

-     * The destination directory is created if it does not exist.

-     * If the destination directory did exist, then this method merges

-     * the source with the destination, with the source taking precedence.

-     *

-     * @param srcDir  an existing directory to copy, must not be <code>null</code>

-     * @param destDir  the new directory, must not be <code>null</code>

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @since Commons IO 1.1

-     */

-    @VisibleForTesting

-    public static void copyDirectory(File srcDir, File destDir) throws IOException {

-        copyDirectory(srcDir, destDir, true);

-    }

-

-    /**

-     * Copies a whole directory to a new location.

-     * <p>

-     * This method copies the contents of the specified source directory

-     * to within the specified destination directory.

-     * <p>

-     * The destination directory is created if it does not exist.

-     * If the destination directory did exist, then this method merges

-     * the source with the destination, with the source taking precedence.

-     *

-     * @param srcDir  an existing directory to copy, must not be <code>null</code>

-     * @param destDir  the new directory, must not be <code>null</code>

-     * @param preserveFileDate  true if the file date of the copy

-     *  should be the same as the original

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @since Commons IO 1.1

-     */

-    public static void copyDirectory(File srcDir, File destDir,

-            boolean preserveFileDate) throws IOException {

-        copyDirectory(srcDir, destDir, null, preserveFileDate);

-    }

-

-    /**

-     * Copies a filtered directory to a new location preserving the file dates.

-     * <p>

-     * This method copies the contents of the specified source directory

-     * to within the specified destination directory.

-     * <p>

-     * The destination directory is created if it does not exist.

-     * If the destination directory did exist, then this method merges

-     * the source with the destination, with the source taking precedence.

-     *

-     * <h4>Example: Copy directories only</h4>

-     *  <pre>

-     *  // only copy the directory structure

-     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);

-     *  </pre>

-     *

-     * <h4>Example: Copy directories and txt files</h4>

-     *  <pre>

-     *  // Create a filter for ".txt" files

-     *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");

-     *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);

-     *

-     *  // Create a filter for either directories or ".txt" files

-     *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);

-     *

-     *  // Copy using the filter

-     *  FileUtils.copyDirectory(srcDir, destDir, filter);

-     *  </pre>

-     *

-     * @param srcDir  an existing directory to copy, must not be <code>null</code>

-     * @param destDir  the new directory, must not be <code>null</code>

-     * @param filter  the filter to apply, null means copy all directories and files

-     *  should be the same as the original

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @since Commons IO 1.4

-     */

-    public static void copyDirectory(File srcDir, File destDir,

-            FileFilter filter) throws IOException {

-        copyDirectory(srcDir, destDir, filter, true);

-    }

-

-    /**

-     * Copies a filtered directory to a new location.

-     * <p>

-     * This method copies the contents of the specified source directory

-     * to within the specified destination directory.

-     * <p>

-     * The destination directory is created if it does not exist.

-     * If the destination directory did exist, then this method merges

-     * the source with the destination, with the source taking precedence.

-     *

-     * <h4>Example: Copy directories only</h4>

-     *  <pre>

-     *  // only copy the directory structure

-     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);

-     *  </pre>

-     *

-     * <h4>Example: Copy directories and txt files</h4>

-     *  <pre>

-     *  // Create a filter for ".txt" files

-     *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");

-     *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);

-     *

-     *  // Create a filter for either directories or ".txt" files

-     *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);

-     *

-     *  // Copy using the filter

-     *  FileUtils.copyDirectory(srcDir, destDir, filter, false);

-     *  </pre>

-     *

-     * @param srcDir  an existing directory to copy, must not be <code>null</code>

-     * @param destDir  the new directory, must not be <code>null</code>

-     * @param filter  the filter to apply, null means copy all directories and files

-     * @param preserveFileDate  true if the file date of the copy

-     *  should be the same as the original

-     *

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs during copying

-     * @since Commons IO 1.4

-     */

-    public static void copyDirectory(File srcDir, File destDir,

-            FileFilter filter, boolean preserveFileDate) throws IOException {

-        if (srcDir == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (srcDir.exists() == false) {

-            throw new FileNotFoundException("Source '" + srcDir + "' does not exist");

-        }

-        if (srcDir.isDirectory() == false) {

-            throw new IOException("Source '" + srcDir + "' exists but is not a directory");

-        }

-        if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {

-            throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");

-        }

-

-        // Cater for destination being directory within the source directory (see IO-141)

-        List<String> exclusionList = null;

-        if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {

-            File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);

-            if (srcFiles != null && srcFiles.length > 0) {

-                exclusionList = new ArrayList<String>(srcFiles.length);

-                for (int i = 0; i < srcFiles.length; i++) {

-                    File copiedFile = new File(destDir, srcFiles[i].getName());

-                    exclusionList.add(copiedFile.getCanonicalPath());

-                }

-            }

-        }

-        doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);

-    }

-

-    /**

-     * Internal copy directory method.

-     *

-     * @param srcDir  the validated source directory, must not be <code>null</code>

-     * @param destDir  the validated destination directory, must not be <code>null</code>

-     * @param filter  the filter to apply, null means copy all directories and files

-     * @param preserveFileDate  whether to preserve the file date

-     * @param exclusionList  List of files and directories to exclude from the copy, may be null

-     * @throws IOException if an error occurs

-     * @since Commons IO 1.1

-     */

-    private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,

-            boolean preserveFileDate, List<String> exclusionList) throws IOException {

-        if (destDir.exists()) {

-            if (destDir.isDirectory() == false) {

-                throw new IOException("Destination '" + destDir + "' exists but is not a directory");

-            }

-        } else {

-            if (destDir.mkdirs() == false) {

-                throw new IOException("Destination '" + destDir + "' directory cannot be created");

-            }

-            if (preserveFileDate) {

-                destDir.setLastModified(srcDir.lastModified());

-            }

-        }

-        if (destDir.canWrite() == false) {

-            throw new IOException("Destination '" + destDir + "' cannot be written to");

-        }

-        // recurse

-        File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);

-        if (files == null) {  // null if security restricted

-            throw new IOException("Failed to list contents of " + srcDir);

-        }

-        for (int i = 0; i < files.length; i++) {

-            File copiedFile = new File(destDir, files[i].getName());

-            if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {

-                if (files[i].isDirectory()) {

-                    doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);

-                } else {

-                    doCopyFile(files[i], copiedFile, preserveFileDate);

-                }

-            }

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Copies bytes from the URL <code>source</code> to a file

-     * <code>destination</code>. The directories up to <code>destination</code>

-     * will be created if they don't already exist. <code>destination</code>

-     * will be overwritten if it already exists.

-     *

-     * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>

-     * @param destination  the non-directory <code>File</code> to write bytes to

-     *  (possibly overwriting), must not be <code>null</code>

-     * @throws IOException if <code>source</code> URL cannot be opened

-     * @throws IOException if <code>destination</code> is a directory

-     * @throws IOException if <code>destination</code> cannot be written

-     * @throws IOException if <code>destination</code> needs creating but can't be

-     * @throws IOException if an IO error occurs during copying

-     */

-    public static void copyURLToFile(URL source, File destination) throws IOException {

-        InputStream input = source.openStream();

-        try {

-            FileOutputStream output = openOutputStream(destination);

-            try {

-                IOUtils.copy(input, output);

-            } finally {

-                IOUtils.closeQuietly(output);

-            }

-        } finally {

-            IOUtils.closeQuietly(input);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Deletes a directory recursively.

-     *

-     * @param directory  directory to delete

-     * @throws IOException in case deletion is unsuccessful

-     */

-    public static void deleteDirectory(File directory) throws IOException {

-        if (!directory.exists()) {

-            return;

-        }

-

-        cleanDirectory(directory);

-        if (!directory.delete()) {

-            String message =

-                "Unable to delete directory " + directory + ".";

-            throw new IOException(message);

-        }

-    }

-

-    /**

-     * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.

-     * <p>

-     * The difference between File.delete() and this method are:

-     * <ul>

-     * <li>A directory to be deleted does not have to be empty.</li>

-     * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>

-     * </ul>

-     *

-     * @param file  file or directory to delete, can be <code>null</code>

-     * @return <code>true</code> if the file or directory was deleted, otherwise

-     * <code>false</code>

-     *

-     * @since Commons IO 1.4

-     */

-    public static boolean deleteQuietly(File file) {

-        if (file == null) {

-            return false;

-        }

-        try {

-            if (file.isDirectory()) {

-                cleanDirectory(file);

-            }

-        } catch (Exception e) {

-        }

-

-        try {

-            return file.delete();

-        } catch (Exception e) {

-            return false;

-        }

-    }

-

-    /**

-     * Cleans a directory without deleting it.

-     *

-     * @param directory directory to clean

-     * @throws IOException in case cleaning is unsuccessful

-     */

-    public static void cleanDirectory(File directory) throws IOException {

-        if (!directory.exists()) {

-            String message = directory + " does not exist";

-            throw new IllegalArgumentException(message);

-        }

-

-        if (!directory.isDirectory()) {

-            String message = directory + " is not a directory";

-            throw new IllegalArgumentException(message);

-        }

-

-        File[] files = directory.listFiles();

-        if (files == null) {  // null if security restricted

-            throw new IOException("Failed to list contents of " + directory);

-        }

-

-        IOException exception = null;

-        for (int i = 0; i < files.length; i++) {

-            File file = files[i];

-            try {

-                forceDelete(file);

-            } catch (IOException ioe) {

-                exception = ioe;

-            }

-        }

-

-        if (null != exception) {

-            throw exception;

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Waits for NFS to propagate a file creation, imposing a timeout.

-     * <p>

-     * This method repeatedly tests {@link File#exists()} until it returns

-     * true up to the maximum time specified in seconds.

-     *

-     * @param file  the file to check, must not be <code>null</code>

-     * @param seconds  the maximum time in seconds to wait

-     * @return true if file exists

-     * @throws NullPointerException if the file is <code>null</code>

-     */

-    public static boolean waitFor(File file, int seconds) {

-        int timeout = 0;

-        int tick = 0;

-        while (!file.exists()) {

-            if (tick++ >= 10) {

-                tick = 0;

-                if (timeout++ > seconds) {

-                    return false;

-                }

-            }

-            try {

-                Thread.sleep(100);

-            } catch (InterruptedException ignore) {

-                // ignore exception

-            } catch (Exception ex) {

-                break;

-            }

-        }

-        return true;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Reads the contents of a file into a String.

-     * The file is always closed.

-     *

-     * @param file  the file to read, must not be <code>null</code>

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @return the file contents, never <code>null</code>

-     * @throws IOException in case of an I/O error

-     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM

-     */

-    public static String readFileToString(File file, String encoding) throws IOException {

-        InputStream in = null;

-        try {

-            in = openInputStream(file);

-            return IOUtils.toString(in, encoding);

-        } finally {

-            IOUtils.closeQuietly(in);

-        }

-    }

-

-

-    /**

-     * Reads the contents of a file into a String using the default encoding for the VM.

-     * The file is always closed.

-     *

-     * @param file  the file to read, must not be <code>null</code>

-     * @return the file contents, never <code>null</code>

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.3.1

-     */

-    public static String readFileToString(File file) throws IOException {

-        return readFileToString(file, null);

-    }

-

-    /**

-     * Reads the contents of a file into a byte array.

-     * The file is always closed.

-     *

-     * @param file  the file to read, must not be <code>null</code>

-     * @return the file contents, never <code>null</code>

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.1

-     */

-    public static byte[] readFileToByteArray(File file) throws IOException {

-        InputStream in = null;

-        try {

-            in = openInputStream(file);

-            return IOUtils.toByteArray(in);

-        } finally {

-            IOUtils.closeQuietly(in);

-        }

-    }

-

-    /**

-     * Reads the contents of a file line by line to a List of Strings.

-     * The file is always closed.

-     *

-     * @param file  the file to read, must not be <code>null</code>

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @return the list of Strings representing each line in the file, never <code>null</code>

-     * @throws IOException in case of an I/O error

-     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM

-     * @since Commons IO 1.1

-     */

-    public static List<String> readLines(File file, String encoding) throws IOException {

-        InputStream in = null;

-        try {

-            in = openInputStream(file);

-            return IOUtils.readLines(in, encoding);

-        } finally {

-            IOUtils.closeQuietly(in);

-        }

-    }

-

-    /**

-     * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.

-     * The file is always closed.

-     *

-     * @param file  the file to read, must not be <code>null</code>

-     * @return the list of Strings representing each line in the file, never <code>null</code>

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.3

-     */

-    public static List<String> readLines(File file) throws IOException {

-        return readLines(file, null);

-    }

-

-    /**

-     * Returns an Iterator for the lines in a <code>File</code>.

-     * <p>

-     * This method opens an <code>InputStream</code> for the file.

-     * When you have finished with the iterator you should close the stream

-     * to free internal resources. This can be done by calling the

-     * {@link LineIterator#close()} or

-     * {@link LineIterator#closeQuietly(LineIterator)} method.

-     * <p>

-     * The recommended usage pattern is:

-     * <pre>

-     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");

-     * try {

-     *   while (it.hasNext()) {

-     *     String line = it.nextLine();

-     *     /// do something with line

-     *   }

-     * } finally {

-     *   LineIterator.closeQuietly(iterator);

-     * }

-     * </pre>

-     * <p>

-     * If an exception occurs during the creation of the iterator, the

-     * underlying stream is closed.

-     *

-     * @param file  the file to open for input, must not be <code>null</code>

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @return an Iterator of the lines in the file, never <code>null</code>

-     * @throws IOException in case of an I/O error (file closed)

-     * @since Commons IO 1.2

-     */

-    public static LineIterator lineIterator(File file, String encoding) throws IOException {

-        InputStream in = null;

-        try {

-            in = openInputStream(file);

-            return IOUtils.lineIterator(in, encoding);

-        } catch (IOException ex) {

-            IOUtils.closeQuietly(in);

-            throw ex;

-        } catch (RuntimeException ex) {

-            IOUtils.closeQuietly(in);

-            throw ex;

-        }

-    }

-

-    /**

-     * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.

-     *

-     * @param file  the file to open for input, must not be <code>null</code>

-     * @return an Iterator of the lines in the file, never <code>null</code>

-     * @throws IOException in case of an I/O error (file closed)

-     * @since Commons IO 1.3

-     * @see #lineIterator(File, String)

-     */

-    public static LineIterator lineIterator(File file) throws IOException {

-        return lineIterator(file, null);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Writes a String to a file creating the file if it does not exist.

-     *

-     * NOTE: As from v1.3, the parent directories of the file will be created

-     * if they do not exist.

-     *

-     * @param file  the file to write

-     * @param data  the content to write to the file

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @throws IOException in case of an I/O error

-     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM

-     */

-    public static void writeStringToFile(File file, String data, String encoding) throws IOException {

-        OutputStream out = null;

-        try {

-            out = openOutputStream(file);

-            IOUtils.write(data, out, encoding);

-        } finally {

-            IOUtils.closeQuietly(out);

-        }

-    }

-

-    /**

-     * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.

-     *

-     * @param file  the file to write

-     * @param data  the content to write to the file

-     * @throws IOException in case of an I/O error

-     */

-    public static void writeStringToFile(File file, String data) throws IOException {

-        writeStringToFile(file, data, null);

-    }

-

-    /**

-     * Writes a byte array to a file creating the file if it does not exist.

-     * <p>

-     * NOTE: As from v1.3, the parent directories of the file will be created

-     * if they do not exist.

-     *

-     * @param file  the file to write to

-     * @param data  the content to write to the file

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.1

-     */

-    public static void writeByteArrayToFile(File file, byte[] data) throws IOException {

-        OutputStream out = null;

-        try {

-            out = openOutputStream(file);

-            out.write(data);

-        } finally {

-            IOUtils.closeQuietly(out);

-        }

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * the specified <code>File</code> line by line.

-     * The specified character encoding and the default line ending will be used.

-     * <p>

-     * NOTE: As from v1.3, the parent directories of the file will be created

-     * if they do not exist.

-     *

-     * @param file  the file to write to

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @param lines  the lines to write, <code>null</code> entries produce blank lines

-     * @throws IOException in case of an I/O error

-     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM

-     * @since Commons IO 1.1

-     */

-    public static void writeLines(File file, String encoding, Collection<Object> lines) throws IOException {

-        writeLines(file, encoding, lines, null);

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * the specified <code>File</code> line by line.

-     * The default VM encoding and the default line ending will be used.

-     *

-     * @param file  the file to write to

-     * @param lines  the lines to write, <code>null</code> entries produce blank lines

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.3

-     */

-    public static void writeLines(File file, Collection<Object> lines) throws IOException {

-        writeLines(file, null, lines, null);

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * the specified <code>File</code> line by line.

-     * The specified character encoding and the line ending will be used.

-     * <p>

-     * NOTE: As from v1.3, the parent directories of the file will be created

-     * if they do not exist.

-     *

-     * @param file  the file to write to

-     * @param encoding  the encoding to use, <code>null</code> means platform default

-     * @param lines  the lines to write, <code>null</code> entries produce blank lines

-     * @param lineEnding  the line separator to use, <code>null</code> is system default

-     * @throws IOException in case of an I/O error

-     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM

-     * @since Commons IO 1.1

-     */

-    public static void writeLines(File file, String encoding, Collection<Object> lines, String lineEnding) throws IOException {

-        OutputStream out = null;

-        try {

-            out = openOutputStream(file);

-            IOUtils.writeLines(lines, lineEnding, out, encoding);

-        } finally {

-            IOUtils.closeQuietly(out);

-        }

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * the specified <code>File</code> line by line.

-     * The default VM encoding and the specified line ending will be used.

-     *

-     * @param file  the file to write to

-     * @param lines  the lines to write, <code>null</code> entries produce blank lines

-     * @param lineEnding  the line separator to use, <code>null</code> is system default

-     * @throws IOException in case of an I/O error

-     * @since Commons IO 1.3

-     */

-    public static void writeLines(File file, Collection<Object> lines, String lineEnding) throws IOException {

-        writeLines(file, null, lines, lineEnding);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Deletes a file. If file is a directory, delete it and all sub-directories.

-     * <p>

-     * The difference between File.delete() and this method are:

-     * <ul>

-     * <li>A directory to be deleted does not have to be empty.</li>

-     * <li>You get exceptions when a file or directory cannot be deleted.

-     *      (java.io.File methods returns a boolean)</li>

-     * </ul>

-     *

-     * @param file  file or directory to delete, must not be <code>null</code>

-     * @throws NullPointerException if the directory is <code>null</code>

-     * @throws FileNotFoundException if the file was not found

-     * @throws IOException in case deletion is unsuccessful

-     */

-    public static void forceDelete(File file) throws IOException {

-        if (file.isDirectory()) {

-            deleteDirectory(file);

-        } else {

-            boolean filePresent = file.exists();

-            if (!file.delete()) {

-                if (!filePresent){

-                    throw new FileNotFoundException("File does not exist: " + file);

-                }

-                String message =

-                    "Unable to delete file: " + file;

-                throw new IOException(message);

-            }

-        }

-    }

-

-    /**

-     * Schedules a file to be deleted when JVM exits.

-     * If file is directory delete it and all sub-directories.

-     *

-     * @param file  file or directory to delete, must not be <code>null</code>

-     * @throws NullPointerException if the file is <code>null</code>

-     * @throws IOException in case deletion is unsuccessful

-     */

-    public static void forceDeleteOnExit(File file) throws IOException {

-        if (file.isDirectory()) {

-            deleteDirectoryOnExit(file);

-        } else {

-            file.deleteOnExit();

-        }

-    }

-

-    /**

-     * Schedules a directory recursively for deletion on JVM exit.

-     *

-     * @param directory  directory to delete, must not be <code>null</code>

-     * @throws NullPointerException if the directory is <code>null</code>

-     * @throws IOException in case deletion is unsuccessful

-     */

-    private static void deleteDirectoryOnExit(File directory) throws IOException {

-        if (!directory.exists()) {

-            return;

-        }

-

-        cleanDirectoryOnExit(directory);

-        directory.deleteOnExit();

-    }

-

-    /**

-     * Cleans a directory without deleting it.

-     *

-     * @param directory  directory to clean, must not be <code>null</code>

-     * @throws NullPointerException if the directory is <code>null</code>

-     * @throws IOException in case cleaning is unsuccessful

-     */

-    private static void cleanDirectoryOnExit(File directory) throws IOException {

-        if (!directory.exists()) {

-            String message = directory + " does not exist";

-            throw new IllegalArgumentException(message);

-        }

-

-        if (!directory.isDirectory()) {

-            String message = directory + " is not a directory";

-            throw new IllegalArgumentException(message);

-        }

-

-        File[] files = directory.listFiles();

-        if (files == null) {  // null if security restricted

-            throw new IOException("Failed to list contents of " + directory);

-        }

-

-        IOException exception = null;

-        for (int i = 0; i < files.length; i++) {

-            File file = files[i];

-            try {

-                forceDeleteOnExit(file);

-            } catch (IOException ioe) {

-                exception = ioe;

-            }

-        }

-

-        if (null != exception) {

-            throw exception;

-        }

-    }

-

-    /**

-     * Makes a directory, including any necessary but nonexistent parent

-     * directories. If there already exists a file with specified name or

-     * the directory cannot be created then an exception is thrown.

-     *

-     * @param directory  directory to create, must not be <code>null</code>

-     * @throws NullPointerException if the directory is <code>null</code>

-     * @throws IOException if the directory cannot be created

-     */

-    public static void forceMkdir(File directory) throws IOException {

-        if (directory.exists()) {

-            if (directory.isFile()) {

-                String message =

-                    "File "

-                        + directory

-                        + " exists and is "

-                        + "not a directory. Unable to create directory.";

-                throw new IOException(message);

-            }

-        } else {

-            if (!directory.mkdirs()) {

-                String message =

-                    "Unable to create directory " + directory;

-                throw new IOException(message);

-            }

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Counts the size of a directory recursively (sum of the length of all files).

-     *

-     * @param directory  directory to inspect, must not be <code>null</code>

-     * @return size of directory in bytes, 0 if directory is security restricted

-     * @throws NullPointerException if the directory is <code>null</code>

-     */

-    public static long sizeOfDirectory(File directory) {

-        if (!directory.exists()) {

-            String message = directory + " does not exist";

-            throw new IllegalArgumentException(message);

-        }

-

-        if (!directory.isDirectory()) {

-            String message = directory + " is not a directory";

-            throw new IllegalArgumentException(message);

-        }

-

-        long size = 0;

-

-        File[] files = directory.listFiles();

-        if (files == null) {  // null if security restricted

-            return 0L;

-        }

-        for (int i = 0; i < files.length; i++) {

-            File file = files[i];

-

-            if (file.isDirectory()) {

-                size += sizeOfDirectory(file);

-            } else {

-                size += file.length();

-            }

-        }

-

-        return size;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Tests if the specified <code>File</code> is newer than the reference

-     * <code>File</code>.

-     *

-     * @param file  the <code>File</code> of which the modification date must

-     * be compared, must not be <code>null</code>

-     * @param reference  the <code>File</code> of which the modification date

-     * is used, must not be <code>null</code>

-     * @return true if the <code>File</code> exists and has been modified more

-     * recently than the reference <code>File</code>

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist

-     */

-     public static boolean isFileNewer(File file, File reference) {

-        if (reference == null) {

-            throw new IllegalArgumentException("No specified reference file");

-        }

-        if (!reference.exists()) {

-            throw new IllegalArgumentException("The reference file '"

-                    + file + "' doesn't exist");

-        }

-        return isFileNewer(file, reference.lastModified());

-    }

-

-    /**

-     * Tests if the specified <code>File</code> is newer than the specified

-     * <code>Date</code>.

-     *

-     * @param file  the <code>File</code> of which the modification date

-     * must be compared, must not be <code>null</code>

-     * @param date  the date reference, must not be <code>null</code>

-     * @return true if the <code>File</code> exists and has been modified

-     * after the given <code>Date</code>.

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     * @throws IllegalArgumentException if the date is <code>null</code>

-     */

-    public static boolean isFileNewer(File file, Date date) {

-        if (date == null) {

-            throw new IllegalArgumentException("No specified date");

-        }

-        return isFileNewer(file, date.getTime());

-    }

-

-    /**

-     * Tests if the specified <code>File</code> is newer than the specified

-     * time reference.

-     *

-     * @param file  the <code>File</code> of which the modification date must

-     * be compared, must not be <code>null</code>

-     * @param timeMillis  the time reference measured in milliseconds since the

-     * epoch (00:00:00 GMT, January 1, 1970)

-     * @return true if the <code>File</code> exists and has been modified after

-     * the given time reference.

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     */

-     public static boolean isFileNewer(File file, long timeMillis) {

-        if (file == null) {

-            throw new IllegalArgumentException("No specified file");

-        }

-        if (!file.exists()) {

-            return false;

-        }

-        return file.lastModified() > timeMillis;

-    }

-

-

-    //-----------------------------------------------------------------------

-    /**

-     * Tests if the specified <code>File</code> is older than the reference

-     * <code>File</code>.

-     *

-     * @param file  the <code>File</code> of which the modification date must

-     * be compared, must not be <code>null</code>

-     * @param reference  the <code>File</code> of which the modification date

-     * is used, must not be <code>null</code>

-     * @return true if the <code>File</code> exists and has been modified before

-     * the reference <code>File</code>

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist

-     */

-     public static boolean isFileOlder(File file, File reference) {

-        if (reference == null) {

-            throw new IllegalArgumentException("No specified reference file");

-        }

-        if (!reference.exists()) {

-            throw new IllegalArgumentException("The reference file '"

-                    + file + "' doesn't exist");

-        }

-        return isFileOlder(file, reference.lastModified());

-    }

-

-    /**

-     * Tests if the specified <code>File</code> is older than the specified

-     * <code>Date</code>.

-     *

-     * @param file  the <code>File</code> of which the modification date

-     * must be compared, must not be <code>null</code>

-     * @param date  the date reference, must not be <code>null</code>

-     * @return true if the <code>File</code> exists and has been modified

-     * before the given <code>Date</code>.

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     * @throws IllegalArgumentException if the date is <code>null</code>

-     */

-    public static boolean isFileOlder(File file, Date date) {

-        if (date == null) {

-            throw new IllegalArgumentException("No specified date");

-        }

-        return isFileOlder(file, date.getTime());

-    }

-

-    /**

-     * Tests if the specified <code>File</code> is older than the specified

-     * time reference.

-     *

-     * @param file  the <code>File</code> of which the modification date must

-     * be compared, must not be <code>null</code>

-     * @param timeMillis  the time reference measured in milliseconds since the

-     * epoch (00:00:00 GMT, January 1, 1970)

-     * @return true if the <code>File</code> exists and has been modified before

-     * the given time reference.

-     * @throws IllegalArgumentException if the file is <code>null</code>

-     */

-     public static boolean isFileOlder(File file, long timeMillis) {

-        if (file == null) {

-            throw new IllegalArgumentException("No specified file");

-        }

-        if (!file.exists()) {

-            return false;

-        }

-        return file.lastModified() < timeMillis;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Computes the checksum of a file using the CRC32 checksum routine.

-     * The value of the checksum is returned.

-     *

-     * @param file  the file to checksum, must not be <code>null</code>

-     * @return the checksum value

-     * @throws NullPointerException if the file or checksum is <code>null</code>

-     * @throws IllegalArgumentException if the file is a directory

-     * @throws IOException if an IO error occurs reading the file

-     * @since Commons IO 1.3

-     */

-    public static long checksumCRC32(File file) throws IOException {

-        CRC32 crc = new CRC32();

-        checksum(file, crc);

-        return crc.getValue();

-    }

-

-    /**

-     * Computes the checksum of a file using the specified checksum object.

-     * Multiple files may be checked using one <code>Checksum</code> instance

-     * if desired simply by reusing the same checksum object.

-     * For example:

-     * <pre>

-     *   long csum = FileUtils.checksum(file, new CRC32()).getValue();

-     * </pre>

-     *

-     * @param file  the file to checksum, must not be <code>null</code>

-     * @param checksum  the checksum object to be used, must not be <code>null</code>

-     * @return the checksum specified, updated with the content of the file

-     * @throws NullPointerException if the file or checksum is <code>null</code>

-     * @throws IllegalArgumentException if the file is a directory

-     * @throws IOException if an IO error occurs reading the file

-     * @since Commons IO 1.3

-     */

-    public static Checksum checksum(File file, Checksum checksum) throws IOException {

-        if (file.isDirectory()) {

-            throw new IllegalArgumentException("Checksums can't be computed on directories");

-        }

-        InputStream in = null;

-        try {

-            in = new CheckedInputStream(new FileInputStream(file), checksum);

-            IOUtils.copy(in, new NullOutputStream());

-        } finally {

-            IOUtils.closeQuietly(in);

-        }

-        return checksum;

-    }

-

-    /**

-     * Moves a directory.

-     * <p>

-     * When the destination directory is on another file system, do a "copy and delete".

-     *

-     * @param srcDir the directory to be moved

-     * @param destDir the destination directory

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs moving the file

-     * @since Commons IO 1.4

-     */

-    public static void moveDirectory(File srcDir, File destDir) throws IOException {

-        if (srcDir == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (!srcDir.exists()) {

-            throw new FileNotFoundException("Source '" + srcDir + "' does not exist");

-        }

-        if (!srcDir.isDirectory()) {

-            throw new IOException("Source '" + srcDir + "' is not a directory");

-        }

-        if (destDir.exists()) {

-            throw new IOException("Destination '" + destDir + "' already exists");

-        }

-        boolean rename = srcDir.renameTo(destDir);

-        if (!rename) {

-            copyDirectory( srcDir, destDir );

-            deleteDirectory( srcDir );

-            if (srcDir.exists()) {

-                throw new IOException("Failed to delete original directory '" + srcDir +

-                        "' after copy to '" + destDir + "'");

-            }

-        }

-    }

-

-    /**

-     * Moves a directory to another directory.

-     *

-     * @param src the file to be moved

-     * @param destDir the destination file

-     * @param createDestDir If <code>true</code> create the destination directory,

-     * otherwise if <code>false</code> throw an IOException

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs moving the file

-     * @since Commons IO 1.4

-     */

-    public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {

-        if (src == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination directory must not be null");

-        }

-        if (!destDir.exists() && createDestDir) {

-            destDir.mkdirs();

-        }

-        if (!destDir.exists()) {

-            throw new FileNotFoundException("Destination directory '" + destDir +

-                    "' does not exist [createDestDir=" + createDestDir +"]");

-        }

-        if (!destDir.isDirectory()) {

-            throw new IOException("Destination '" + destDir + "' is not a directory");

-        }

-        moveDirectory(src, new File(destDir, src.getName()));

-

-    }

-

-    /**

-     * Moves a file.

-     * <p>

-     * When the destination file is on another file system, do a "copy and delete".

-     *

-     * @param srcFile the file to be moved

-     * @param destFile the destination file

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs moving the file

-     * @since Commons IO 1.4

-     */

-    public static void moveFile(File srcFile, File destFile) throws IOException {

-        if (srcFile == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destFile == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (!srcFile.exists()) {

-            throw new FileNotFoundException("Source '" + srcFile + "' does not exist");

-        }

-        if (srcFile.isDirectory()) {

-            throw new IOException("Source '" + srcFile + "' is a directory");

-        }

-        if (destFile.exists()) {

-            throw new IOException("Destination '" + destFile + "' already exists");

-        }

-        if (destFile.isDirectory()) {

-            throw new IOException("Destination '" + destFile + "' is a directory");

-        }

-        boolean rename = srcFile.renameTo(destFile);

-        if (!rename) {

-            copyFile( srcFile, destFile );

-            if (!srcFile.delete()) {

-                FileUtils.deleteQuietly(destFile);

-                throw new IOException("Failed to delete original file '" + srcFile +

-                        "' after copy to '" + destFile + "'");

-            }

-        }

-    }

-

-    /**

-     * Moves a file to a directory.

-     *

-     * @param srcFile the file to be moved

-     * @param destDir the destination file

-     * @param createDestDir If <code>true</code> create the destination directory,

-     * otherwise if <code>false</code> throw an IOException

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs moving the file

-     * @since Commons IO 1.4

-     */

-    public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {

-        if (srcFile == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination directory must not be null");

-        }

-        if (!destDir.exists() && createDestDir) {

-            destDir.mkdirs();

-        }

-        if (!destDir.exists()) {

-            throw new FileNotFoundException("Destination directory '" + destDir +

-                    "' does not exist [createDestDir=" + createDestDir +"]");

-        }

-        if (!destDir.isDirectory()) {

-            throw new IOException("Destination '" + destDir + "' is not a directory");

-        }

-        moveFile(srcFile, new File(destDir, srcFile.getName()));

-    }

-

-    /**

-     * Moves a file or directory to the destination directory.

-     * <p>

-     * When the destination is on another file system, do a "copy and delete".

-     *

-     * @param src the file or directory to be moved

-     * @param destDir the destination directory

-     * @param createDestDir If <code>true</code> create the destination directory,

-     * otherwise if <code>false</code> throw an IOException

-     * @throws NullPointerException if source or destination is <code>null</code>

-     * @throws IOException if source or destination is invalid

-     * @throws IOException if an IO error occurs moving the file

-     * @since Commons IO 1.4

-     */

-    public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {

-        if (src == null) {

-            throw new NullPointerException("Source must not be null");

-        }

-        if (destDir == null) {

-            throw new NullPointerException("Destination must not be null");

-        }

-        if (!src.exists()) {

-            throw new FileNotFoundException("Source '" + src + "' does not exist");

-        }

-        if (src.isDirectory()) {

-            moveDirectoryToDirectory(src, destDir, createDestDir);

-        } else {

-            moveFileToDirectory(src, destDir, createDestDir);

-        }

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+import java.util.zip.Checksum;
+
+import org.apache.commons.io.filefilter.DirectoryFileFilter;
+import org.apache.commons.io.filefilter.FalseFileFilter;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.SuffixFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.apache.commons.io.output.NullOutputStream;
+
+/**
+ * General file manipulation utilities.
+ * <p>
+ * Facilities are provided in the following areas:
+ * <ul>
+ * <li>writing to a file
+ * <li>reading from a file
+ * <li>make a directory including parent directories
+ * <li>copying files and directories
+ * <li>deleting files and directories
+ * <li>converting to and from a URL
+ * <li>listing files and directories by filter and extension
+ * <li>comparing file content
+ * <li>file last changed date
+ * <li>calculating a checksum
+ * </ul>
+ * <p>
+ * Origin of code: Excalibur, Alexandria, Commons-Utils
+ *
+ * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
+ * @author Matthew Hawthorne
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @author Stephen Colebourne
+ * @author Ian Springer
+ * @author Chris Eldredge
+ * @author Jim Harrington
+ * @author Niall Pemberton
+ * @author Sandy McArthur
+ * @version $Id: FileUtils.java 610810 2008-01-10 15:04:49Z niallp $
+ */
+@VisibleForTesting
+public class FileUtils {
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public FileUtils() {
+        super();
+    }
+
+    /**
+     * The number of bytes in a kilobyte.
+     */
+    public static final long ONE_KB = 1024;
+
+    /**
+     * The number of bytes in a megabyte.
+     */
+    public static final long ONE_MB = ONE_KB * ONE_KB;
+
+    /**
+     * The number of bytes in a gigabyte.
+     */
+    public static final long ONE_GB = ONE_KB * ONE_MB;
+
+    /**
+     * An empty array of type <code>File</code>.
+     */
+    public static final File[] EMPTY_FILE_ARRAY = new File[0];
+
+    //-----------------------------------------------------------------------
+    /**
+     * Opens a {@link FileInputStream} for the specified file, providing better
+     * error messages than simply calling <code>new FileInputStream(file)</code>.
+     * <p>
+     * At the end of the method either the stream will be successfully opened,
+     * or an exception will have been thrown.
+     * <p>
+     * An exception is thrown if the file does not exist.
+     * An exception is thrown if the file object exists but is a directory.
+     * An exception is thrown if the file exists but cannot be read.
+     *
+     * @param file  the file to open for input, must not be <code>null</code>
+     * @return a new {@link FileInputStream} for the specified file
+     * @throws FileNotFoundException if the file does not exist
+     * @throws IOException if the file object is a directory
+     * @throws IOException if the file cannot be read
+     * @since Commons IO 1.3
+     */
+    public static FileInputStream openInputStream(File file) throws IOException {
+        if (file.exists()) {
+            if (file.isDirectory()) {
+                throw new IOException("File '" + file + "' exists but is a directory");
+            }
+            if (file.canRead() == false) {
+                throw new IOException("File '" + file + "' cannot be read");
+            }
+        } else {
+            throw new FileNotFoundException("File '" + file + "' does not exist");
+        }
+        return new FileInputStream(file);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Opens a {@link FileOutputStream} for the specified file, checking and
+     * creating the parent directory if it does not exist.
+     * <p>
+     * At the end of the method either the stream will be successfully opened,
+     * or an exception will have been thrown.
+     * <p>
+     * The parent directory will be created if it does not exist.
+     * The file will be created if it does not exist.
+     * An exception is thrown if the file object exists but is a directory.
+     * An exception is thrown if the file exists but cannot be written to.
+     * An exception is thrown if the parent directory cannot be created.
+     *
+     * @param file  the file to open for output, must not be <code>null</code>
+     * @return a new {@link FileOutputStream} for the specified file
+     * @throws IOException if the file object is a directory
+     * @throws IOException if the file cannot be written to
+     * @throws IOException if a parent directory needs creating but that fails
+     * @since Commons IO 1.3
+     */
+    public static FileOutputStream openOutputStream(File file) throws IOException {
+        if (file.exists()) {
+            if (file.isDirectory()) {
+                throw new IOException("File '" + file + "' exists but is a directory");
+            }
+            if (file.canWrite() == false) {
+                throw new IOException("File '" + file + "' cannot be written to");
+            }
+        } else {
+            File parent = file.getParentFile();
+            if (parent != null && parent.exists() == false) {
+                if (parent.mkdirs() == false) {
+                    throw new IOException("File '" + file + "' could not be created");
+                }
+            }
+        }
+        return new FileOutputStream(file);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a human-readable version of the file size, where the input
+     * represents a specific number of bytes.
+     *
+     * @param size  the number of bytes
+     * @return a human-readable display value (includes units)
+     */
+    public static String byteCountToDisplaySize(long size) {
+        String displaySize;
+
+        if (size / ONE_GB > 0) {
+            displaySize = String.valueOf(size / ONE_GB) + " GB";
+        } else if (size / ONE_MB > 0) {
+            displaySize = String.valueOf(size / ONE_MB) + " MB";
+        } else if (size / ONE_KB > 0) {
+            displaySize = String.valueOf(size / ONE_KB) + " KB";
+        } else {
+            displaySize = String.valueOf(size) + " bytes";
+        }
+        return displaySize;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Implements the same behaviour as the "touch" utility on Unix. It creates
+     * a new file with size 0 or, if the file exists already, it is opened and
+     * closed without modifying it, but updating the file date and time.
+     * <p>
+     * NOTE: As from v1.3, this method throws an IOException if the last
+     * modified date of the file cannot be set. Also, as from v1.3 this method
+     * creates parent directories if they do not exist.
+     *
+     * @param file  the File to touch
+     * @throws IOException If an I/O problem occurs
+     */
+    public static void touch(File file) throws IOException {
+        if (!file.exists()) {
+            OutputStream out = openOutputStream(file);
+            IOUtils.closeQuietly(out);
+        }
+        boolean success = file.setLastModified(System.currentTimeMillis());
+        if (!success) {
+            throw new IOException("Unable to set the last modification time for " + file);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Converts a Collection containing java.io.File instanced into array
+     * representation. This is to account for the difference between
+     * File.listFiles() and FileUtils.listFiles().
+     *
+     * @param files  a Collection containing java.io.File instances
+     * @return an array of java.io.File
+     */
+    public static File[] convertFileCollectionToFileArray(Collection<File> files) {
+         return files.toArray(new File[files.size()]);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Finds files within a given directory (and optionally its
+     * subdirectories). All files found are filtered by an IOFileFilter.
+     *
+     * @param files the collection of files found.
+     * @param directory the directory to search in.
+     * @param filter the filter to apply to files and directories.
+     */
+    private static void innerListFiles(Collection<File> files, File directory,
+            IOFileFilter filter) {
+        File[] found = directory.listFiles((FileFilter) filter);
+        if (found != null) {
+            for (int i = 0; i < found.length; i++) {
+                if (found[i].isDirectory()) {
+                    innerListFiles(files, found[i], filter);
+                } else {
+                    files.add(found[i]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Finds files within a given directory (and optionally its
+     * subdirectories). All files found are filtered by an IOFileFilter.
+     * <p>
+     * If your search should recurse into subdirectories you can pass in
+     * an IOFileFilter for directories. You don't need to bind a
+     * DirectoryFileFilter (via logical AND) to this filter. This method does
+     * that for you.
+     * <p>
+     * An example: If you want to search through all directories called
+     * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
+     * <p>
+     * Another common usage of this method is find files in a directory
+     * tree but ignoring the directories generated CVS. You can simply pass
+     * in <code>FileFilterUtils.makeCVSAware(null)</code>.
+     *
+     * @param directory  the directory to search in
+     * @param fileFilter  filter to apply when finding files.
+     * @param dirFilter  optional filter to apply when finding subdirectories.
+     * If this parameter is <code>null</code>, subdirectories will not be included in the
+     * search. Use TrueFileFilter.INSTANCE to match all directories.
+     * @return an collection of java.io.File with the matching files
+     * @see org.apache.commons.io.filefilter.FileFilterUtils
+     * @see org.apache.commons.io.filefilter.NameFileFilter
+     */
+    public static Collection<File> listFiles(
+            File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
+        if (!directory.isDirectory()) {
+            throw new IllegalArgumentException(
+                    "Parameter 'directory' is not a directory");
+        }
+        if (fileFilter == null) {
+            throw new NullPointerException("Parameter 'fileFilter' is null");
+        }
+
+        //Setup effective file filter
+        IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
+            FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
+
+        //Setup effective directory filter
+        IOFileFilter effDirFilter;
+        if (dirFilter == null) {
+            effDirFilter = FalseFileFilter.INSTANCE;
+        } else {
+            effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
+                DirectoryFileFilter.INSTANCE);
+        }
+
+        //Find files
+        Collection<File> files = new java.util.LinkedList<File>();
+        innerListFiles(files, directory,
+            FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
+        return files;
+    }
+
+    /**
+     * Allows iteration over the files in given directory (and optionally
+     * its subdirectories).
+     * <p>
+     * All files found are filtered by an IOFileFilter. This method is
+     * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
+     *
+     * @param directory  the directory to search in
+     * @param fileFilter  filter to apply when finding files.
+     * @param dirFilter  optional filter to apply when finding subdirectories.
+     * If this parameter is <code>null</code>, subdirectories will not be included in the
+     * search. Use TrueFileFilter.INSTANCE to match all directories.
+     * @return an iterator of java.io.File for the matching files
+     * @see org.apache.commons.io.filefilter.FileFilterUtils
+     * @see org.apache.commons.io.filefilter.NameFileFilter
+     * @since Commons IO 1.2
+     */
+    public static Iterator<File> iterateFiles(
+            File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
+        return listFiles(directory, fileFilter, dirFilter).iterator();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Converts an array of file extensions to suffixes for use
+     * with IOFileFilters.
+     *
+     * @param extensions  an array of extensions. Format: {"java", "xml"}
+     * @return an array of suffixes. Format: {".java", ".xml"}
+     */
+    private static String[] toSuffixes(String[] extensions) {
+        String[] suffixes = new String[extensions.length];
+        for (int i = 0; i < extensions.length; i++) {
+            suffixes[i] = "." + extensions[i];
+        }
+        return suffixes;
+    }
+
+
+    /**
+     * Finds files within a given directory (and optionally its subdirectories)
+     * which match an array of extensions.
+     *
+     * @param directory  the directory to search in
+     * @param extensions  an array of extensions, ex. {"java","xml"}. If this
+     * parameter is <code>null</code>, all files are returned.
+     * @param recursive  if true all subdirectories are searched as well
+     * @return an collection of java.io.File with the matching files
+     */
+    public static Collection<File> listFiles(
+            File directory, String[] extensions, boolean recursive) {
+        IOFileFilter filter;
+        if (extensions == null) {
+            filter = TrueFileFilter.INSTANCE;
+        } else {
+            String[] suffixes = toSuffixes(extensions);
+            filter = new SuffixFileFilter(suffixes);
+        }
+        return listFiles(directory, filter,
+            (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
+    }
+
+    /**
+     * Allows iteration over the files in a given directory (and optionally
+     * its subdirectories) which match an array of extensions. This method
+     * is based on {@link #listFiles(File, String[], boolean)}.
+     *
+     * @param directory  the directory to search in
+     * @param extensions  an array of extensions, ex. {"java","xml"}. If this
+     * parameter is <code>null</code>, all files are returned.
+     * @param recursive  if true all subdirectories are searched as well
+     * @return an iterator of java.io.File with the matching files
+     * @since Commons IO 1.2
+     */
+    public static Iterator<File> iterateFiles(
+            File directory, String[] extensions, boolean recursive) {
+        return listFiles(directory, extensions, recursive).iterator();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Compares the contents of two files to determine if they are equal or not.
+     * <p>
+     * This method checks to see if the two files are different lengths
+     * or if they point to the same file, before resorting to byte-by-byte
+     * comparison of the contents.
+     * <p>
+     * Code origin: Avalon
+     *
+     * @param file1  the first file
+     * @param file2  the second file
+     * @return true if the content of the files are equal or they both don't
+     * exist, false otherwise
+     * @throws IOException in case of an I/O error
+     */
+    public static boolean contentEquals(File file1, File file2) throws IOException {
+        boolean file1Exists = file1.exists();
+        if (file1Exists != file2.exists()) {
+            return false;
+        }
+
+        if (!file1Exists) {
+            // two not existing files are equal
+            return true;
+        }
+
+        if (file1.isDirectory() || file2.isDirectory()) {
+            // don't want to compare directory contents
+            throw new IOException("Can't compare directories, only files");
+        }
+
+        if (file1.length() != file2.length()) {
+            // lengths differ, cannot be equal
+            return false;
+        }
+
+        if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
+            // same file
+            return true;
+        }
+
+        InputStream input1 = null;
+        InputStream input2 = null;
+        try {
+            input1 = new FileInputStream(file1);
+            input2 = new FileInputStream(file2);
+            return IOUtils.contentEquals(input1, input2);
+
+        } finally {
+            IOUtils.closeQuietly(input1);
+            IOUtils.closeQuietly(input2);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Convert from a <code>URL</code> to a <code>File</code>.
+     * <p>
+     * From version 1.1 this method will decode the URL.
+     * Syntax such as <code>file:///my%20docs/file.txt</code> will be
+     * correctly decoded to <code>/my docs/file.txt</code>.
+     *
+     * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
+     * @return the equivalent <code>File</code> object, or <code>null</code>
+     *  if the URL's protocol is not <code>file</code>
+     * @throws IllegalArgumentException if the file is incorrectly encoded
+     */
+    public static File toFile(URL url) {
+        if (url == null || !url.getProtocol().equals("file")) {
+            return null;
+        } else {
+            String filename = url.getFile().replace('/', File.separatorChar);
+            int pos =0;
+            while ((pos = filename.indexOf('%', pos)) >= 0) {
+                if (pos + 2 < filename.length()) {
+                    String hexStr = filename.substring(pos + 1, pos + 3);
+                    char ch = (char) Integer.parseInt(hexStr, 16);
+                    filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
+                }
+            }
+            return new File(filename);
+        }
+    }
+
+    /**
+     * Converts each of an array of <code>URL</code> to a <code>File</code>.
+     * <p>
+     * Returns an array of the same size as the input.
+     * If the input is <code>null</code>, an empty array is returned.
+     * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
+     * index.
+     * <p>
+     * This method will decode the URL.
+     * Syntax such as <code>file:///my%20docs/file.txt</code> will be
+     * correctly decoded to <code>/my docs/file.txt</code>.
+     *
+     * @param urls  the file URLs to convert, <code>null</code> returns empty array
+     * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
+     *  if there was a <code>null</code> at that index in the input array
+     * @throws IllegalArgumentException if any file is not a URL file
+     * @throws IllegalArgumentException if any file is incorrectly encoded
+     * @since Commons IO 1.1
+     */
+    public static File[] toFiles(URL[] urls) {
+        if (urls == null || urls.length == 0) {
+            return EMPTY_FILE_ARRAY;
+        }
+        File[] files = new File[urls.length];
+        for (int i = 0; i < urls.length; i++) {
+            URL url = urls[i];
+            if (url != null) {
+                if (url.getProtocol().equals("file") == false) {
+                    throw new IllegalArgumentException(
+                            "URL could not be converted to a File: " + url);
+                }
+                files[i] = toFile(url);
+            }
+        }
+        return files;
+    }
+
+    /**
+     * Converts each of an array of <code>File</code> to a <code>URL</code>.
+     * <p>
+     * Returns an array of the same size as the input.
+     *
+     * @param files  the files to convert
+     * @return an array of URLs matching the input
+     * @throws IOException if a file cannot be converted
+     */
+    public static URL[] toURLs(File[] files) throws IOException {
+        URL[] urls = new URL[files.length];
+
+        for (int i = 0; i < urls.length; i++) {
+            urls[i] = files[i].toURI().toURL();
+        }
+
+        return urls;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Copies a file to a directory preserving the file date.
+     * <p>
+     * This method copies the contents of the specified source file
+     * to a file of the same name in the specified destination directory.
+     * The destination directory is created if it does not exist.
+     * If the destination file exists, then this method will overwrite it.
+     *
+     * @param srcFile  an existing file to copy, must not be <code>null</code>
+     * @param destDir  the directory to place the copy in, must not be <code>null</code>
+     *
+     * @throws NullPointerException if source or destination is null
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @see #copyFile(File, File, boolean)
+     */
+    public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
+        copyFileToDirectory(srcFile, destDir, true);
+    }
+
+    /**
+     * Copies a file to a directory optionally preserving the file date.
+     * <p>
+     * This method copies the contents of the specified source file
+     * to a file of the same name in the specified destination directory.
+     * The destination directory is created if it does not exist.
+     * If the destination file exists, then this method will overwrite it.
+     *
+     * @param srcFile  an existing file to copy, must not be <code>null</code>
+     * @param destDir  the directory to place the copy in, must not be <code>null</code>
+     * @param preserveFileDate  true if the file date of the copy
+     *  should be the same as the original
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @see #copyFile(File, File, boolean)
+     * @since Commons IO 1.3
+     */
+    public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
+        if (destDir == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (destDir.exists() && destDir.isDirectory() == false) {
+            throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
+        }
+        copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
+    }
+
+    /**
+     * Copies a file to a new location preserving the file date.
+     * <p>
+     * This method copies the contents of the specified source file to the
+     * specified destination file. The directory holding the destination file is
+     * created if it does not exist. If the destination file exists, then this
+     * method will overwrite it.
+     *
+     * @param srcFile  an existing file to copy, must not be <code>null</code>
+     * @param destFile  the new file, must not be <code>null</code>
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @see #copyFileToDirectory(File, File)
+     */
+    public static void copyFile(File srcFile, File destFile) throws IOException {
+        copyFile(srcFile, destFile, true);
+    }
+
+    /**
+     * Copies a file to a new location.
+     * <p>
+     * This method copies the contents of the specified source file
+     * to the specified destination file.
+     * The directory holding the destination file is created if it does not exist.
+     * If the destination file exists, then this method will overwrite it.
+     *
+     * @param srcFile  an existing file to copy, must not be <code>null</code>
+     * @param destFile  the new file, must not be <code>null</code>
+     * @param preserveFileDate  true if the file date of the copy
+     *  should be the same as the original
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @see #copyFileToDirectory(File, File, boolean)
+     */
+    public static void copyFile(File srcFile, File destFile,
+            boolean preserveFileDate) throws IOException {
+        if (srcFile == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destFile == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (srcFile.exists() == false) {
+            throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
+        }
+        if (srcFile.isDirectory()) {
+            throw new IOException("Source '" + srcFile + "' exists but is a directory");
+        }
+        if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
+            throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
+        }
+        if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
+            if (destFile.getParentFile().mkdirs() == false) {
+                throw new IOException("Destination '" + destFile + "' directory cannot be created");
+            }
+        }
+        if (destFile.exists() && destFile.canWrite() == false) {
+            throw new IOException("Destination '" + destFile + "' exists but is read-only");
+        }
+        doCopyFile(srcFile, destFile, preserveFileDate);
+    }
+
+    /**
+     * Internal copy file method.
+     *
+     * @param srcFile  the validated source file, must not be <code>null</code>
+     * @param destFile  the validated destination file, must not be <code>null</code>
+     * @param preserveFileDate  whether to preserve the file date
+     * @throws IOException if an error occurs
+     */
+    private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
+        if (destFile.exists() && destFile.isDirectory()) {
+            throw new IOException("Destination '" + destFile + "' exists but is a directory");
+        }
+
+        FileInputStream input = new FileInputStream(srcFile);
+        try {
+            FileOutputStream output = new FileOutputStream(destFile);
+            try {
+                IOUtils.copy(input, output);
+            } finally {
+                IOUtils.closeQuietly(output);
+            }
+        } finally {
+            IOUtils.closeQuietly(input);
+        }
+
+        if (srcFile.length() != destFile.length()) {
+            throw new IOException("Failed to copy full contents from '" +
+                    srcFile + "' to '" + destFile + "'");
+        }
+        if (preserveFileDate) {
+            destFile.setLastModified(srcFile.lastModified());
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Copies a directory to within another directory preserving the file dates.
+     * <p>
+     * This method copies the source directory and all its contents to a
+     * directory of the same name in the specified destination directory.
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     *
+     * @param srcDir  an existing directory to copy, must not be <code>null</code>
+     * @param destDir  the directory to place the copy in, must not be <code>null</code>
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @since Commons IO 1.2
+     */
+    public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
+        if (srcDir == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (srcDir.exists() && srcDir.isDirectory() == false) {
+            throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (destDir.exists() && destDir.isDirectory() == false) {
+            throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
+        }
+        copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
+    }
+
+    /**
+     * Copies a whole directory to a new location preserving the file dates.
+     * <p>
+     * This method copies the specified directory and all its child
+     * directories and files to the specified destination.
+     * The destination is the new location and name of the directory.
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     *
+     * @param srcDir  an existing directory to copy, must not be <code>null</code>
+     * @param destDir  the new directory, must not be <code>null</code>
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @since Commons IO 1.1
+     */
+    @VisibleForTesting
+    public static void copyDirectory(File srcDir, File destDir) throws IOException {
+        copyDirectory(srcDir, destDir, true);
+    }
+
+    /**
+     * Copies a whole directory to a new location.
+     * <p>
+     * This method copies the contents of the specified source directory
+     * to within the specified destination directory.
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     *
+     * @param srcDir  an existing directory to copy, must not be <code>null</code>
+     * @param destDir  the new directory, must not be <code>null</code>
+     * @param preserveFileDate  true if the file date of the copy
+     *  should be the same as the original
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @since Commons IO 1.1
+     */
+    public static void copyDirectory(File srcDir, File destDir,
+            boolean preserveFileDate) throws IOException {
+        copyDirectory(srcDir, destDir, null, preserveFileDate);
+    }
+
+    /**
+     * Copies a filtered directory to a new location preserving the file dates.
+     * <p>
+     * This method copies the contents of the specified source directory
+     * to within the specified destination directory.
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     *
+     * <h4>Example: Copy directories only</h4>
+     *  <pre>
+     *  // only copy the directory structure
+     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
+     *  </pre>
+     *
+     * <h4>Example: Copy directories and txt files</h4>
+     *  <pre>
+     *  // Create a filter for ".txt" files
+     *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
+     *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
+     *
+     *  // Create a filter for either directories or ".txt" files
+     *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
+     *
+     *  // Copy using the filter
+     *  FileUtils.copyDirectory(srcDir, destDir, filter);
+     *  </pre>
+     *
+     * @param srcDir  an existing directory to copy, must not be <code>null</code>
+     * @param destDir  the new directory, must not be <code>null</code>
+     * @param filter  the filter to apply, null means copy all directories and files
+     *  should be the same as the original
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @since Commons IO 1.4
+     */
+    public static void copyDirectory(File srcDir, File destDir,
+            FileFilter filter) throws IOException {
+        copyDirectory(srcDir, destDir, filter, true);
+    }
+
+    /**
+     * Copies a filtered directory to a new location.
+     * <p>
+     * This method copies the contents of the specified source directory
+     * to within the specified destination directory.
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     *
+     * <h4>Example: Copy directories only</h4>
+     *  <pre>
+     *  // only copy the directory structure
+     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
+     *  </pre>
+     *
+     * <h4>Example: Copy directories and txt files</h4>
+     *  <pre>
+     *  // Create a filter for ".txt" files
+     *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
+     *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
+     *
+     *  // Create a filter for either directories or ".txt" files
+     *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
+     *
+     *  // Copy using the filter
+     *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
+     *  </pre>
+     *
+     * @param srcDir  an existing directory to copy, must not be <code>null</code>
+     * @param destDir  the new directory, must not be <code>null</code>
+     * @param filter  the filter to apply, null means copy all directories and files
+     * @param preserveFileDate  true if the file date of the copy
+     *  should be the same as the original
+     *
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs during copying
+     * @since Commons IO 1.4
+     */
+    public static void copyDirectory(File srcDir, File destDir,
+            FileFilter filter, boolean preserveFileDate) throws IOException {
+        if (srcDir == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (srcDir.exists() == false) {
+            throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
+        }
+        if (srcDir.isDirectory() == false) {
+            throw new IOException("Source '" + srcDir + "' exists but is not a directory");
+        }
+        if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
+            throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
+        }
+
+        // Cater for destination being directory within the source directory (see IO-141)
+        List<String> exclusionList = null;
+        if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
+            File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
+            if (srcFiles != null && srcFiles.length > 0) {
+                exclusionList = new ArrayList<String>(srcFiles.length);
+                for (int i = 0; i < srcFiles.length; i++) {
+                    File copiedFile = new File(destDir, srcFiles[i].getName());
+                    exclusionList.add(copiedFile.getCanonicalPath());
+                }
+            }
+        }
+        doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
+    }
+
+    /**
+     * Internal copy directory method.
+     *
+     * @param srcDir  the validated source directory, must not be <code>null</code>
+     * @param destDir  the validated destination directory, must not be <code>null</code>
+     * @param filter  the filter to apply, null means copy all directories and files
+     * @param preserveFileDate  whether to preserve the file date
+     * @param exclusionList  List of files and directories to exclude from the copy, may be null
+     * @throws IOException if an error occurs
+     * @since Commons IO 1.1
+     */
+    private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
+            boolean preserveFileDate, List<String> exclusionList) throws IOException {
+        if (destDir.exists()) {
+            if (destDir.isDirectory() == false) {
+                throw new IOException("Destination '" + destDir + "' exists but is not a directory");
+            }
+        } else {
+            if (destDir.mkdirs() == false) {
+                throw new IOException("Destination '" + destDir + "' directory cannot be created");
+            }
+            if (preserveFileDate) {
+                destDir.setLastModified(srcDir.lastModified());
+            }
+        }
+        if (destDir.canWrite() == false) {
+            throw new IOException("Destination '" + destDir + "' cannot be written to");
+        }
+        // recurse
+        File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
+        if (files == null) {  // null if security restricted
+            throw new IOException("Failed to list contents of " + srcDir);
+        }
+        for (int i = 0; i < files.length; i++) {
+            File copiedFile = new File(destDir, files[i].getName());
+            if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
+                if (files[i].isDirectory()) {
+                    doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
+                } else {
+                    doCopyFile(files[i], copiedFile, preserveFileDate);
+                }
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Copies bytes from the URL <code>source</code> to a file
+     * <code>destination</code>. The directories up to <code>destination</code>
+     * will be created if they don't already exist. <code>destination</code>
+     * will be overwritten if it already exists.
+     *
+     * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
+     * @param destination  the non-directory <code>File</code> to write bytes to
+     *  (possibly overwriting), must not be <code>null</code>
+     * @throws IOException if <code>source</code> URL cannot be opened
+     * @throws IOException if <code>destination</code> is a directory
+     * @throws IOException if <code>destination</code> cannot be written
+     * @throws IOException if <code>destination</code> needs creating but can't be
+     * @throws IOException if an IO error occurs during copying
+     */
+    public static void copyURLToFile(URL source, File destination) throws IOException {
+        InputStream input = source.openStream();
+        try {
+            FileOutputStream output = openOutputStream(destination);
+            try {
+                IOUtils.copy(input, output);
+            } finally {
+                IOUtils.closeQuietly(output);
+            }
+        } finally {
+            IOUtils.closeQuietly(input);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Deletes a directory recursively.
+     *
+     * @param directory  directory to delete
+     * @throws IOException in case deletion is unsuccessful
+     */
+    public static void deleteDirectory(File directory) throws IOException {
+        if (!directory.exists()) {
+            return;
+        }
+
+        cleanDirectory(directory);
+        if (!directory.delete()) {
+            String message =
+                "Unable to delete directory " + directory + ".";
+            throw new IOException(message);
+        }
+    }
+
+    /**
+     * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
+     * <p>
+     * The difference between File.delete() and this method are:
+     * <ul>
+     * <li>A directory to be deleted does not have to be empty.</li>
+     * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
+     * </ul>
+     *
+     * @param file  file or directory to delete, can be <code>null</code>
+     * @return <code>true</code> if the file or directory was deleted, otherwise
+     * <code>false</code>
+     *
+     * @since Commons IO 1.4
+     */
+    public static boolean deleteQuietly(File file) {
+        if (file == null) {
+            return false;
+        }
+        try {
+            if (file.isDirectory()) {
+                cleanDirectory(file);
+            }
+        } catch (Exception e) {
+        }
+
+        try {
+            return file.delete();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * Cleans a directory without deleting it.
+     *
+     * @param directory directory to clean
+     * @throws IOException in case cleaning is unsuccessful
+     */
+    public static void cleanDirectory(File directory) throws IOException {
+        if (!directory.exists()) {
+            String message = directory + " does not exist";
+            throw new IllegalArgumentException(message);
+        }
+
+        if (!directory.isDirectory()) {
+            String message = directory + " is not a directory";
+            throw new IllegalArgumentException(message);
+        }
+
+        File[] files = directory.listFiles();
+        if (files == null) {  // null if security restricted
+            throw new IOException("Failed to list contents of " + directory);
+        }
+
+        IOException exception = null;
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            try {
+                forceDelete(file);
+            } catch (IOException ioe) {
+                exception = ioe;
+            }
+        }
+
+        if (null != exception) {
+            throw exception;
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Waits for NFS to propagate a file creation, imposing a timeout.
+     * <p>
+     * This method repeatedly tests {@link File#exists()} until it returns
+     * true up to the maximum time specified in seconds.
+     *
+     * @param file  the file to check, must not be <code>null</code>
+     * @param seconds  the maximum time in seconds to wait
+     * @return true if file exists
+     * @throws NullPointerException if the file is <code>null</code>
+     */
+    public static boolean waitFor(File file, int seconds) {
+        int timeout = 0;
+        int tick = 0;
+        while (!file.exists()) {
+            if (tick++ >= 10) {
+                tick = 0;
+                if (timeout++ > seconds) {
+                    return false;
+                }
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ignore) {
+                // ignore exception
+            } catch (Exception ex) {
+                break;
+            }
+        }
+        return true;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Reads the contents of a file into a String.
+     * The file is always closed.
+     *
+     * @param file  the file to read, must not be <code>null</code>
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @return the file contents, never <code>null</code>
+     * @throws IOException in case of an I/O error
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
+     */
+    public static String readFileToString(File file, String encoding) throws IOException {
+        InputStream in = null;
+        try {
+            in = openInputStream(file);
+            return IOUtils.toString(in, encoding);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+
+    /**
+     * Reads the contents of a file into a String using the default encoding for the VM.
+     * The file is always closed.
+     *
+     * @param file  the file to read, must not be <code>null</code>
+     * @return the file contents, never <code>null</code>
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.3.1
+     */
+    public static String readFileToString(File file) throws IOException {
+        return readFileToString(file, null);
+    }
+
+    /**
+     * Reads the contents of a file into a byte array.
+     * The file is always closed.
+     *
+     * @param file  the file to read, must not be <code>null</code>
+     * @return the file contents, never <code>null</code>
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.1
+     */
+    public static byte[] readFileToByteArray(File file) throws IOException {
+        InputStream in = null;
+        try {
+            in = openInputStream(file);
+            return IOUtils.toByteArray(in);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+    /**
+     * Reads the contents of a file line by line to a List of Strings.
+     * The file is always closed.
+     *
+     * @param file  the file to read, must not be <code>null</code>
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @return the list of Strings representing each line in the file, never <code>null</code>
+     * @throws IOException in case of an I/O error
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
+     * @since Commons IO 1.1
+     */
+    public static List<String> readLines(File file, String encoding) throws IOException {
+        InputStream in = null;
+        try {
+            in = openInputStream(file);
+            return IOUtils.readLines(in, encoding);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+    /**
+     * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
+     * The file is always closed.
+     *
+     * @param file  the file to read, must not be <code>null</code>
+     * @return the list of Strings representing each line in the file, never <code>null</code>
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.3
+     */
+    public static List<String> readLines(File file) throws IOException {
+        return readLines(file, null);
+    }
+
+    /**
+     * Returns an Iterator for the lines in a <code>File</code>.
+     * <p>
+     * This method opens an <code>InputStream</code> for the file.
+     * When you have finished with the iterator you should close the stream
+     * to free internal resources. This can be done by calling the
+     * {@link LineIterator#close()} or
+     * {@link LineIterator#closeQuietly(LineIterator)} method.
+     * <p>
+     * The recommended usage pattern is:
+     * <pre>
+     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
+     * try {
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   LineIterator.closeQuietly(iterator);
+     * }
+     * </pre>
+     * <p>
+     * If an exception occurs during the creation of the iterator, the
+     * underlying stream is closed.
+     *
+     * @param file  the file to open for input, must not be <code>null</code>
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @return an Iterator of the lines in the file, never <code>null</code>
+     * @throws IOException in case of an I/O error (file closed)
+     * @since Commons IO 1.2
+     */
+    public static LineIterator lineIterator(File file, String encoding) throws IOException {
+        InputStream in = null;
+        try {
+            in = openInputStream(file);
+            return IOUtils.lineIterator(in, encoding);
+        } catch (IOException ex) {
+            IOUtils.closeQuietly(in);
+            throw ex;
+        } catch (RuntimeException ex) {
+            IOUtils.closeQuietly(in);
+            throw ex;
+        }
+    }
+
+    /**
+     * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
+     *
+     * @param file  the file to open for input, must not be <code>null</code>
+     * @return an Iterator of the lines in the file, never <code>null</code>
+     * @throws IOException in case of an I/O error (file closed)
+     * @since Commons IO 1.3
+     * @see #lineIterator(File, String)
+     */
+    public static LineIterator lineIterator(File file) throws IOException {
+        return lineIterator(file, null);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Writes a String to a file creating the file if it does not exist.
+     *
+     * NOTE: As from v1.3, the parent directories of the file will be created
+     * if they do not exist.
+     *
+     * @param file  the file to write
+     * @param data  the content to write to the file
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @throws IOException in case of an I/O error
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
+     */
+    public static void writeStringToFile(File file, String data, String encoding) throws IOException {
+        OutputStream out = null;
+        try {
+            out = openOutputStream(file);
+            IOUtils.write(data, out, encoding);
+        } finally {
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
+     *
+     * @param file  the file to write
+     * @param data  the content to write to the file
+     * @throws IOException in case of an I/O error
+     */
+    public static void writeStringToFile(File file, String data) throws IOException {
+        writeStringToFile(file, data, null);
+    }
+
+    /**
+     * Writes a byte array to a file creating the file if it does not exist.
+     * <p>
+     * NOTE: As from v1.3, the parent directories of the file will be created
+     * if they do not exist.
+     *
+     * @param file  the file to write to
+     * @param data  the content to write to the file
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.1
+     */
+    public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
+        OutputStream out = null;
+        try {
+            out = openOutputStream(file);
+            out.write(data);
+        } finally {
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * the specified <code>File</code> line by line.
+     * The specified character encoding and the default line ending will be used.
+     * <p>
+     * NOTE: As from v1.3, the parent directories of the file will be created
+     * if they do not exist.
+     *
+     * @param file  the file to write to
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @param lines  the lines to write, <code>null</code> entries produce blank lines
+     * @throws IOException in case of an I/O error
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
+     * @since Commons IO 1.1
+     */
+    public static void writeLines(File file, String encoding, Collection<Object> lines) throws IOException {
+        writeLines(file, encoding, lines, null);
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * the specified <code>File</code> line by line.
+     * The default VM encoding and the default line ending will be used.
+     *
+     * @param file  the file to write to
+     * @param lines  the lines to write, <code>null</code> entries produce blank lines
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.3
+     */
+    public static void writeLines(File file, Collection<Object> lines) throws IOException {
+        writeLines(file, null, lines, null);
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * the specified <code>File</code> line by line.
+     * The specified character encoding and the line ending will be used.
+     * <p>
+     * NOTE: As from v1.3, the parent directories of the file will be created
+     * if they do not exist.
+     *
+     * @param file  the file to write to
+     * @param encoding  the encoding to use, <code>null</code> means platform default
+     * @param lines  the lines to write, <code>null</code> entries produce blank lines
+     * @param lineEnding  the line separator to use, <code>null</code> is system default
+     * @throws IOException in case of an I/O error
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
+     * @since Commons IO 1.1
+     */
+    public static void writeLines(File file, String encoding, Collection<Object> lines, String lineEnding) throws IOException {
+        OutputStream out = null;
+        try {
+            out = openOutputStream(file);
+            IOUtils.writeLines(lines, lineEnding, out, encoding);
+        } finally {
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * the specified <code>File</code> line by line.
+     * The default VM encoding and the specified line ending will be used.
+     *
+     * @param file  the file to write to
+     * @param lines  the lines to write, <code>null</code> entries produce blank lines
+     * @param lineEnding  the line separator to use, <code>null</code> is system default
+     * @throws IOException in case of an I/O error
+     * @since Commons IO 1.3
+     */
+    public static void writeLines(File file, Collection<Object> lines, String lineEnding) throws IOException {
+        writeLines(file, null, lines, lineEnding);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Deletes a file. If file is a directory, delete it and all sub-directories.
+     * <p>
+     * The difference between File.delete() and this method are:
+     * <ul>
+     * <li>A directory to be deleted does not have to be empty.</li>
+     * <li>You get exceptions when a file or directory cannot be deleted.
+     *      (java.io.File methods returns a boolean)</li>
+     * </ul>
+     *
+     * @param file  file or directory to delete, must not be <code>null</code>
+     * @throws NullPointerException if the directory is <code>null</code>
+     * @throws FileNotFoundException if the file was not found
+     * @throws IOException in case deletion is unsuccessful
+     */
+    public static void forceDelete(File file) throws IOException {
+        if (file.isDirectory()) {
+            deleteDirectory(file);
+        } else {
+            boolean filePresent = file.exists();
+            if (!file.delete()) {
+                if (!filePresent){
+                    throw new FileNotFoundException("File does not exist: " + file);
+                }
+                String message =
+                    "Unable to delete file: " + file;
+                throw new IOException(message);
+            }
+        }
+    }
+
+    /**
+     * Schedules a file to be deleted when JVM exits.
+     * If file is directory delete it and all sub-directories.
+     *
+     * @param file  file or directory to delete, must not be <code>null</code>
+     * @throws NullPointerException if the file is <code>null</code>
+     * @throws IOException in case deletion is unsuccessful
+     */
+    public static void forceDeleteOnExit(File file) throws IOException {
+        if (file.isDirectory()) {
+            deleteDirectoryOnExit(file);
+        } else {
+            file.deleteOnExit();
+        }
+    }
+
+    /**
+     * Schedules a directory recursively for deletion on JVM exit.
+     *
+     * @param directory  directory to delete, must not be <code>null</code>
+     * @throws NullPointerException if the directory is <code>null</code>
+     * @throws IOException in case deletion is unsuccessful
+     */
+    private static void deleteDirectoryOnExit(File directory) throws IOException {
+        if (!directory.exists()) {
+            return;
+        }
+
+        cleanDirectoryOnExit(directory);
+        directory.deleteOnExit();
+    }
+
+    /**
+     * Cleans a directory without deleting it.
+     *
+     * @param directory  directory to clean, must not be <code>null</code>
+     * @throws NullPointerException if the directory is <code>null</code>
+     * @throws IOException in case cleaning is unsuccessful
+     */
+    private static void cleanDirectoryOnExit(File directory) throws IOException {
+        if (!directory.exists()) {
+            String message = directory + " does not exist";
+            throw new IllegalArgumentException(message);
+        }
+
+        if (!directory.isDirectory()) {
+            String message = directory + " is not a directory";
+            throw new IllegalArgumentException(message);
+        }
+
+        File[] files = directory.listFiles();
+        if (files == null) {  // null if security restricted
+            throw new IOException("Failed to list contents of " + directory);
+        }
+
+        IOException exception = null;
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            try {
+                forceDeleteOnExit(file);
+            } catch (IOException ioe) {
+                exception = ioe;
+            }
+        }
+
+        if (null != exception) {
+            throw exception;
+        }
+    }
+
+    /**
+     * Makes a directory, including any necessary but nonexistent parent
+     * directories. If there already exists a file with specified name or
+     * the directory cannot be created then an exception is thrown.
+     *
+     * @param directory  directory to create, must not be <code>null</code>
+     * @throws NullPointerException if the directory is <code>null</code>
+     * @throws IOException if the directory cannot be created
+     */
+    public static void forceMkdir(File directory) throws IOException {
+        if (directory.exists()) {
+            if (directory.isFile()) {
+                String message =
+                    "File "
+                        + directory
+                        + " exists and is "
+                        + "not a directory. Unable to create directory.";
+                throw new IOException(message);
+            }
+        } else {
+            if (!directory.mkdirs()) {
+                String message =
+                    "Unable to create directory " + directory;
+                throw new IOException(message);
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Counts the size of a directory recursively (sum of the length of all files).
+     *
+     * @param directory  directory to inspect, must not be <code>null</code>
+     * @return size of directory in bytes, 0 if directory is security restricted
+     * @throws NullPointerException if the directory is <code>null</code>
+     */
+    public static long sizeOfDirectory(File directory) {
+        if (!directory.exists()) {
+            String message = directory + " does not exist";
+            throw new IllegalArgumentException(message);
+        }
+
+        if (!directory.isDirectory()) {
+            String message = directory + " is not a directory";
+            throw new IllegalArgumentException(message);
+        }
+
+        long size = 0;
+
+        File[] files = directory.listFiles();
+        if (files == null) {  // null if security restricted
+            return 0L;
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+
+            if (file.isDirectory()) {
+                size += sizeOfDirectory(file);
+            } else {
+                size += file.length();
+            }
+        }
+
+        return size;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests if the specified <code>File</code> is newer than the reference
+     * <code>File</code>.
+     *
+     * @param file  the <code>File</code> of which the modification date must
+     * be compared, must not be <code>null</code>
+     * @param reference  the <code>File</code> of which the modification date
+     * is used, must not be <code>null</code>
+     * @return true if the <code>File</code> exists and has been modified more
+     * recently than the reference <code>File</code>
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
+     */
+     public static boolean isFileNewer(File file, File reference) {
+        if (reference == null) {
+            throw new IllegalArgumentException("No specified reference file");
+        }
+        if (!reference.exists()) {
+            throw new IllegalArgumentException("The reference file '"
+                    + file + "' doesn't exist");
+        }
+        return isFileNewer(file, reference.lastModified());
+    }
+
+    /**
+     * Tests if the specified <code>File</code> is newer than the specified
+     * <code>Date</code>.
+     *
+     * @param file  the <code>File</code> of which the modification date
+     * must be compared, must not be <code>null</code>
+     * @param date  the date reference, must not be <code>null</code>
+     * @return true if the <code>File</code> exists and has been modified
+     * after the given <code>Date</code>.
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     * @throws IllegalArgumentException if the date is <code>null</code>
+     */
+    public static boolean isFileNewer(File file, Date date) {
+        if (date == null) {
+            throw new IllegalArgumentException("No specified date");
+        }
+        return isFileNewer(file, date.getTime());
+    }
+
+    /**
+     * Tests if the specified <code>File</code> is newer than the specified
+     * time reference.
+     *
+     * @param file  the <code>File</code> of which the modification date must
+     * be compared, must not be <code>null</code>
+     * @param timeMillis  the time reference measured in milliseconds since the
+     * epoch (00:00:00 GMT, January 1, 1970)
+     * @return true if the <code>File</code> exists and has been modified after
+     * the given time reference.
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     */
+     public static boolean isFileNewer(File file, long timeMillis) {
+        if (file == null) {
+            throw new IllegalArgumentException("No specified file");
+        }
+        if (!file.exists()) {
+            return false;
+        }
+        return file.lastModified() > timeMillis;
+    }
+
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests if the specified <code>File</code> is older than the reference
+     * <code>File</code>.
+     *
+     * @param file  the <code>File</code> of which the modification date must
+     * be compared, must not be <code>null</code>
+     * @param reference  the <code>File</code> of which the modification date
+     * is used, must not be <code>null</code>
+     * @return true if the <code>File</code> exists and has been modified before
+     * the reference <code>File</code>
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
+     */
+     public static boolean isFileOlder(File file, File reference) {
+        if (reference == null) {
+            throw new IllegalArgumentException("No specified reference file");
+        }
+        if (!reference.exists()) {
+            throw new IllegalArgumentException("The reference file '"
+                    + file + "' doesn't exist");
+        }
+        return isFileOlder(file, reference.lastModified());
+    }
+
+    /**
+     * Tests if the specified <code>File</code> is older than the specified
+     * <code>Date</code>.
+     *
+     * @param file  the <code>File</code> of which the modification date
+     * must be compared, must not be <code>null</code>
+     * @param date  the date reference, must not be <code>null</code>
+     * @return true if the <code>File</code> exists and has been modified
+     * before the given <code>Date</code>.
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     * @throws IllegalArgumentException if the date is <code>null</code>
+     */
+    public static boolean isFileOlder(File file, Date date) {
+        if (date == null) {
+            throw new IllegalArgumentException("No specified date");
+        }
+        return isFileOlder(file, date.getTime());
+    }
+
+    /**
+     * Tests if the specified <code>File</code> is older than the specified
+     * time reference.
+     *
+     * @param file  the <code>File</code> of which the modification date must
+     * be compared, must not be <code>null</code>
+     * @param timeMillis  the time reference measured in milliseconds since the
+     * epoch (00:00:00 GMT, January 1, 1970)
+     * @return true if the <code>File</code> exists and has been modified before
+     * the given time reference.
+     * @throws IllegalArgumentException if the file is <code>null</code>
+     */
+     public static boolean isFileOlder(File file, long timeMillis) {
+        if (file == null) {
+            throw new IllegalArgumentException("No specified file");
+        }
+        if (!file.exists()) {
+            return false;
+        }
+        return file.lastModified() < timeMillis;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Computes the checksum of a file using the CRC32 checksum routine.
+     * The value of the checksum is returned.
+     *
+     * @param file  the file to checksum, must not be <code>null</code>
+     * @return the checksum value
+     * @throws NullPointerException if the file or checksum is <code>null</code>
+     * @throws IllegalArgumentException if the file is a directory
+     * @throws IOException if an IO error occurs reading the file
+     * @since Commons IO 1.3
+     */
+    public static long checksumCRC32(File file) throws IOException {
+        CRC32 crc = new CRC32();
+        checksum(file, crc);
+        return crc.getValue();
+    }
+
+    /**
+     * Computes the checksum of a file using the specified checksum object.
+     * Multiple files may be checked using one <code>Checksum</code> instance
+     * if desired simply by reusing the same checksum object.
+     * For example:
+     * <pre>
+     *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
+     * </pre>
+     *
+     * @param file  the file to checksum, must not be <code>null</code>
+     * @param checksum  the checksum object to be used, must not be <code>null</code>
+     * @return the checksum specified, updated with the content of the file
+     * @throws NullPointerException if the file or checksum is <code>null</code>
+     * @throws IllegalArgumentException if the file is a directory
+     * @throws IOException if an IO error occurs reading the file
+     * @since Commons IO 1.3
+     */
+    public static Checksum checksum(File file, Checksum checksum) throws IOException {
+        if (file.isDirectory()) {
+            throw new IllegalArgumentException("Checksums can't be computed on directories");
+        }
+        InputStream in = null;
+        try {
+            in = new CheckedInputStream(new FileInputStream(file), checksum);
+            IOUtils.copy(in, new NullOutputStream());
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+        return checksum;
+    }
+
+    /**
+     * Moves a directory.
+     * <p>
+     * When the destination directory is on another file system, do a "copy and delete".
+     *
+     * @param srcDir the directory to be moved
+     * @param destDir the destination directory
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs moving the file
+     * @since Commons IO 1.4
+     */
+    public static void moveDirectory(File srcDir, File destDir) throws IOException {
+        if (srcDir == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (!srcDir.exists()) {
+            throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
+        }
+        if (!srcDir.isDirectory()) {
+            throw new IOException("Source '" + srcDir + "' is not a directory");
+        }
+        if (destDir.exists()) {
+            throw new IOException("Destination '" + destDir + "' already exists");
+        }
+        boolean rename = srcDir.renameTo(destDir);
+        if (!rename) {
+            copyDirectory( srcDir, destDir );
+            deleteDirectory( srcDir );
+            if (srcDir.exists()) {
+                throw new IOException("Failed to delete original directory '" + srcDir +
+                        "' after copy to '" + destDir + "'");
+            }
+        }
+    }
+
+    /**
+     * Moves a directory to another directory.
+     *
+     * @param src the file to be moved
+     * @param destDir the destination file
+     * @param createDestDir If <code>true</code> create the destination directory,
+     * otherwise if <code>false</code> throw an IOException
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs moving the file
+     * @since Commons IO 1.4
+     */
+    public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
+        if (src == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination directory must not be null");
+        }
+        if (!destDir.exists() && createDestDir) {
+            destDir.mkdirs();
+        }
+        if (!destDir.exists()) {
+            throw new FileNotFoundException("Destination directory '" + destDir +
+                    "' does not exist [createDestDir=" + createDestDir +"]");
+        }
+        if (!destDir.isDirectory()) {
+            throw new IOException("Destination '" + destDir + "' is not a directory");
+        }
+        moveDirectory(src, new File(destDir, src.getName()));
+
+    }
+
+    /**
+     * Moves a file.
+     * <p>
+     * When the destination file is on another file system, do a "copy and delete".
+     *
+     * @param srcFile the file to be moved
+     * @param destFile the destination file
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs moving the file
+     * @since Commons IO 1.4
+     */
+    public static void moveFile(File srcFile, File destFile) throws IOException {
+        if (srcFile == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destFile == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (!srcFile.exists()) {
+            throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
+        }
+        if (srcFile.isDirectory()) {
+            throw new IOException("Source '" + srcFile + "' is a directory");
+        }
+        if (destFile.exists()) {
+            throw new IOException("Destination '" + destFile + "' already exists");
+        }
+        if (destFile.isDirectory()) {
+            throw new IOException("Destination '" + destFile + "' is a directory");
+        }
+        boolean rename = srcFile.renameTo(destFile);
+        if (!rename) {
+            copyFile( srcFile, destFile );
+            if (!srcFile.delete()) {
+                FileUtils.deleteQuietly(destFile);
+                throw new IOException("Failed to delete original file '" + srcFile +
+                        "' after copy to '" + destFile + "'");
+            }
+        }
+    }
+
+    /**
+     * Moves a file to a directory.
+     *
+     * @param srcFile the file to be moved
+     * @param destDir the destination file
+     * @param createDestDir If <code>true</code> create the destination directory,
+     * otherwise if <code>false</code> throw an IOException
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs moving the file
+     * @since Commons IO 1.4
+     */
+    public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
+        if (srcFile == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination directory must not be null");
+        }
+        if (!destDir.exists() && createDestDir) {
+            destDir.mkdirs();
+        }
+        if (!destDir.exists()) {
+            throw new FileNotFoundException("Destination directory '" + destDir +
+                    "' does not exist [createDestDir=" + createDestDir +"]");
+        }
+        if (!destDir.isDirectory()) {
+            throw new IOException("Destination '" + destDir + "' is not a directory");
+        }
+        moveFile(srcFile, new File(destDir, srcFile.getName()));
+    }
+
+    /**
+     * Moves a file or directory to the destination directory.
+     * <p>
+     * When the destination is on another file system, do a "copy and delete".
+     *
+     * @param src the file or directory to be moved
+     * @param destDir the destination directory
+     * @param createDestDir If <code>true</code> create the destination directory,
+     * otherwise if <code>false</code> throw an IOException
+     * @throws NullPointerException if source or destination is <code>null</code>
+     * @throws IOException if source or destination is invalid
+     * @throws IOException if an IO error occurs moving the file
+     * @since Commons IO 1.4
+     */
+    public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
+        if (src == null) {
+            throw new NullPointerException("Source must not be null");
+        }
+        if (destDir == null) {
+            throw new NullPointerException("Destination must not be null");
+        }
+        if (!src.exists()) {
+            throw new FileNotFoundException("Source '" + src + "' does not exist");
+        }
+        if (src.isDirectory()) {
+            moveDirectoryToDirectory(src, destDir, createDestDir);
+        } else {
+            moveFileToDirectory(src, destDir, createDestDir);
+        }
+    }
+
+}
diff --git a/src/org/apache/commons/io/FilenameUtils.java b/src/org/apache/commons/io/FilenameUtils.java
index d8cc78b..4b03b54 100644
--- a/src/org/apache/commons/io/FilenameUtils.java
+++ b/src/org/apache/commons/io/FilenameUtils.java
@@ -1,1260 +1,1260 @@
-/*

- * 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.commons.io;

-

-import java.io.File;

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Iterator;

-import java.util.Stack;

-

-/**

- * General filename and filepath manipulation utilities.

- * <p>

- * When dealing with filenames you can hit problems when moving from a Windows

- * based development machine to a Unix based production machine.

- * This class aims to help avoid those problems.

- * <p>

- * <b>NOTE</b>: You may be able to avoid using this class entirely simply by

- * using JDK {@link java.io.File File} objects and the two argument constructor

- * {@link java.io.File#File(java.io.File, java.lang.String) File(File,String)}.

- * <p>

- * Most methods on this class are designed to work the same on both Unix and Windows.

- * Those that don't include 'System', 'Unix' or 'Windows' in their name.

- * <p>

- * Most methods recognise both separators (forward and back), and both

- * sets of prefixes. See the javadoc of each method for details.

- * <p>

- * This class defines six components within a filename

- * (example C:\dev\project\file.txt):

- * <ul>

- * <li>the prefix - C:\</li>

- * <li>the path - dev\project\</li>

- * <li>the full path - C:\dev\project\</li>

- * <li>the name - file.txt</li>

- * <li>the base name - file</li>

- * <li>the extension - txt</li>

- * </ul>

- * Note that this class works best if directory filenames end with a separator.

- * If you omit the last separator, it is impossible to determine if the filename

- * corresponds to a file or a directory. As a result, we have chosen to say

- * it corresponds to a file.

- * <p>

- * This class only supports Unix and Windows style names.

- * Prefixes are matched as follows:

- * <pre>

- * Windows:

- * a\b\c.txt           --> ""          --> relative

- * \a\b\c.txt          --> "\"         --> current drive absolute

- * C:a\b\c.txt         --> "C:"        --> drive relative

- * C:\a\b\c.txt        --> "C:\"       --> absolute

- * \\server\a\b\c.txt  --> "\\server\" --> UNC

- *

- * Unix:

- * a/b/c.txt           --> ""          --> relative

- * /a/b/c.txt          --> "/"         --> absolute

- * ~/a/b/c.txt         --> "~/"        --> current user

- * ~                   --> "~/"        --> current user (slash added)

- * ~user/a/b/c.txt     --> "~user/"    --> named user

- * ~user               --> "~user/"    --> named user (slash added)

- * </pre>

- * Both prefix styles are matched always, irrespective of the machine that you are

- * currently running on.

- * <p>

- * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.

- *

- * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>

- * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>

- * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>

- * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>

- * @author Matthew Hawthorne

- * @author Martin Cooper

- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>

- * @author Stephen Colebourne

- * @version $Id: FilenameUtils.java 609870 2008-01-08 04:46:26Z niallp $

- * @since Commons IO 1.1

- */

-public class FilenameUtils {

-

-    /**

-     * The extension separator character.

-     * @since Commons IO 1.4

-     */

-    public static final char EXTENSION_SEPARATOR = '.';

-

-    /**

-     * The extension separator String.

-     * @since Commons IO 1.4

-     */

-    public static final String EXTENSION_SEPARATOR_STR = (new Character(EXTENSION_SEPARATOR)).toString();

-

-    /**

-     * The Unix separator character.

-     */

-    private static final char UNIX_SEPARATOR = '/';

-

-    /**

-     * The Windows separator character.

-     */

-    private static final char WINDOWS_SEPARATOR = '\\';

-

-    /**

-     * The system separator character.

-     */

-    private static final char SYSTEM_SEPARATOR = File.separatorChar;

-

-    /**

-     * The separator character that is the opposite of the system separator.

-     */

-    private static final char OTHER_SEPARATOR;

-    static {

-        if (isSystemWindows()) {

-            OTHER_SEPARATOR = UNIX_SEPARATOR;

-        } else {

-            OTHER_SEPARATOR = WINDOWS_SEPARATOR;

-        }

-    }

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public FilenameUtils() {

-        super();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Determines if Windows file system is in use.

-     *

-     * @return true if the system is Windows

-     */

-    static boolean isSystemWindows() {

-        return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks if the character is a separator.

-     *

-     * @param ch  the character to check

-     * @return true if it is a separator character

-     */

-    private static boolean isSeparator(char ch) {

-        return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Normalizes a path, removing double and single dot path steps.

-     * <p>

-     * This method normalizes a path to a standard format.

-     * The input may contain separators in either Unix or Windows format.

-     * The output will contain separators in the format of the system.

-     * <p>

-     * A trailing slash will be retained.

-     * A double slash will be merged to a single slash (but UNC names are handled).

-     * A single dot path segment will be removed.

-     * A double dot will cause that path segment and the one before to be removed.

-     * If the double dot has no parent path segment to work with, <code>null</code>

-     * is returned.

-     * <p>

-     * The output will be the same on both Unix and Windows except

-     * for the separator character.

-     * <pre>

-     * /foo//               -->   /foo/

-     * /foo/./              -->   /foo/

-     * /foo/../bar          -->   /bar

-     * /foo/../bar/         -->   /bar/

-     * /foo/../bar/../baz   -->   /baz

-     * //foo//./bar         -->   /foo/bar

-     * /../                 -->   null

-     * ../foo               -->   null

-     * foo/bar/..           -->   foo/

-     * foo/../../bar        -->   null

-     * foo/../bar           -->   bar

-     * //server/foo/../bar  -->   //server/bar

-     * //server/../bar      -->   null

-     * C:\foo\..\bar        -->   C:\bar

-     * C:\..\bar            -->   null

-     * ~/foo/../bar/        -->   ~/bar/

-     * ~/../bar             -->   null

-     * </pre>

-     * (Note the file separator returned will be correct for Windows/Unix)

-     *

-     * @param filename  the filename to normalize, null returns null

-     * @return the normalized filename, or null if invalid

-     */

-    public static String normalize(String filename) {

-        return doNormalize(filename, true);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Normalizes a path, removing double and single dot path steps,

-     * and removing any final directory separator.

-     * <p>

-     * This method normalizes a path to a standard format.

-     * The input may contain separators in either Unix or Windows format.

-     * The output will contain separators in the format of the system.

-     * <p>

-     * A trailing slash will be removed.

-     * A double slash will be merged to a single slash (but UNC names are handled).

-     * A single dot path segment will be removed.

-     * A double dot will cause that path segment and the one before to be removed.

-     * If the double dot has no parent path segment to work with, <code>null</code>

-     * is returned.

-     * <p>

-     * The output will be the same on both Unix and Windows except

-     * for the separator character.

-     * <pre>

-     * /foo//               -->   /foo

-     * /foo/./              -->   /foo

-     * /foo/../bar          -->   /bar

-     * /foo/../bar/         -->   /bar

-     * /foo/../bar/../baz   -->   /baz

-     * //foo//./bar         -->   /foo/bar

-     * /../                 -->   null

-     * ../foo               -->   null

-     * foo/bar/..           -->   foo

-     * foo/../../bar        -->   null

-     * foo/../bar           -->   bar

-     * //server/foo/../bar  -->   //server/bar

-     * //server/../bar      -->   null

-     * C:\foo\..\bar        -->   C:\bar

-     * C:\..\bar            -->   null

-     * ~/foo/../bar/        -->   ~/bar

-     * ~/../bar             -->   null

-     * </pre>

-     * (Note the file separator returned will be correct for Windows/Unix)

-     *

-     * @param filename  the filename to normalize, null returns null

-     * @return the normalized filename, or null if invalid

-     */

-    public static String normalizeNoEndSeparator(String filename) {

-        return doNormalize(filename, false);

-    }

-

-    /**

-     * Internal method to perform the normalization.

-     *

-     * @param filename  the filename

-     * @param keepSeparator  true to keep the final separator

-     * @return the normalized filename

-     */

-    private static String doNormalize(String filename, boolean keepSeparator) {

-        if (filename == null) {

-            return null;

-        }

-        int size = filename.length();

-        if (size == 0) {

-            return filename;

-        }

-        int prefix = getPrefixLength(filename);

-        if (prefix < 0) {

-            return null;

-        }

-

-        char[] array = new char[size + 2];  // +1 for possible extra slash, +2 for arraycopy

-        filename.getChars(0, filename.length(), array, 0);

-

-        // fix separators throughout

-        for (int i = 0; i < array.length; i++) {

-            if (array[i] == OTHER_SEPARATOR) {

-                array[i] = SYSTEM_SEPARATOR;

-            }

-        }

-

-        // add extra separator on the end to simplify code below

-        boolean lastIsDirectory = true;

-        if (array[size - 1] != SYSTEM_SEPARATOR) {

-            array[size++] = SYSTEM_SEPARATOR;

-            lastIsDirectory = false;

-        }

-

-        // adjoining slashes

-        for (int i = prefix + 1; i < size; i++) {

-            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) {

-                System.arraycopy(array, i, array, i - 1, size - i);

-                size--;

-                i--;

-            }

-        }

-

-        // dot slash

-        for (int i = prefix + 1; i < size; i++) {

-            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' &&

-                    (i == prefix + 1 || array[i - 2] == SYSTEM_SEPARATOR)) {

-                if (i == size - 1) {

-                    lastIsDirectory = true;

-                }

-                System.arraycopy(array, i + 1, array, i - 1, size - i);

-                size -=2;

-                i--;

-            }

-        }

-

-        // double dot slash

-        outer:

-        for (int i = prefix + 2; i < size; i++) {

-            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && array[i - 2] == '.' &&

-                    (i == prefix + 2 || array[i - 3] == SYSTEM_SEPARATOR)) {

-                if (i == prefix + 2) {

-                    return null;

-                }

-                if (i == size - 1) {

-                    lastIsDirectory = true;

-                }

-                int j;

-                for (j = i - 4 ; j >= prefix; j--) {

-                    if (array[j] == SYSTEM_SEPARATOR) {

-                        // remove b/../ from a/b/../c

-                        System.arraycopy(array, i + 1, array, j + 1, size - i);

-                        size -= (i - j);

-                        i = j + 1;

-                        continue outer;

-                    }

-                }

-                // remove a/../ from a/../c

-                System.arraycopy(array, i + 1, array, prefix, size - i);

-                size -= (i + 1 - prefix);

-                i = prefix + 1;

-            }

-        }

-

-        if (size <= 0) {  // should never be less than 0

-            return "";

-        }

-        if (size <= prefix) {  // should never be less than prefix

-            return new String(array, 0, size);

-        }

-        if (lastIsDirectory && keepSeparator) {

-            return new String(array, 0, size);  // keep trailing separator

-        }

-        return new String(array, 0, size - 1);  // lose trailing separator

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Concatenates a filename to a base path using normal command line style rules.

-     * <p>

-     * The effect is equivalent to resultant directory after changing

-     * directory to the first argument, followed by changing directory to

-     * the second argument.

-     * <p>

-     * The first argument is the base path, the second is the path to concatenate.

-     * The returned path is always normalized via {@link #normalize(String)},

-     * thus <code>..</code> is handled.

-     * <p>

-     * If <code>pathToAdd</code> is absolute (has an absolute prefix), then

-     * it will be normalized and returned.

-     * Otherwise, the paths will be joined, normalized and returned.

-     * <p>

-     * The output will be the same on both Unix and Windows except

-     * for the separator character.

-     * <pre>

-     * /foo/ + bar          -->   /foo/bar

-     * /foo + bar           -->   /foo/bar

-     * /foo + /bar          -->   /bar

-     * /foo + C:/bar        -->   C:/bar

-     * /foo + C:bar         -->   C:bar (*)

-     * /foo/a/ + ../bar     -->   foo/bar

-     * /foo/ + ../../bar    -->   null

-     * /foo/ + /bar         -->   /bar

-     * /foo/.. + /bar       -->   /bar

-     * /foo + bar/c.txt     -->   /foo/bar/c.txt

-     * /foo/c.txt + bar     -->   /foo/c.txt/bar (!)

-     * </pre>

-     * (*) Note that the Windows relative drive prefix is unreliable when

-     * used with this method.

-     * (!) Note that the first parameter must be a path. If it ends with a name, then

-     * the name will be built into the concatenated path. If this might be a problem,

-     * use {@link #getFullPath(String)} on the base path argument.

-     *

-     * @param basePath  the base path to attach to, always treated as a path

-     * @param fullFilenameToAdd  the filename (or path) to attach to the base

-     * @return the concatenated path, or null if invalid

-     */

-    public static String concat(String basePath, String fullFilenameToAdd) {

-        int prefix = getPrefixLength(fullFilenameToAdd);

-        if (prefix < 0) {

-            return null;

-        }

-        if (prefix > 0) {

-            return normalize(fullFilenameToAdd);

-        }

-        if (basePath == null) {

-            return null;

-        }

-        int len = basePath.length();

-        if (len == 0) {

-            return normalize(fullFilenameToAdd);

-        }

-        char ch = basePath.charAt(len - 1);

-        if (isSeparator(ch)) {

-            return normalize(basePath + fullFilenameToAdd);

-        } else {

-            return normalize(basePath + '/' + fullFilenameToAdd);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Converts all separators to the Unix separator of forward slash.

-     *

-     * @param path  the path to be changed, null ignored

-     * @return the updated path

-     */

-    public static String separatorsToUnix(String path) {

-        if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {

-            return path;

-        }

-        return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);

-    }

-

-    /**

-     * Converts all separators to the Windows separator of backslash.

-     *

-     * @param path  the path to be changed, null ignored

-     * @return the updated path

-     */

-    public static String separatorsToWindows(String path) {

-        if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {

-            return path;

-        }

-        return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);

-    }

-

-    /**

-     * Converts all separators to the system separator.

-     *

-     * @param path  the path to be changed, null ignored

-     * @return the updated path

-     */

-    public static String separatorsToSystem(String path) {

-        if (path == null) {

-            return null;

-        }

-        if (isSystemWindows()) {

-            return separatorsToWindows(path);

-        } else {

-            return separatorsToUnix(path);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * <p>

-     * The prefix length includes the first slash in the full filename

-     * if applicable. Thus, it is possible that the length returned is greater

-     * than the length of the input string.

-     * <pre>

-     * Windows:

-     * a\b\c.txt           --> ""          --> relative

-     * \a\b\c.txt          --> "\"         --> current drive absolute

-     * C:a\b\c.txt         --> "C:"        --> drive relative

-     * C:\a\b\c.txt        --> "C:\"       --> absolute

-     * \\server\a\b\c.txt  --> "\\server\" --> UNC

-     *

-     * Unix:

-     * a/b/c.txt           --> ""          --> relative

-     * /a/b/c.txt          --> "/"         --> absolute

-     * ~/a/b/c.txt         --> "~/"        --> current user

-     * ~                   --> "~/"        --> current user (slash added)

-     * ~user/a/b/c.txt     --> "~user/"    --> named user

-     * ~user               --> "~user/"    --> named user (slash added)

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     * ie. both Unix and Windows prefixes are matched regardless.

-     *

-     * @param filename  the filename to find the prefix in, null returns -1

-     * @return the length of the prefix, -1 if invalid or null

-     */

-    public static int getPrefixLength(String filename) {

-        if (filename == null) {

-            return -1;

-        }

-        int len = filename.length();

-        if (len == 0) {

-            return 0;

-        }

-        char ch0 = filename.charAt(0);

-        if (ch0 == ':') {

-            return -1;

-        }

-        if (len == 1) {

-            if (ch0 == '~') {

-                return 2;  // return a length greater than the input

-            }

-            return (isSeparator(ch0) ? 1 : 0);

-        } else {

-            if (ch0 == '~') {

-                int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);

-                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);

-                if (posUnix == -1 && posWin == -1) {

-                    return len + 1;  // return a length greater than the input

-                }

-                posUnix = (posUnix == -1 ? posWin : posUnix);

-                posWin = (posWin == -1 ? posUnix : posWin);

-                return Math.min(posUnix, posWin) + 1;

-            }

-            char ch1 = filename.charAt(1);

-            if (ch1 == ':') {

-                ch0 = Character.toUpperCase(ch0);

-                if (ch0 >= 'A' && ch0 <= 'Z') {

-                    if (len == 2 || isSeparator(filename.charAt(2)) == false) {

-                        return 2;

-                    }

-                    return 3;

-                }

-                return -1;

-

-            } else if (isSeparator(ch0) && isSeparator(ch1)) {

-                int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);

-                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);

-                if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) {

-                    return -1;

-                }

-                posUnix = (posUnix == -1 ? posWin : posUnix);

-                posWin = (posWin == -1 ? posUnix : posWin);

-                return Math.min(posUnix, posWin) + 1;

-            } else {

-                return (isSeparator(ch0) ? 1 : 0);

-            }

-        }

-    }

-

-    /**

-     * Returns the index of the last directory separator character.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The position of the last forward or backslash is returned.

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to find the last path separator in, null returns -1

-     * @return the index of the last separator character, or -1 if there

-     * is no such character

-     */

-    public static int indexOfLastSeparator(String filename) {

-        if (filename == null) {

-            return -1;

-        }

-        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);

-        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);

-        return Math.max(lastUnixPos, lastWindowsPos);

-    }

-

-    /**

-     * Returns the index of the last extension separator character, which is a dot.

-     * <p>

-     * This method also checks that there is no directory separator after the last dot.

-     * To do this it uses {@link #indexOfLastSeparator(String)} which will

-     * handle a file in either Unix or Windows format.

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to find the last path separator in, null returns -1

-     * @return the index of the last separator character, or -1 if there

-     * is no such character

-     */

-    public static int indexOfExtension(String filename) {

-        if (filename == null) {

-            return -1;

-        }

-        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);

-        int lastSeparator = indexOfLastSeparator(filename);

-        return (lastSeparator > extensionPos ? -1 : extensionPos);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Gets the prefix from a full filename, such as <code>C:/</code>

-     * or <code>~/</code>.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The prefix includes the first slash in the full filename where applicable.

-     * <pre>

-     * Windows:

-     * a\b\c.txt           --> ""          --> relative

-     * \a\b\c.txt          --> "\"         --> current drive absolute

-     * C:a\b\c.txt         --> "C:"        --> drive relative

-     * C:\a\b\c.txt        --> "C:\"       --> absolute

-     * \\server\a\b\c.txt  --> "\\server\" --> UNC

-     *

-     * Unix:

-     * a/b/c.txt           --> ""          --> relative

-     * /a/b/c.txt          --> "/"         --> absolute

-     * ~/a/b/c.txt         --> "~/"        --> current user

-     * ~                   --> "~/"        --> current user (slash added)

-     * ~user/a/b/c.txt     --> "~user/"    --> named user

-     * ~user               --> "~user/"    --> named user (slash added)

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     * ie. both Unix and Windows prefixes are matched regardless.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the prefix of the file, null if invalid

-     */

-    public static String getPrefix(String filename) {

-        if (filename == null) {

-            return null;

-        }

-        int len = getPrefixLength(filename);

-        if (len < 0) {

-            return null;

-        }

-        if (len > filename.length()) {

-            return filename + UNIX_SEPARATOR;  // we know this only happens for unix

-        }

-        return filename.substring(0, len);

-    }

-

-    /**

-     * Gets the path from a full filename, which excludes the prefix.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The method is entirely text based, and returns the text before and

-     * including the last forward or backslash.

-     * <pre>

-     * C:\a\b\c.txt --> a\b\

-     * ~/a/b/c.txt  --> a/b/

-     * a.txt        --> ""

-     * a/b/c        --> a/b/

-     * a/b/c/       --> a/b/c/

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     * <p>

-     * This method drops the prefix from the result.

-     * See {@link #getFullPath(String)} for the method that retains the prefix.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the path of the file, an empty string if none exists, null if invalid

-     */

-    public static String getPath(String filename) {

-        return doGetPath(filename, 1);

-    }

-

-    /**

-     * Gets the path from a full filename, which excludes the prefix, and

-     * also excluding the final directory separator.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The method is entirely text based, and returns the text before the

-     * last forward or backslash.

-     * <pre>

-     * C:\a\b\c.txt --> a\b

-     * ~/a/b/c.txt  --> a/b

-     * a.txt        --> ""

-     * a/b/c        --> a/b

-     * a/b/c/       --> a/b/c

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     * <p>

-     * This method drops the prefix from the result.

-     * See {@link #getFullPathNoEndSeparator(String)} for the method that retains the prefix.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the path of the file, an empty string if none exists, null if invalid

-     */

-    public static String getPathNoEndSeparator(String filename) {

-        return doGetPath(filename, 0);

-    }

-

-    /**

-     * Does the work of getting the path.

-     *

-     * @param filename  the filename

-     * @param separatorAdd  0 to omit the end separator, 1 to return it

-     * @return the path

-     */

-    private static String doGetPath(String filename, int separatorAdd) {

-        if (filename == null) {

-            return null;

-        }

-        int prefix = getPrefixLength(filename);

-        if (prefix < 0) {

-            return null;

-        }

-        int index = indexOfLastSeparator(filename);

-        if (prefix >= filename.length() || index < 0) {

-            return "";

-        }

-        return filename.substring(prefix, index + separatorAdd);

-    }

-

-    /**

-     * Gets the full path from a full filename, which is the prefix + path.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The method is entirely text based, and returns the text before and

-     * including the last forward or backslash.

-     * <pre>

-     * C:\a\b\c.txt --> C:\a\b\

-     * ~/a/b/c.txt  --> ~/a/b/

-     * a.txt        --> ""

-     * a/b/c        --> a/b/

-     * a/b/c/       --> a/b/c/

-     * C:           --> C:

-     * C:\          --> C:\

-     * ~            --> ~/

-     * ~/           --> ~/

-     * ~user        --> ~user/

-     * ~user/       --> ~user/

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the path of the file, an empty string if none exists, null if invalid

-     */

-    public static String getFullPath(String filename) {

-        return doGetFullPath(filename, true);

-    }

-

-    /**

-     * Gets the full path from a full filename, which is the prefix + path,

-     * and also excluding the final directory separator.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The method is entirely text based, and returns the text before the

-     * last forward or backslash.

-     * <pre>

-     * C:\a\b\c.txt --> C:\a\b

-     * ~/a/b/c.txt  --> ~/a/b

-     * a.txt        --> ""

-     * a/b/c        --> a/b

-     * a/b/c/       --> a/b/c

-     * C:           --> C:

-     * C:\          --> C:\

-     * ~            --> ~

-     * ~/           --> ~

-     * ~user        --> ~user

-     * ~user/       --> ~user

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the path of the file, an empty string if none exists, null if invalid

-     */

-    public static String getFullPathNoEndSeparator(String filename) {

-        return doGetFullPath(filename, false);

-    }

-

-    /**

-     * Does the work of getting the path.

-     *

-     * @param filename  the filename

-     * @param includeSeparator  true to include the end separator

-     * @return the path

-     */

-    private static String doGetFullPath(String filename, boolean includeSeparator) {

-        if (filename == null) {

-            return null;

-        }

-        int prefix = getPrefixLength(filename);

-        if (prefix < 0) {

-            return null;

-        }

-        if (prefix >= filename.length()) {

-            if (includeSeparator) {

-                return getPrefix(filename);  // add end slash if necessary

-            } else {

-                return filename;

-            }

-        }

-        int index = indexOfLastSeparator(filename);

-        if (index < 0) {

-            return filename.substring(0, prefix);

-        }

-        int end = index + (includeSeparator ?  1 : 0);

-        return filename.substring(0, end);

-    }

-

-    /**

-     * Gets the name minus the path from a full filename.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The text after the last forward or backslash is returned.

-     * <pre>

-     * a/b/c.txt --> c.txt

-     * a.txt     --> a.txt

-     * a/b/c     --> c

-     * a/b/c/    --> ""

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the name of the file without the path, or an empty string if none exists

-     */

-    public static String getName(String filename) {

-        if (filename == null) {

-            return null;

-        }

-        int index = indexOfLastSeparator(filename);

-        return filename.substring(index + 1);

-    }

-

-    /**

-     * Gets the base name, minus the full path and extension, from a full filename.

-     * <p>

-     * This method will handle a file in either Unix or Windows format.

-     * The text after the last forward or backslash and before the last dot is returned.

-     * <pre>

-     * a/b/c.txt --> c

-     * a.txt     --> a

-     * a/b/c     --> c

-     * a/b/c/    --> ""

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the name of the file without the path, or an empty string if none exists

-     */

-    public static String getBaseName(String filename) {

-        return removeExtension(getName(filename));

-    }

-

-    /**

-     * Gets the extension of a filename.

-     * <p>

-     * This method returns the textual part of the filename after the last dot.

-     * There must be no directory separator after the dot.

-     * <pre>

-     * foo.txt      --> "txt"

-     * a/b/c.jpg    --> "jpg"

-     * a/b.txt/c    --> ""

-     * a/b/c        --> ""

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename the filename to retrieve the extension of.

-     * @return the extension of the file or an empty string if none exists.

-     */

-    public static String getExtension(String filename) {

-        if (filename == null) {

-            return null;

-        }

-        int index = indexOfExtension(filename);

-        if (index == -1) {

-            return "";

-        } else {

-            return filename.substring(index + 1);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Removes the extension from a filename.

-     * <p>

-     * This method returns the textual part of the filename before the last dot.

-     * There must be no directory separator after the dot.

-     * <pre>

-     * foo.txt    --> foo

-     * a\b\c.jpg  --> a\b\c

-     * a\b\c      --> a\b\c

-     * a.b\c      --> a.b\c

-     * </pre>

-     * <p>

-     * The output will be the same irrespective of the machine that the code is running on.

-     *

-     * @param filename  the filename to query, null returns null

-     * @return the filename minus the extension

-     */

-    public static String removeExtension(String filename) {

-        if (filename == null) {

-            return null;

-        }

-        int index = indexOfExtension(filename);

-        if (index == -1) {

-            return filename;

-        } else {

-            return filename.substring(0, index);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks whether two filenames are equal exactly.

-     * <p>

-     * No processing is performed on the filenames other than comparison,

-     * thus this is merely a null-safe case-sensitive equals.

-     *

-     * @param filename1  the first filename to query, may be null

-     * @param filename2  the second filename to query, may be null

-     * @return true if the filenames are equal, null equals null

-     * @see IOCase#SENSITIVE

-     */

-    public static boolean equals(String filename1, String filename2) {

-        return equals(filename1, filename2, false, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Checks whether two filenames are equal using the case rules of the system.

-     * <p>

-     * No processing is performed on the filenames other than comparison.

-     * The check is case-sensitive on Unix and case-insensitive on Windows.

-     *

-     * @param filename1  the first filename to query, may be null

-     * @param filename2  the second filename to query, may be null

-     * @return true if the filenames are equal, null equals null

-     * @see IOCase#SYSTEM

-     */

-    public static boolean equalsOnSystem(String filename1, String filename2) {

-        return equals(filename1, filename2, false, IOCase.SYSTEM);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks whether two filenames are equal after both have been normalized.

-     * <p>

-     * Both filenames are first passed to {@link #normalize(String)}.

-     * The check is then performed in a case-sensitive manner.

-     *

-     * @param filename1  the first filename to query, may be null

-     * @param filename2  the second filename to query, may be null

-     * @return true if the filenames are equal, null equals null

-     * @see IOCase#SENSITIVE

-     */

-    public static boolean equalsNormalized(String filename1, String filename2) {

-        return equals(filename1, filename2, true, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Checks whether two filenames are equal after both have been normalized

-     * and using the case rules of the system.

-     * <p>

-     * Both filenames are first passed to {@link #normalize(String)}.

-     * The check is then performed case-sensitive on Unix and

-     * case-insensitive on Windows.

-     *

-     * @param filename1  the first filename to query, may be null

-     * @param filename2  the second filename to query, may be null

-     * @return true if the filenames are equal, null equals null

-     * @see IOCase#SYSTEM

-     */

-    public static boolean equalsNormalizedOnSystem(String filename1, String filename2) {

-        return equals(filename1, filename2, true, IOCase.SYSTEM);

-    }

-

-    /**

-     * Checks whether two filenames are equal, optionally normalizing and providing

-     * control over the case-sensitivity.

-     *

-     * @param filename1  the first filename to query, may be null

-     * @param filename2  the second filename to query, may be null

-     * @param normalized  whether to normalize the filenames

-     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive

-     * @return true if the filenames are equal, null equals null

-     * @since Commons IO 1.3

-     */

-    public static boolean equals(

-            String filename1, String filename2,

-            boolean normalized, IOCase caseSensitivity) {

-

-        if (filename1 == null || filename2 == null) {

-            return filename1 == filename2;

-        }

-        if (normalized) {

-            filename1 = normalize(filename1);

-            filename2 = normalize(filename2);

-            if (filename1 == null || filename2 == null) {

-                throw new NullPointerException(

-                    "Error normalizing one or both of the file names");

-            }

-        }

-        if (caseSensitivity == null) {

-            caseSensitivity = IOCase.SENSITIVE;

-        }

-        return caseSensitivity.checkEquals(filename1, filename2);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks whether the extension of the filename is that specified.

-     * <p>

-     * This method obtains the extension as the textual part of the filename

-     * after the last dot. There must be no directory separator after the dot.

-     * The extension check is case-sensitive on all platforms.

-     *

-     * @param filename  the filename to query, null returns false

-     * @param extension  the extension to check for, null or empty checks for no extension

-     * @return true if the filename has the specified extension

-     */

-    public static boolean isExtension(String filename, String extension) {

-        if (filename == null) {

-            return false;

-        }

-        if (extension == null || extension.length() == 0) {

-            return (indexOfExtension(filename) == -1);

-        }

-        String fileExt = getExtension(filename);

-        return fileExt.equals(extension);

-    }

-

-    /**

-     * Checks whether the extension of the filename is one of those specified.

-     * <p>

-     * This method obtains the extension as the textual part of the filename

-     * after the last dot. There must be no directory separator after the dot.

-     * The extension check is case-sensitive on all platforms.

-     *

-     * @param filename  the filename to query, null returns false

-     * @param extensions  the extensions to check for, null checks for no extension

-     * @return true if the filename is one of the extensions

-     */

-    public static boolean isExtension(String filename, String[] extensions) {

-        if (filename == null) {

-            return false;

-        }

-        if (extensions == null || extensions.length == 0) {

-            return (indexOfExtension(filename) == -1);

-        }

-        String fileExt = getExtension(filename);

-        for (int i = 0; i < extensions.length; i++) {

-            if (fileExt.equals(extensions[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Checks whether the extension of the filename is one of those specified.

-     * <p>

-     * This method obtains the extension as the textual part of the filename

-     * after the last dot. There must be no directory separator after the dot.

-     * The extension check is case-sensitive on all platforms.

-     *

-     * @param filename  the filename to query, null returns false

-     * @param extensions  the extensions to check for, null checks for no extension

-     * @return true if the filename is one of the extensions

-     */

-    public static boolean isExtension(String filename, Collection<String> extensions) {

-        if (filename == null) {

-            return false;

-        }

-        if (extensions == null || extensions.isEmpty()) {

-            return (indexOfExtension(filename) == -1);

-        }

-        String fileExt = getExtension(filename);

-        for (Iterator<String> it = extensions.iterator(); it.hasNext();) {

-            if (fileExt.equals(it.next())) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks a filename to see if it matches the specified wildcard matcher,

-     * always testing case-sensitive.

-     * <p>

-     * The wildcard matcher uses the characters '?' and '*' to represent a

-     * single or multiple wildcard characters.

-     * This is the same as often found on Dos/Unix command lines.

-     * The check is case-sensitive always.

-     * <pre>

-     * wildcardMatch("c.txt", "*.txt")      --> true

-     * wildcardMatch("c.txt", "*.jpg")      --> false

-     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true

-     * wildcardMatch("c.txt", "*.???")      --> true

-     * wildcardMatch("c.txt", "*.????")     --> false

-     * </pre>

-     *

-     * @param filename  the filename to match on

-     * @param wildcardMatcher  the wildcard string to match against

-     * @return true if the filename matches the wilcard string

-     * @see IOCase#SENSITIVE

-     */

-    public static boolean wildcardMatch(String filename, String wildcardMatcher) {

-        return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Checks a filename to see if it matches the specified wildcard matcher

-     * using the case rules of the system.

-     * <p>

-     * The wildcard matcher uses the characters '?' and '*' to represent a

-     * single or multiple wildcard characters.

-     * This is the same as often found on Dos/Unix command lines.

-     * The check is case-sensitive on Unix and case-insensitive on Windows.

-     * <pre>

-     * wildcardMatch("c.txt", "*.txt")      --> true

-     * wildcardMatch("c.txt", "*.jpg")      --> false

-     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true

-     * wildcardMatch("c.txt", "*.???")      --> true

-     * wildcardMatch("c.txt", "*.????")     --> false

-     * </pre>

-     *

-     * @param filename  the filename to match on

-     * @param wildcardMatcher  the wildcard string to match against

-     * @return true if the filename matches the wilcard string

-     * @see IOCase#SYSTEM

-     */

-    public static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) {

-        return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);

-    }

-

-    /**

-     * Checks a filename to see if it matches the specified wildcard matcher

-     * allowing control over case-sensitivity.

-     * <p>

-     * The wildcard matcher uses the characters '?' and '*' to represent a

-     * single or multiple wildcard characters.

-     *

-     * @param filename  the filename to match on

-     * @param wildcardMatcher  the wildcard string to match against

-     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive

-     * @return true if the filename matches the wilcard string

-     * @since Commons IO 1.3

-     */

-    public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {

-        if (filename == null && wildcardMatcher == null) {

-            return true;

-        }

-        if (filename == null || wildcardMatcher == null) {

-            return false;

-        }

-        if (caseSensitivity == null) {

-            caseSensitivity = IOCase.SENSITIVE;

-        }

-        filename = caseSensitivity.convertCase(filename);

-        wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);

-        String[] wcs = splitOnTokens(wildcardMatcher);

-        boolean anyChars = false;

-        int textIdx = 0;

-        int wcsIdx = 0;

-        Stack<int[]> backtrack = new Stack<int[]>();

-

-        // loop around a backtrack stack, to handle complex * matching

-        do {

-            if (backtrack.size() > 0) {

-                int[] array = backtrack.pop();

-                wcsIdx = array[0];

-                textIdx = array[1];

-                anyChars = true;

-            }

-

-            // loop whilst tokens and text left to process

-            while (wcsIdx < wcs.length) {

-

-                if (wcs[wcsIdx].equals("?")) {

-                    // ? so move to next text char

-                    textIdx++;

-                    anyChars = false;

-

-                } else if (wcs[wcsIdx].equals("*")) {

-                    // set any chars status

-                    anyChars = true;

-                    if (wcsIdx == wcs.length - 1) {

-                        textIdx = filename.length();

-                    }

-

-                } else {

-                    // matching text token

-                    if (anyChars) {

-                        // any chars then try to locate text token

-                        textIdx = filename.indexOf(wcs[wcsIdx], textIdx);

-                        if (textIdx == -1) {

-                            // token not found

-                            break;

-                        }

-                        int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);

-                        if (repeat >= 0) {

-                            backtrack.push(new int[] {wcsIdx, repeat});

-                        }

-                    } else {

-                        // matching from current position

-                        if (!filename.startsWith(wcs[wcsIdx], textIdx)) {

-                            // couldnt match token

-                            break;

-                        }

-                    }

-

-                    // matched text token, move text index to end of matched token

-                    textIdx += wcs[wcsIdx].length();

-                    anyChars = false;

-                }

-

-                wcsIdx++;

-            }

-

-            // full match

-            if (wcsIdx == wcs.length && textIdx == filename.length()) {

-                return true;

-            }

-

-        } while (backtrack.size() > 0);

-

-        return false;

-    }

-

-    /**

-     * Splits a string into a number of tokens.

-     *

-     * @param text  the text to split

-     * @return the tokens, never null

-     */

-    static String[] splitOnTokens(String text) {

-        // used by wildcardMatch

-        // package level so a unit test may run on this

-

-        if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {

-            return new String[] { text };

-        }

-

-        char[] array = text.toCharArray();

-        ArrayList<String> list = new ArrayList<String>();

-        StringBuffer buffer = new StringBuffer();

-        for (int i = 0; i < array.length; i++) {

-            if (array[i] == '?' || array[i] == '*') {

-                if (buffer.length() != 0) {

-                    list.add(buffer.toString());

-                    buffer.setLength(0);

-                }

-                if (array[i] == '?') {

-                    list.add("?");

-                } else if (list.size() == 0 ||

-                        (i > 0 && list.get(list.size() - 1).equals("*") == false)) {

-                    list.add("*");

-                }

-            } else {

-                buffer.append(array[i]);

-            }

-        }

-        if (buffer.length() != 0) {

-            list.add(buffer.toString());

-        }

-

-        return list.toArray( new String[ list.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.commons.io;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * General filename and filepath manipulation utilities.
+ * <p>
+ * When dealing with filenames you can hit problems when moving from a Windows
+ * based development machine to a Unix based production machine.
+ * This class aims to help avoid those problems.
+ * <p>
+ * <b>NOTE</b>: You may be able to avoid using this class entirely simply by
+ * using JDK {@link java.io.File File} objects and the two argument constructor
+ * {@link java.io.File#File(java.io.File, java.lang.String) File(File,String)}.
+ * <p>
+ * Most methods on this class are designed to work the same on both Unix and Windows.
+ * Those that don't include 'System', 'Unix' or 'Windows' in their name.
+ * <p>
+ * Most methods recognise both separators (forward and back), and both
+ * sets of prefixes. See the javadoc of each method for details.
+ * <p>
+ * This class defines six components within a filename
+ * (example C:\dev\project\file.txt):
+ * <ul>
+ * <li>the prefix - C:\</li>
+ * <li>the path - dev\project\</li>
+ * <li>the full path - C:\dev\project\</li>
+ * <li>the name - file.txt</li>
+ * <li>the base name - file</li>
+ * <li>the extension - txt</li>
+ * </ul>
+ * Note that this class works best if directory filenames end with a separator.
+ * If you omit the last separator, it is impossible to determine if the filename
+ * corresponds to a file or a directory. As a result, we have chosen to say
+ * it corresponds to a file.
+ * <p>
+ * This class only supports Unix and Windows style names.
+ * Prefixes are matched as follows:
+ * <pre>
+ * Windows:
+ * a\b\c.txt           --> ""          --> relative
+ * \a\b\c.txt          --> "\"         --> current drive absolute
+ * C:a\b\c.txt         --> "C:"        --> drive relative
+ * C:\a\b\c.txt        --> "C:\"       --> absolute
+ * \\server\a\b\c.txt  --> "\\server\" --> UNC
+ *
+ * Unix:
+ * a/b/c.txt           --> ""          --> relative
+ * /a/b/c.txt          --> "/"         --> absolute
+ * ~/a/b/c.txt         --> "~/"        --> current user
+ * ~                   --> "~/"        --> current user (slash added)
+ * ~user/a/b/c.txt     --> "~user/"    --> named user
+ * ~user               --> "~user/"    --> named user (slash added)
+ * </pre>
+ * Both prefix styles are matched always, irrespective of the machine that you are
+ * currently running on.
+ * <p>
+ * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.
+ *
+ * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
+ * @author Matthew Hawthorne
+ * @author Martin Cooper
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @author Stephen Colebourne
+ * @version $Id: FilenameUtils.java 609870 2008-01-08 04:46:26Z niallp $
+ * @since Commons IO 1.1
+ */
+public class FilenameUtils {
+
+    /**
+     * The extension separator character.
+     * @since Commons IO 1.4
+     */
+    public static final char EXTENSION_SEPARATOR = '.';
+
+    /**
+     * The extension separator String.
+     * @since Commons IO 1.4
+     */
+    public static final String EXTENSION_SEPARATOR_STR = (new Character(EXTENSION_SEPARATOR)).toString();
+
+    /**
+     * The Unix separator character.
+     */
+    private static final char UNIX_SEPARATOR = '/';
+
+    /**
+     * The Windows separator character.
+     */
+    private static final char WINDOWS_SEPARATOR = '\\';
+
+    /**
+     * The system separator character.
+     */
+    private static final char SYSTEM_SEPARATOR = File.separatorChar;
+
+    /**
+     * The separator character that is the opposite of the system separator.
+     */
+    private static final char OTHER_SEPARATOR;
+    static {
+        if (isSystemWindows()) {
+            OTHER_SEPARATOR = UNIX_SEPARATOR;
+        } else {
+            OTHER_SEPARATOR = WINDOWS_SEPARATOR;
+        }
+    }
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public FilenameUtils() {
+        super();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Determines if Windows file system is in use.
+     *
+     * @return true if the system is Windows
+     */
+    static boolean isSystemWindows() {
+        return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks if the character is a separator.
+     *
+     * @param ch  the character to check
+     * @return true if it is a separator character
+     */
+    private static boolean isSeparator(char ch) {
+        return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Normalizes a path, removing double and single dot path steps.
+     * <p>
+     * This method normalizes a path to a standard format.
+     * The input may contain separators in either Unix or Windows format.
+     * The output will contain separators in the format of the system.
+     * <p>
+     * A trailing slash will be retained.
+     * A double slash will be merged to a single slash (but UNC names are handled).
+     * A single dot path segment will be removed.
+     * A double dot will cause that path segment and the one before to be removed.
+     * If the double dot has no parent path segment to work with, <code>null</code>
+     * is returned.
+     * <p>
+     * The output will be the same on both Unix and Windows except
+     * for the separator character.
+     * <pre>
+     * /foo//               -->   /foo/
+     * /foo/./              -->   /foo/
+     * /foo/../bar          -->   /bar
+     * /foo/../bar/         -->   /bar/
+     * /foo/../bar/../baz   -->   /baz
+     * //foo//./bar         -->   /foo/bar
+     * /../                 -->   null
+     * ../foo               -->   null
+     * foo/bar/..           -->   foo/
+     * foo/../../bar        -->   null
+     * foo/../bar           -->   bar
+     * //server/foo/../bar  -->   //server/bar
+     * //server/../bar      -->   null
+     * C:\foo\..\bar        -->   C:\bar
+     * C:\..\bar            -->   null
+     * ~/foo/../bar/        -->   ~/bar/
+     * ~/../bar             -->   null
+     * </pre>
+     * (Note the file separator returned will be correct for Windows/Unix)
+     *
+     * @param filename  the filename to normalize, null returns null
+     * @return the normalized filename, or null if invalid
+     */
+    public static String normalize(String filename) {
+        return doNormalize(filename, true);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Normalizes a path, removing double and single dot path steps,
+     * and removing any final directory separator.
+     * <p>
+     * This method normalizes a path to a standard format.
+     * The input may contain separators in either Unix or Windows format.
+     * The output will contain separators in the format of the system.
+     * <p>
+     * A trailing slash will be removed.
+     * A double slash will be merged to a single slash (but UNC names are handled).
+     * A single dot path segment will be removed.
+     * A double dot will cause that path segment and the one before to be removed.
+     * If the double dot has no parent path segment to work with, <code>null</code>
+     * is returned.
+     * <p>
+     * The output will be the same on both Unix and Windows except
+     * for the separator character.
+     * <pre>
+     * /foo//               -->   /foo
+     * /foo/./              -->   /foo
+     * /foo/../bar          -->   /bar
+     * /foo/../bar/         -->   /bar
+     * /foo/../bar/../baz   -->   /baz
+     * //foo//./bar         -->   /foo/bar
+     * /../                 -->   null
+     * ../foo               -->   null
+     * foo/bar/..           -->   foo
+     * foo/../../bar        -->   null
+     * foo/../bar           -->   bar
+     * //server/foo/../bar  -->   //server/bar
+     * //server/../bar      -->   null
+     * C:\foo\..\bar        -->   C:\bar
+     * C:\..\bar            -->   null
+     * ~/foo/../bar/        -->   ~/bar
+     * ~/../bar             -->   null
+     * </pre>
+     * (Note the file separator returned will be correct for Windows/Unix)
+     *
+     * @param filename  the filename to normalize, null returns null
+     * @return the normalized filename, or null if invalid
+     */
+    public static String normalizeNoEndSeparator(String filename) {
+        return doNormalize(filename, false);
+    }
+
+    /**
+     * Internal method to perform the normalization.
+     *
+     * @param filename  the filename
+     * @param keepSeparator  true to keep the final separator
+     * @return the normalized filename
+     */
+    private static String doNormalize(String filename, boolean keepSeparator) {
+        if (filename == null) {
+            return null;
+        }
+        int size = filename.length();
+        if (size == 0) {
+            return filename;
+        }
+        int prefix = getPrefixLength(filename);
+        if (prefix < 0) {
+            return null;
+        }
+
+        char[] array = new char[size + 2];  // +1 for possible extra slash, +2 for arraycopy
+        filename.getChars(0, filename.length(), array, 0);
+
+        // fix separators throughout
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == OTHER_SEPARATOR) {
+                array[i] = SYSTEM_SEPARATOR;
+            }
+        }
+
+        // add extra separator on the end to simplify code below
+        boolean lastIsDirectory = true;
+        if (array[size - 1] != SYSTEM_SEPARATOR) {
+            array[size++] = SYSTEM_SEPARATOR;
+            lastIsDirectory = false;
+        }
+
+        // adjoining slashes
+        for (int i = prefix + 1; i < size; i++) {
+            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) {
+                System.arraycopy(array, i, array, i - 1, size - i);
+                size--;
+                i--;
+            }
+        }
+
+        // dot slash
+        for (int i = prefix + 1; i < size; i++) {
+            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' &&
+                    (i == prefix + 1 || array[i - 2] == SYSTEM_SEPARATOR)) {
+                if (i == size - 1) {
+                    lastIsDirectory = true;
+                }
+                System.arraycopy(array, i + 1, array, i - 1, size - i);
+                size -=2;
+                i--;
+            }
+        }
+
+        // double dot slash
+        outer:
+        for (int i = prefix + 2; i < size; i++) {
+            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && array[i - 2] == '.' &&
+                    (i == prefix + 2 || array[i - 3] == SYSTEM_SEPARATOR)) {
+                if (i == prefix + 2) {
+                    return null;
+                }
+                if (i == size - 1) {
+                    lastIsDirectory = true;
+                }
+                int j;
+                for (j = i - 4 ; j >= prefix; j--) {
+                    if (array[j] == SYSTEM_SEPARATOR) {
+                        // remove b/../ from a/b/../c
+                        System.arraycopy(array, i + 1, array, j + 1, size - i);
+                        size -= (i - j);
+                        i = j + 1;
+                        continue outer;
+                    }
+                }
+                // remove a/../ from a/../c
+                System.arraycopy(array, i + 1, array, prefix, size - i);
+                size -= (i + 1 - prefix);
+                i = prefix + 1;
+            }
+        }
+
+        if (size <= 0) {  // should never be less than 0
+            return "";
+        }
+        if (size <= prefix) {  // should never be less than prefix
+            return new String(array, 0, size);
+        }
+        if (lastIsDirectory && keepSeparator) {
+            return new String(array, 0, size);  // keep trailing separator
+        }
+        return new String(array, 0, size - 1);  // lose trailing separator
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Concatenates a filename to a base path using normal command line style rules.
+     * <p>
+     * The effect is equivalent to resultant directory after changing
+     * directory to the first argument, followed by changing directory to
+     * the second argument.
+     * <p>
+     * The first argument is the base path, the second is the path to concatenate.
+     * The returned path is always normalized via {@link #normalize(String)},
+     * thus <code>..</code> is handled.
+     * <p>
+     * If <code>pathToAdd</code> is absolute (has an absolute prefix), then
+     * it will be normalized and returned.
+     * Otherwise, the paths will be joined, normalized and returned.
+     * <p>
+     * The output will be the same on both Unix and Windows except
+     * for the separator character.
+     * <pre>
+     * /foo/ + bar          -->   /foo/bar
+     * /foo + bar           -->   /foo/bar
+     * /foo + /bar          -->   /bar
+     * /foo + C:/bar        -->   C:/bar
+     * /foo + C:bar         -->   C:bar (*)
+     * /foo/a/ + ../bar     -->   foo/bar
+     * /foo/ + ../../bar    -->   null
+     * /foo/ + /bar         -->   /bar
+     * /foo/.. + /bar       -->   /bar
+     * /foo + bar/c.txt     -->   /foo/bar/c.txt
+     * /foo/c.txt + bar     -->   /foo/c.txt/bar (!)
+     * </pre>
+     * (*) Note that the Windows relative drive prefix is unreliable when
+     * used with this method.
+     * (!) Note that the first parameter must be a path. If it ends with a name, then
+     * the name will be built into the concatenated path. If this might be a problem,
+     * use {@link #getFullPath(String)} on the base path argument.
+     *
+     * @param basePath  the base path to attach to, always treated as a path
+     * @param fullFilenameToAdd  the filename (or path) to attach to the base
+     * @return the concatenated path, or null if invalid
+     */
+    public static String concat(String basePath, String fullFilenameToAdd) {
+        int prefix = getPrefixLength(fullFilenameToAdd);
+        if (prefix < 0) {
+            return null;
+        }
+        if (prefix > 0) {
+            return normalize(fullFilenameToAdd);
+        }
+        if (basePath == null) {
+            return null;
+        }
+        int len = basePath.length();
+        if (len == 0) {
+            return normalize(fullFilenameToAdd);
+        }
+        char ch = basePath.charAt(len - 1);
+        if (isSeparator(ch)) {
+            return normalize(basePath + fullFilenameToAdd);
+        } else {
+            return normalize(basePath + '/' + fullFilenameToAdd);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Converts all separators to the Unix separator of forward slash.
+     *
+     * @param path  the path to be changed, null ignored
+     * @return the updated path
+     */
+    public static String separatorsToUnix(String path) {
+        if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {
+            return path;
+        }
+        return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);
+    }
+
+    /**
+     * Converts all separators to the Windows separator of backslash.
+     *
+     * @param path  the path to be changed, null ignored
+     * @return the updated path
+     */
+    public static String separatorsToWindows(String path) {
+        if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {
+            return path;
+        }
+        return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);
+    }
+
+    /**
+     * Converts all separators to the system separator.
+     *
+     * @param path  the path to be changed, null ignored
+     * @return the updated path
+     */
+    public static String separatorsToSystem(String path) {
+        if (path == null) {
+            return null;
+        }
+        if (isSystemWindows()) {
+            return separatorsToWindows(path);
+        } else {
+            return separatorsToUnix(path);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * <p>
+     * The prefix length includes the first slash in the full filename
+     * if applicable. Thus, it is possible that the length returned is greater
+     * than the length of the input string.
+     * <pre>
+     * Windows:
+     * a\b\c.txt           --> ""          --> relative
+     * \a\b\c.txt          --> "\"         --> current drive absolute
+     * C:a\b\c.txt         --> "C:"        --> drive relative
+     * C:\a\b\c.txt        --> "C:\"       --> absolute
+     * \\server\a\b\c.txt  --> "\\server\" --> UNC
+     *
+     * Unix:
+     * a/b/c.txt           --> ""          --> relative
+     * /a/b/c.txt          --> "/"         --> absolute
+     * ~/a/b/c.txt         --> "~/"        --> current user
+     * ~                   --> "~/"        --> current user (slash added)
+     * ~user/a/b/c.txt     --> "~user/"    --> named user
+     * ~user               --> "~user/"    --> named user (slash added)
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     * ie. both Unix and Windows prefixes are matched regardless.
+     *
+     * @param filename  the filename to find the prefix in, null returns -1
+     * @return the length of the prefix, -1 if invalid or null
+     */
+    public static int getPrefixLength(String filename) {
+        if (filename == null) {
+            return -1;
+        }
+        int len = filename.length();
+        if (len == 0) {
+            return 0;
+        }
+        char ch0 = filename.charAt(0);
+        if (ch0 == ':') {
+            return -1;
+        }
+        if (len == 1) {
+            if (ch0 == '~') {
+                return 2;  // return a length greater than the input
+            }
+            return (isSeparator(ch0) ? 1 : 0);
+        } else {
+            if (ch0 == '~') {
+                int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
+                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);
+                if (posUnix == -1 && posWin == -1) {
+                    return len + 1;  // return a length greater than the input
+                }
+                posUnix = (posUnix == -1 ? posWin : posUnix);
+                posWin = (posWin == -1 ? posUnix : posWin);
+                return Math.min(posUnix, posWin) + 1;
+            }
+            char ch1 = filename.charAt(1);
+            if (ch1 == ':') {
+                ch0 = Character.toUpperCase(ch0);
+                if (ch0 >= 'A' && ch0 <= 'Z') {
+                    if (len == 2 || isSeparator(filename.charAt(2)) == false) {
+                        return 2;
+                    }
+                    return 3;
+                }
+                return -1;
+
+            } else if (isSeparator(ch0) && isSeparator(ch1)) {
+                int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
+                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
+                if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) {
+                    return -1;
+                }
+                posUnix = (posUnix == -1 ? posWin : posUnix);
+                posWin = (posWin == -1 ? posUnix : posWin);
+                return Math.min(posUnix, posWin) + 1;
+            } else {
+                return (isSeparator(ch0) ? 1 : 0);
+            }
+        }
+    }
+
+    /**
+     * Returns the index of the last directory separator character.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The position of the last forward or backslash is returned.
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to find the last path separator in, null returns -1
+     * @return the index of the last separator character, or -1 if there
+     * is no such character
+     */
+    public static int indexOfLastSeparator(String filename) {
+        if (filename == null) {
+            return -1;
+        }
+        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
+        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
+        return Math.max(lastUnixPos, lastWindowsPos);
+    }
+
+    /**
+     * Returns the index of the last extension separator character, which is a dot.
+     * <p>
+     * This method also checks that there is no directory separator after the last dot.
+     * To do this it uses {@link #indexOfLastSeparator(String)} which will
+     * handle a file in either Unix or Windows format.
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to find the last path separator in, null returns -1
+     * @return the index of the last separator character, or -1 if there
+     * is no such character
+     */
+    public static int indexOfExtension(String filename) {
+        if (filename == null) {
+            return -1;
+        }
+        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
+        int lastSeparator = indexOfLastSeparator(filename);
+        return (lastSeparator > extensionPos ? -1 : extensionPos);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets the prefix from a full filename, such as <code>C:/</code>
+     * or <code>~/</code>.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The prefix includes the first slash in the full filename where applicable.
+     * <pre>
+     * Windows:
+     * a\b\c.txt           --> ""          --> relative
+     * \a\b\c.txt          --> "\"         --> current drive absolute
+     * C:a\b\c.txt         --> "C:"        --> drive relative
+     * C:\a\b\c.txt        --> "C:\"       --> absolute
+     * \\server\a\b\c.txt  --> "\\server\" --> UNC
+     *
+     * Unix:
+     * a/b/c.txt           --> ""          --> relative
+     * /a/b/c.txt          --> "/"         --> absolute
+     * ~/a/b/c.txt         --> "~/"        --> current user
+     * ~                   --> "~/"        --> current user (slash added)
+     * ~user/a/b/c.txt     --> "~user/"    --> named user
+     * ~user               --> "~user/"    --> named user (slash added)
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     * ie. both Unix and Windows prefixes are matched regardless.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the prefix of the file, null if invalid
+     */
+    public static String getPrefix(String filename) {
+        if (filename == null) {
+            return null;
+        }
+        int len = getPrefixLength(filename);
+        if (len < 0) {
+            return null;
+        }
+        if (len > filename.length()) {
+            return filename + UNIX_SEPARATOR;  // we know this only happens for unix
+        }
+        return filename.substring(0, len);
+    }
+
+    /**
+     * Gets the path from a full filename, which excludes the prefix.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The method is entirely text based, and returns the text before and
+     * including the last forward or backslash.
+     * <pre>
+     * C:\a\b\c.txt --> a\b\
+     * ~/a/b/c.txt  --> a/b/
+     * a.txt        --> ""
+     * a/b/c        --> a/b/
+     * a/b/c/       --> a/b/c/
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     * <p>
+     * This method drops the prefix from the result.
+     * See {@link #getFullPath(String)} for the method that retains the prefix.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the path of the file, an empty string if none exists, null if invalid
+     */
+    public static String getPath(String filename) {
+        return doGetPath(filename, 1);
+    }
+
+    /**
+     * Gets the path from a full filename, which excludes the prefix, and
+     * also excluding the final directory separator.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The method is entirely text based, and returns the text before the
+     * last forward or backslash.
+     * <pre>
+     * C:\a\b\c.txt --> a\b
+     * ~/a/b/c.txt  --> a/b
+     * a.txt        --> ""
+     * a/b/c        --> a/b
+     * a/b/c/       --> a/b/c
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     * <p>
+     * This method drops the prefix from the result.
+     * See {@link #getFullPathNoEndSeparator(String)} for the method that retains the prefix.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the path of the file, an empty string if none exists, null if invalid
+     */
+    public static String getPathNoEndSeparator(String filename) {
+        return doGetPath(filename, 0);
+    }
+
+    /**
+     * Does the work of getting the path.
+     *
+     * @param filename  the filename
+     * @param separatorAdd  0 to omit the end separator, 1 to return it
+     * @return the path
+     */
+    private static String doGetPath(String filename, int separatorAdd) {
+        if (filename == null) {
+            return null;
+        }
+        int prefix = getPrefixLength(filename);
+        if (prefix < 0) {
+            return null;
+        }
+        int index = indexOfLastSeparator(filename);
+        if (prefix >= filename.length() || index < 0) {
+            return "";
+        }
+        return filename.substring(prefix, index + separatorAdd);
+    }
+
+    /**
+     * Gets the full path from a full filename, which is the prefix + path.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The method is entirely text based, and returns the text before and
+     * including the last forward or backslash.
+     * <pre>
+     * C:\a\b\c.txt --> C:\a\b\
+     * ~/a/b/c.txt  --> ~/a/b/
+     * a.txt        --> ""
+     * a/b/c        --> a/b/
+     * a/b/c/       --> a/b/c/
+     * C:           --> C:
+     * C:\          --> C:\
+     * ~            --> ~/
+     * ~/           --> ~/
+     * ~user        --> ~user/
+     * ~user/       --> ~user/
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the path of the file, an empty string if none exists, null if invalid
+     */
+    public static String getFullPath(String filename) {
+        return doGetFullPath(filename, true);
+    }
+
+    /**
+     * Gets the full path from a full filename, which is the prefix + path,
+     * and also excluding the final directory separator.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The method is entirely text based, and returns the text before the
+     * last forward or backslash.
+     * <pre>
+     * C:\a\b\c.txt --> C:\a\b
+     * ~/a/b/c.txt  --> ~/a/b
+     * a.txt        --> ""
+     * a/b/c        --> a/b
+     * a/b/c/       --> a/b/c
+     * C:           --> C:
+     * C:\          --> C:\
+     * ~            --> ~
+     * ~/           --> ~
+     * ~user        --> ~user
+     * ~user/       --> ~user
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the path of the file, an empty string if none exists, null if invalid
+     */
+    public static String getFullPathNoEndSeparator(String filename) {
+        return doGetFullPath(filename, false);
+    }
+
+    /**
+     * Does the work of getting the path.
+     *
+     * @param filename  the filename
+     * @param includeSeparator  true to include the end separator
+     * @return the path
+     */
+    private static String doGetFullPath(String filename, boolean includeSeparator) {
+        if (filename == null) {
+            return null;
+        }
+        int prefix = getPrefixLength(filename);
+        if (prefix < 0) {
+            return null;
+        }
+        if (prefix >= filename.length()) {
+            if (includeSeparator) {
+                return getPrefix(filename);  // add end slash if necessary
+            } else {
+                return filename;
+            }
+        }
+        int index = indexOfLastSeparator(filename);
+        if (index < 0) {
+            return filename.substring(0, prefix);
+        }
+        int end = index + (includeSeparator ?  1 : 0);
+        return filename.substring(0, end);
+    }
+
+    /**
+     * Gets the name minus the path from a full filename.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The text after the last forward or backslash is returned.
+     * <pre>
+     * a/b/c.txt --> c.txt
+     * a.txt     --> a.txt
+     * a/b/c     --> c
+     * a/b/c/    --> ""
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the name of the file without the path, or an empty string if none exists
+     */
+    public static String getName(String filename) {
+        if (filename == null) {
+            return null;
+        }
+        int index = indexOfLastSeparator(filename);
+        return filename.substring(index + 1);
+    }
+
+    /**
+     * Gets the base name, minus the full path and extension, from a full filename.
+     * <p>
+     * This method will handle a file in either Unix or Windows format.
+     * The text after the last forward or backslash and before the last dot is returned.
+     * <pre>
+     * a/b/c.txt --> c
+     * a.txt     --> a
+     * a/b/c     --> c
+     * a/b/c/    --> ""
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the name of the file without the path, or an empty string if none exists
+     */
+    public static String getBaseName(String filename) {
+        return removeExtension(getName(filename));
+    }
+
+    /**
+     * Gets the extension of a filename.
+     * <p>
+     * This method returns the textual part of the filename after the last dot.
+     * There must be no directory separator after the dot.
+     * <pre>
+     * foo.txt      --> "txt"
+     * a/b/c.jpg    --> "jpg"
+     * a/b.txt/c    --> ""
+     * a/b/c        --> ""
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename the filename to retrieve the extension of.
+     * @return the extension of the file or an empty string if none exists.
+     */
+    public static String getExtension(String filename) {
+        if (filename == null) {
+            return null;
+        }
+        int index = indexOfExtension(filename);
+        if (index == -1) {
+            return "";
+        } else {
+            return filename.substring(index + 1);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Removes the extension from a filename.
+     * <p>
+     * This method returns the textual part of the filename before the last dot.
+     * There must be no directory separator after the dot.
+     * <pre>
+     * foo.txt    --> foo
+     * a\b\c.jpg  --> a\b\c
+     * a\b\c      --> a\b\c
+     * a.b\c      --> a.b\c
+     * </pre>
+     * <p>
+     * The output will be the same irrespective of the machine that the code is running on.
+     *
+     * @param filename  the filename to query, null returns null
+     * @return the filename minus the extension
+     */
+    public static String removeExtension(String filename) {
+        if (filename == null) {
+            return null;
+        }
+        int index = indexOfExtension(filename);
+        if (index == -1) {
+            return filename;
+        } else {
+            return filename.substring(0, index);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks whether two filenames are equal exactly.
+     * <p>
+     * No processing is performed on the filenames other than comparison,
+     * thus this is merely a null-safe case-sensitive equals.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     * @see IOCase#SENSITIVE
+     */
+    public static boolean equals(String filename1, String filename2) {
+        return equals(filename1, filename2, false, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Checks whether two filenames are equal using the case rules of the system.
+     * <p>
+     * No processing is performed on the filenames other than comparison.
+     * The check is case-sensitive on Unix and case-insensitive on Windows.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     * @see IOCase#SYSTEM
+     */
+    public static boolean equalsOnSystem(String filename1, String filename2) {
+        return equals(filename1, filename2, false, IOCase.SYSTEM);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks whether two filenames are equal after both have been normalized.
+     * <p>
+     * Both filenames are first passed to {@link #normalize(String)}.
+     * The check is then performed in a case-sensitive manner.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     * @see IOCase#SENSITIVE
+     */
+    public static boolean equalsNormalized(String filename1, String filename2) {
+        return equals(filename1, filename2, true, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Checks whether two filenames are equal after both have been normalized
+     * and using the case rules of the system.
+     * <p>
+     * Both filenames are first passed to {@link #normalize(String)}.
+     * The check is then performed case-sensitive on Unix and
+     * case-insensitive on Windows.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     * @see IOCase#SYSTEM
+     */
+    public static boolean equalsNormalizedOnSystem(String filename1, String filename2) {
+        return equals(filename1, filename2, true, IOCase.SYSTEM);
+    }
+
+    /**
+     * Checks whether two filenames are equal, optionally normalizing and providing
+     * control over the case-sensitivity.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @param normalized  whether to normalize the filenames
+     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
+     * @return true if the filenames are equal, null equals null
+     * @since Commons IO 1.3
+     */
+    public static boolean equals(
+            String filename1, String filename2,
+            boolean normalized, IOCase caseSensitivity) {
+
+        if (filename1 == null || filename2 == null) {
+            return filename1 == filename2;
+        }
+        if (normalized) {
+            filename1 = normalize(filename1);
+            filename2 = normalize(filename2);
+            if (filename1 == null || filename2 == null) {
+                throw new NullPointerException(
+                    "Error normalizing one or both of the file names");
+            }
+        }
+        if (caseSensitivity == null) {
+            caseSensitivity = IOCase.SENSITIVE;
+        }
+        return caseSensitivity.checkEquals(filename1, filename2);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks whether the extension of the filename is that specified.
+     * <p>
+     * This method obtains the extension as the textual part of the filename
+     * after the last dot. There must be no directory separator after the dot.
+     * The extension check is case-sensitive on all platforms.
+     *
+     * @param filename  the filename to query, null returns false
+     * @param extension  the extension to check for, null or empty checks for no extension
+     * @return true if the filename has the specified extension
+     */
+    public static boolean isExtension(String filename, String extension) {
+        if (filename == null) {
+            return false;
+        }
+        if (extension == null || extension.length() == 0) {
+            return (indexOfExtension(filename) == -1);
+        }
+        String fileExt = getExtension(filename);
+        return fileExt.equals(extension);
+    }
+
+    /**
+     * Checks whether the extension of the filename is one of those specified.
+     * <p>
+     * This method obtains the extension as the textual part of the filename
+     * after the last dot. There must be no directory separator after the dot.
+     * The extension check is case-sensitive on all platforms.
+     *
+     * @param filename  the filename to query, null returns false
+     * @param extensions  the extensions to check for, null checks for no extension
+     * @return true if the filename is one of the extensions
+     */
+    public static boolean isExtension(String filename, String[] extensions) {
+        if (filename == null) {
+            return false;
+        }
+        if (extensions == null || extensions.length == 0) {
+            return (indexOfExtension(filename) == -1);
+        }
+        String fileExt = getExtension(filename);
+        for (int i = 0; i < extensions.length; i++) {
+            if (fileExt.equals(extensions[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the extension of the filename is one of those specified.
+     * <p>
+     * This method obtains the extension as the textual part of the filename
+     * after the last dot. There must be no directory separator after the dot.
+     * The extension check is case-sensitive on all platforms.
+     *
+     * @param filename  the filename to query, null returns false
+     * @param extensions  the extensions to check for, null checks for no extension
+     * @return true if the filename is one of the extensions
+     */
+    public static boolean isExtension(String filename, Collection<String> extensions) {
+        if (filename == null) {
+            return false;
+        }
+        if (extensions == null || extensions.isEmpty()) {
+            return (indexOfExtension(filename) == -1);
+        }
+        String fileExt = getExtension(filename);
+        for (Iterator<String> it = extensions.iterator(); it.hasNext();) {
+            if (fileExt.equals(it.next())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks a filename to see if it matches the specified wildcard matcher,
+     * always testing case-sensitive.
+     * <p>
+     * The wildcard matcher uses the characters '?' and '*' to represent a
+     * single or multiple wildcard characters.
+     * This is the same as often found on Dos/Unix command lines.
+     * The check is case-sensitive always.
+     * <pre>
+     * wildcardMatch("c.txt", "*.txt")      --> true
+     * wildcardMatch("c.txt", "*.jpg")      --> false
+     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
+     * wildcardMatch("c.txt", "*.???")      --> true
+     * wildcardMatch("c.txt", "*.????")     --> false
+     * </pre>
+     *
+     * @param filename  the filename to match on
+     * @param wildcardMatcher  the wildcard string to match against
+     * @return true if the filename matches the wilcard string
+     * @see IOCase#SENSITIVE
+     */
+    public static boolean wildcardMatch(String filename, String wildcardMatcher) {
+        return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Checks a filename to see if it matches the specified wildcard matcher
+     * using the case rules of the system.
+     * <p>
+     * The wildcard matcher uses the characters '?' and '*' to represent a
+     * single or multiple wildcard characters.
+     * This is the same as often found on Dos/Unix command lines.
+     * The check is case-sensitive on Unix and case-insensitive on Windows.
+     * <pre>
+     * wildcardMatch("c.txt", "*.txt")      --> true
+     * wildcardMatch("c.txt", "*.jpg")      --> false
+     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
+     * wildcardMatch("c.txt", "*.???")      --> true
+     * wildcardMatch("c.txt", "*.????")     --> false
+     * </pre>
+     *
+     * @param filename  the filename to match on
+     * @param wildcardMatcher  the wildcard string to match against
+     * @return true if the filename matches the wilcard string
+     * @see IOCase#SYSTEM
+     */
+    public static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) {
+        return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
+    }
+
+    /**
+     * Checks a filename to see if it matches the specified wildcard matcher
+     * allowing control over case-sensitivity.
+     * <p>
+     * The wildcard matcher uses the characters '?' and '*' to represent a
+     * single or multiple wildcard characters.
+     *
+     * @param filename  the filename to match on
+     * @param wildcardMatcher  the wildcard string to match against
+     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
+     * @return true if the filename matches the wilcard string
+     * @since Commons IO 1.3
+     */
+    public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {
+        if (filename == null && wildcardMatcher == null) {
+            return true;
+        }
+        if (filename == null || wildcardMatcher == null) {
+            return false;
+        }
+        if (caseSensitivity == null) {
+            caseSensitivity = IOCase.SENSITIVE;
+        }
+        filename = caseSensitivity.convertCase(filename);
+        wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);
+        String[] wcs = splitOnTokens(wildcardMatcher);
+        boolean anyChars = false;
+        int textIdx = 0;
+        int wcsIdx = 0;
+        Stack<int[]> backtrack = new Stack<int[]>();
+
+        // loop around a backtrack stack, to handle complex * matching
+        do {
+            if (backtrack.size() > 0) {
+                int[] array = backtrack.pop();
+                wcsIdx = array[0];
+                textIdx = array[1];
+                anyChars = true;
+            }
+
+            // loop whilst tokens and text left to process
+            while (wcsIdx < wcs.length) {
+
+                if (wcs[wcsIdx].equals("?")) {
+                    // ? so move to next text char
+                    textIdx++;
+                    anyChars = false;
+
+                } else if (wcs[wcsIdx].equals("*")) {
+                    // set any chars status
+                    anyChars = true;
+                    if (wcsIdx == wcs.length - 1) {
+                        textIdx = filename.length();
+                    }
+
+                } else {
+                    // matching text token
+                    if (anyChars) {
+                        // any chars then try to locate text token
+                        textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
+                        if (textIdx == -1) {
+                            // token not found
+                            break;
+                        }
+                        int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);
+                        if (repeat >= 0) {
+                            backtrack.push(new int[] {wcsIdx, repeat});
+                        }
+                    } else {
+                        // matching from current position
+                        if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
+                            // couldnt match token
+                            break;
+                        }
+                    }
+
+                    // matched text token, move text index to end of matched token
+                    textIdx += wcs[wcsIdx].length();
+                    anyChars = false;
+                }
+
+                wcsIdx++;
+            }
+
+            // full match
+            if (wcsIdx == wcs.length && textIdx == filename.length()) {
+                return true;
+            }
+
+        } while (backtrack.size() > 0);
+
+        return false;
+    }
+
+    /**
+     * Splits a string into a number of tokens.
+     *
+     * @param text  the text to split
+     * @return the tokens, never null
+     */
+    static String[] splitOnTokens(String text) {
+        // used by wildcardMatch
+        // package level so a unit test may run on this
+
+        if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {
+            return new String[] { text };
+        }
+
+        char[] array = text.toCharArray();
+        ArrayList<String> list = new ArrayList<String>();
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == '?' || array[i] == '*') {
+                if (buffer.length() != 0) {
+                    list.add(buffer.toString());
+                    buffer.setLength(0);
+                }
+                if (array[i] == '?') {
+                    list.add("?");
+                } else if (list.size() == 0 ||
+                        (i > 0 && list.get(list.size() - 1).equals("*") == false)) {
+                    list.add("*");
+                }
+            } else {
+                buffer.append(array[i]);
+            }
+        }
+        if (buffer.length() != 0) {
+            list.add(buffer.toString());
+        }
+
+        return list.toArray( new String[ list.size() ] );
+    }
+
+}
diff --git a/src/org/apache/commons/io/HexDump.java b/src/org/apache/commons/io/HexDump.java
index b0d468d..8293430 100644
--- a/src/org/apache/commons/io/HexDump.java
+++ b/src/org/apache/commons/io/HexDump.java
@@ -1,149 +1,149 @@
-/*

- * 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.commons.io;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * Dumps data in hexadecimal format.

- * <p>

- * Provides a single function to take an array of bytes and display it

- * in hexadecimal form.

- * <p>

- * Origin of code: POI.

- *

- * @author Scott Sanders

- * @author Marc Johnson

- * @version $Id: HexDump.java 596667 2007-11-20 13:50:14Z niallp $

- */

-public class HexDump {

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public HexDump() {

-        super();

-    }

-

-    /**

-     * Dump an array of bytes to an OutputStream.

-     *

-     * @param data  the byte array to be dumped

-     * @param offset  its offset, whatever that might mean

-     * @param stream  the OutputStream to which the data is to be

-     *               written

-     * @param index initial index into the byte array

-     *

-     * @throws IOException is thrown if anything goes wrong writing

-     *         the data to stream

-     * @throws ArrayIndexOutOfBoundsException if the index is

-     *         outside the data array's bounds

-     * @throws IllegalArgumentException if the output stream is null

-     */

-

-    public static void dump(byte[] data, long offset,

-                            OutputStream stream, int index)

-            throws IOException, ArrayIndexOutOfBoundsException,

-            IllegalArgumentException {

-        

-        if ((index < 0) || (index >= data.length)) {

-            throw new ArrayIndexOutOfBoundsException(

-                    "illegal index: " + index + " into array of length "

-                    + data.length);

-        }

-        if (stream == null) {

-            throw new IllegalArgumentException("cannot write to nullstream");

-        }

-        long display_offset = offset + index;

-        StringBuffer buffer = new StringBuffer(74);

-

-        for (int j = index; j < data.length; j += 16) {

-            int chars_read = data.length - j;

-

-            if (chars_read > 16) {

-                chars_read = 16;

-            }

-            dump(buffer, display_offset).append(' ');

-            for (int k = 0; k < 16; k++) {

-                if (k < chars_read) {

-                    dump(buffer, data[k + j]);

-                } else {

-                    buffer.append("  ");

-                }

-                buffer.append(' ');

-            }

-            for (int k = 0; k < chars_read; k++) {

-                if ((data[k + j] >= ' ') && (data[k + j] < 127)) {

-                    buffer.append((char) data[k + j]);

-                } else {

-                    buffer.append('.');

-                }

-            }

-            buffer.append(EOL);

-            stream.write(buffer.toString().getBytes());

-            stream.flush();

-            buffer.setLength(0);

-            display_offset += chars_read;

-        }

-    }

-

-    /**

-     * The line-separator (initializes to "line.separator" system property.

-     */

-    public static final String EOL =

-            System.getProperty("line.separator");

-    private static final char[] _hexcodes =

-            {

-                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

-                'A', 'B', 'C', 'D', 'E', 'F'

-            };

-    private static final int[] _shifts =

-            {

-                28, 24, 20, 16, 12, 8, 4, 0

-            };

-

-    /**

-     * Dump a long value into a StringBuffer.

-     *

-     * @param _lbuffer the StringBuffer to dump the value in

-     * @param value  the long value to be dumped

-     * @return StringBuffer containing the dumped value.

-     */

-    private static StringBuffer dump(StringBuffer _lbuffer, long value) {

-        for (int j = 0; j < 8; j++) {

-            _lbuffer

-                    .append(_hexcodes[((int) (value >> _shifts[j])) & 15]);

-        }

-        return _lbuffer;

-    }

-

-    /**

-     * Dump a byte value into a StringBuffer.

-     *

-     * @param _cbuffer the StringBuffer to dump the value in

-     * @param value  the byte value to be dumped

-     * @return StringBuffer containing the dumped value.

-     */

-    private static StringBuffer dump(StringBuffer _cbuffer, byte value) {

-        for (int j = 0; j < 2; j++) {

-            _cbuffer.append(_hexcodes[(value >> _shifts[j + 6]) & 15]);

-        }

-        return _cbuffer;

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Dumps data in hexadecimal format.
+ * <p>
+ * Provides a single function to take an array of bytes and display it
+ * in hexadecimal form.
+ * <p>
+ * Origin of code: POI.
+ *
+ * @author Scott Sanders
+ * @author Marc Johnson
+ * @version $Id: HexDump.java 596667 2007-11-20 13:50:14Z niallp $
+ */
+public class HexDump {
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public HexDump() {
+        super();
+    }
+
+    /**
+     * Dump an array of bytes to an OutputStream.
+     *
+     * @param data  the byte array to be dumped
+     * @param offset  its offset, whatever that might mean
+     * @param stream  the OutputStream to which the data is to be
+     *               written
+     * @param index initial index into the byte array
+     *
+     * @throws IOException is thrown if anything goes wrong writing
+     *         the data to stream
+     * @throws ArrayIndexOutOfBoundsException if the index is
+     *         outside the data array's bounds
+     * @throws IllegalArgumentException if the output stream is null
+     */
+
+    public static void dump(byte[] data, long offset,
+                            OutputStream stream, int index)
+            throws IOException, ArrayIndexOutOfBoundsException,
+            IllegalArgumentException {
+        
+        if ((index < 0) || (index >= data.length)) {
+            throw new ArrayIndexOutOfBoundsException(
+                    "illegal index: " + index + " into array of length "
+                    + data.length);
+        }
+        if (stream == null) {
+            throw new IllegalArgumentException("cannot write to nullstream");
+        }
+        long display_offset = offset + index;
+        StringBuffer buffer = new StringBuffer(74);
+
+        for (int j = index; j < data.length; j += 16) {
+            int chars_read = data.length - j;
+
+            if (chars_read > 16) {
+                chars_read = 16;
+            }
+            dump(buffer, display_offset).append(' ');
+            for (int k = 0; k < 16; k++) {
+                if (k < chars_read) {
+                    dump(buffer, data[k + j]);
+                } else {
+                    buffer.append("  ");
+                }
+                buffer.append(' ');
+            }
+            for (int k = 0; k < chars_read; k++) {
+                if ((data[k + j] >= ' ') && (data[k + j] < 127)) {
+                    buffer.append((char) data[k + j]);
+                } else {
+                    buffer.append('.');
+                }
+            }
+            buffer.append(EOL);
+            stream.write(buffer.toString().getBytes());
+            stream.flush();
+            buffer.setLength(0);
+            display_offset += chars_read;
+        }
+    }
+
+    /**
+     * The line-separator (initializes to "line.separator" system property.
+     */
+    public static final String EOL =
+            System.getProperty("line.separator");
+    private static final char[] _hexcodes =
+            {
+                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                'A', 'B', 'C', 'D', 'E', 'F'
+            };
+    private static final int[] _shifts =
+            {
+                28, 24, 20, 16, 12, 8, 4, 0
+            };
+
+    /**
+     * Dump a long value into a StringBuffer.
+     *
+     * @param _lbuffer the StringBuffer to dump the value in
+     * @param value  the long value to be dumped
+     * @return StringBuffer containing the dumped value.
+     */
+    private static StringBuffer dump(StringBuffer _lbuffer, long value) {
+        for (int j = 0; j < 8; j++) {
+            _lbuffer
+                    .append(_hexcodes[((int) (value >> _shifts[j])) & 15]);
+        }
+        return _lbuffer;
+    }
+
+    /**
+     * Dump a byte value into a StringBuffer.
+     *
+     * @param _cbuffer the StringBuffer to dump the value in
+     * @param value  the byte value to be dumped
+     * @return StringBuffer containing the dumped value.
+     */
+    private static StringBuffer dump(StringBuffer _cbuffer, byte value) {
+        for (int j = 0; j < 2; j++) {
+            _cbuffer.append(_hexcodes[(value >> _shifts[j + 6]) & 15]);
+        }
+        return _cbuffer;
+    }
+
+}
diff --git a/src/org/apache/commons/io/IOCase.java b/src/org/apache/commons/io/IOCase.java
index 4230f45..2f9bf18 100644
--- a/src/org/apache/commons/io/IOCase.java
+++ b/src/org/apache/commons/io/IOCase.java
@@ -1,238 +1,238 @@
-/*

- * 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.commons.io;

-

-import java.io.Serializable;

-

-/**

- * Enumeration of IO case sensitivity.

- * <p>

- * Different filing systems have different rules for case-sensitivity.

- * Windows is case-insensitive, Unix is case-sensitive.

- * <p>

- * This class captures that difference, providing an enumeration to

- * control how filename comparisons should be performed. It also provides

- * methods that use the enumeration to perform comparisons.

- * <p>

- * Wherever possible, you should use the <code>check</code> methods in this

- * class to compare filenames.

- *

- * @author Stephen Colebourne

- * @version $Id: IOCase.java 606345 2007-12-21 23:43:01Z ggregory $

- * @since Commons IO 1.3

- */

-public final class IOCase implements Serializable {

-

-    /**

-     * The constant for case sensitive regardless of operating system.

-     */

-    public static final IOCase SENSITIVE = new IOCase("Sensitive", true);

-    

-    /**

-     * The constant for case insensitive regardless of operating system.

-     */

-    public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);

-    

-    /**

-     * The constant for case sensitivity determined by the current operating system.

-     * Windows is case-insensitive when comparing filenames, Unix is case-sensitive.

-     * <p>

-     * If you derialize this constant of Windows, and deserialize on Unix, or vice

-     * versa, then the value of the case-sensitivity flag will change.

-     */

-    public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());

-

-    /** Serialization version. */

-    private static final long serialVersionUID = -6343169151696340687L;

-

-    /** The enumeration name. */

-    private final String name;

-    

-    /** The sensitivity flag. */

-    private final transient boolean sensitive;

-

-    //-----------------------------------------------------------------------

-    /**

-     * Factory method to create an IOCase from a name.

-     * 

-     * @param name  the name to find

-     * @return the IOCase object

-     * @throws IllegalArgumentException if the name is invalid

-     */

-    public static IOCase forName(String name) {

-        if (IOCase.SENSITIVE.name.equals(name)){

-            return IOCase.SENSITIVE;

-        }

-        if (IOCase.INSENSITIVE.name.equals(name)){

-            return IOCase.INSENSITIVE;

-        }

-        if (IOCase.SYSTEM.name.equals(name)){

-            return IOCase.SYSTEM;

-        }

-        throw new IllegalArgumentException("Invalid IOCase name: " + name);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Private constructor.

-     * 

-     * @param name  the name

-     * @param sensitive  the sensitivity

-     */

-    private IOCase(String name, boolean sensitive) {

-        this.name = name;

-        this.sensitive = sensitive;

-    }

-

-    /**

-     * Replaces the enumeration from the stream with a real one.

-     * This ensures that the correct flag is set for SYSTEM.

-     * 

-     * @return the resolved object

-     */

-    private Object readResolve() {

-        return forName(name);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Gets the name of the constant.

-     * 

-     * @return the name of the constant

-     */

-    public String getName() {

-        return name;

-    }

-

-    /**

-     * Does the object represent case sensitive comparison.

-     * 

-     * @return true if case sensitive

-     */

-    public boolean isCaseSensitive() {

-        return sensitive;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Compares two strings using the case-sensitivity rule.

-     * <p>

-     * This method mimics {@link String#compareTo} but takes case-sensitivity

-     * into account.

-     * 

-     * @param str1  the first string to compare, not null

-     * @param str2  the second string to compare, not null

-     * @return true if equal using the case rules

-     * @throws NullPointerException if either string is null

-     */

-    public int checkCompareTo(String str1, String str2) {

-        if (str1 == null || str2 == null) {

-            throw new NullPointerException("The strings must not be null");

-        }

-        return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);

-    }

-

-    /**

-     * Compares two strings using the case-sensitivity rule.

-     * <p>

-     * This method mimics {@link String#equals} but takes case-sensitivity

-     * into account.

-     * 

-     * @param str1  the first string to compare, not null

-     * @param str2  the second string to compare, not null

-     * @return true if equal using the case rules

-     * @throws NullPointerException if either string is null

-     */

-    public boolean checkEquals(String str1, String str2) {

-        if (str1 == null || str2 == null) {

-            throw new NullPointerException("The strings must not be null");

-        }

-        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);

-    }

-

-    /**

-     * Checks if one string starts with another using the case-sensitivity rule.

-     * <p>

-     * This method mimics {@link String#startsWith(String)} but takes case-sensitivity

-     * into account.

-     * 

-     * @param str  the string to check, not null

-     * @param start  the start to compare against, not null

-     * @return true if equal using the case rules

-     * @throws NullPointerException if either string is null

-     */

-    public boolean checkStartsWith(String str, String start) {

-        return str.regionMatches(!sensitive, 0, start, 0, start.length());

-    }

-

-    /**

-     * Checks if one string ends with another using the case-sensitivity rule.

-     * <p>

-     * This method mimics {@link String#endsWith} but takes case-sensitivity

-     * into account.

-     * 

-     * @param str  the string to check, not null

-     * @param end  the end to compare against, not null

-     * @return true if equal using the case rules

-     * @throws NullPointerException if either string is null

-     */

-    public boolean checkEndsWith(String str, String end) {

-        int endLen = end.length();

-        return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);

-    }

-

-    /**

-     * Checks if one string contains another at a specific index using the case-sensitivity rule.

-     * <p>

-     * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} 

-     * but takes case-sensitivity into account.

-     * 

-     * @param str  the string to check, not null

-     * @param strStartIndex  the index to start at in str

-     * @param search  the start to search for, not null

-     * @return true if equal using the case rules

-     * @throws NullPointerException if either string is null

-     */

-    public boolean checkRegionMatches(String str, int strStartIndex, String search) {

-        return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());

-    }

-

-    /**

-     * Converts the case of the input String to a standard format.

-     * Subsequent operations can then use standard String methods.

-     * 

-     * @param str  the string to convert, null returns null

-     * @return the lower-case version if case-insensitive

-     */

-    String convertCase(String str) {

-        if (str == null) {

-            return null;

-        }

-        return sensitive ? str : str.toLowerCase();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Gets a string describing the sensitivity.

-     * 

-     * @return a string describing the sensitivity

-     */

-    public String toString() {

-        return name;

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.Serializable;
+
+/**
+ * Enumeration of IO case sensitivity.
+ * <p>
+ * Different filing systems have different rules for case-sensitivity.
+ * Windows is case-insensitive, Unix is case-sensitive.
+ * <p>
+ * This class captures that difference, providing an enumeration to
+ * control how filename comparisons should be performed. It also provides
+ * methods that use the enumeration to perform comparisons.
+ * <p>
+ * Wherever possible, you should use the <code>check</code> methods in this
+ * class to compare filenames.
+ *
+ * @author Stephen Colebourne
+ * @version $Id: IOCase.java 606345 2007-12-21 23:43:01Z ggregory $
+ * @since Commons IO 1.3
+ */
+public final class IOCase implements Serializable {
+
+    /**
+     * The constant for case sensitive regardless of operating system.
+     */
+    public static final IOCase SENSITIVE = new IOCase("Sensitive", true);
+    
+    /**
+     * The constant for case insensitive regardless of operating system.
+     */
+    public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);
+    
+    /**
+     * The constant for case sensitivity determined by the current operating system.
+     * Windows is case-insensitive when comparing filenames, Unix is case-sensitive.
+     * <p>
+     * If you derialize this constant of Windows, and deserialize on Unix, or vice
+     * versa, then the value of the case-sensitivity flag will change.
+     */
+    public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());
+
+    /** Serialization version. */
+    private static final long serialVersionUID = -6343169151696340687L;
+
+    /** The enumeration name. */
+    private final String name;
+    
+    /** The sensitivity flag. */
+    private final transient boolean sensitive;
+
+    //-----------------------------------------------------------------------
+    /**
+     * Factory method to create an IOCase from a name.
+     * 
+     * @param name  the name to find
+     * @return the IOCase object
+     * @throws IllegalArgumentException if the name is invalid
+     */
+    public static IOCase forName(String name) {
+        if (IOCase.SENSITIVE.name.equals(name)){
+            return IOCase.SENSITIVE;
+        }
+        if (IOCase.INSENSITIVE.name.equals(name)){
+            return IOCase.INSENSITIVE;
+        }
+        if (IOCase.SYSTEM.name.equals(name)){
+            return IOCase.SYSTEM;
+        }
+        throw new IllegalArgumentException("Invalid IOCase name: " + name);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Private constructor.
+     * 
+     * @param name  the name
+     * @param sensitive  the sensitivity
+     */
+    private IOCase(String name, boolean sensitive) {
+        this.name = name;
+        this.sensitive = sensitive;
+    }
+
+    /**
+     * Replaces the enumeration from the stream with a real one.
+     * This ensures that the correct flag is set for SYSTEM.
+     * 
+     * @return the resolved object
+     */
+    private Object readResolve() {
+        return forName(name);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets the name of the constant.
+     * 
+     * @return the name of the constant
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Does the object represent case sensitive comparison.
+     * 
+     * @return true if case sensitive
+     */
+    public boolean isCaseSensitive() {
+        return sensitive;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Compares two strings using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#compareTo} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str1  the first string to compare, not null
+     * @param str2  the second string to compare, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public int checkCompareTo(String str1, String str2) {
+        if (str1 == null || str2 == null) {
+            throw new NullPointerException("The strings must not be null");
+        }
+        return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
+    }
+
+    /**
+     * Compares two strings using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#equals} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str1  the first string to compare, not null
+     * @param str2  the second string to compare, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkEquals(String str1, String str2) {
+        if (str1 == null || str2 == null) {
+            throw new NullPointerException("The strings must not be null");
+        }
+        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
+    }
+
+    /**
+     * Checks if one string starts with another using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str  the string to check, not null
+     * @param start  the start to compare against, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkStartsWith(String str, String start) {
+        return str.regionMatches(!sensitive, 0, start, 0, start.length());
+    }
+
+    /**
+     * Checks if one string ends with another using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#endsWith} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str  the string to check, not null
+     * @param end  the end to compare against, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkEndsWith(String str, String end) {
+        int endLen = end.length();
+        return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
+    }
+
+    /**
+     * Checks if one string contains another at a specific index using the case-sensitivity rule.
+     * <p>
+     * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} 
+     * but takes case-sensitivity into account.
+     * 
+     * @param str  the string to check, not null
+     * @param strStartIndex  the index to start at in str
+     * @param search  the start to search for, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkRegionMatches(String str, int strStartIndex, String search) {
+        return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
+    }
+
+    /**
+     * Converts the case of the input String to a standard format.
+     * Subsequent operations can then use standard String methods.
+     * 
+     * @param str  the string to convert, null returns null
+     * @return the lower-case version if case-insensitive
+     */
+    String convertCase(String str) {
+        if (str == null) {
+            return null;
+        }
+        return sensitive ? str : str.toLowerCase();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets a string describing the sensitivity.
+     * 
+     * @return a string describing the sensitivity
+     */
+    public String toString() {
+        return name;
+    }
+
+}
diff --git a/src/org/apache/commons/io/IOExceptionWithCause.java b/src/org/apache/commons/io/IOExceptionWithCause.java
index a15815a..0d81d31 100644
--- a/src/org/apache/commons/io/IOExceptionWithCause.java
+++ b/src/org/apache/commons/io/IOExceptionWithCause.java
@@ -1,69 +1,69 @@
-/*

- * 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.commons.io;

-

-import java.io.IOException;

-

-/**

- * Subclasses IOException with the {@link Throwable} constructors missing before Java 6. If you are using Java 6,

- * consider this class deprecated and use {@link IOException}.

- * 

- * @author <a href="http://commons.apache.org/io/">Apache Commons IO</a>

- * @version $Id$

- * @since Commons IO 1.4

- */

-public class IOExceptionWithCause extends IOException {

-

-    /**

-     * Defines the serial version UID.

-     */

-    private static final long serialVersionUID = 1L;

-

-    /**

-     * Constructs a new instance with the given message and cause.

-     * <p>

-     * As specified in {@link Throwable}, the message in the given <code>cause</code> is not used in this instance's

-     * message.

-     * </p>

-     * 

-     * @param message

-     *            the message (see {@link #getMessage()})

-     * @param cause

-     *            the cause (see {@link #getCause()}). A <code>null</code> value is allowed.

-     */

-    public IOExceptionWithCause(String message, Throwable cause) {

-        super(message);

-        this.initCause(cause);

-    }

-

-    /**

-     * Constructs a new instance with the given cause.

-     * <p>

-     * The message is set to <code>cause==null ? null : cause.toString()</code>, which by default contains the class

-     * and message of <code>cause</code>. This constructor is useful for call sites that just wrap another throwable.

-     * </p>

-     * 

-     * @param cause

-     *            the cause (see {@link #getCause()}). A <code>null</code> value is allowed.

-     */

-    public IOExceptionWithCause(Throwable cause) {

-        super(cause == null ? null : cause.toString());

-        this.initCause(cause);

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.IOException;
+
+/**
+ * Subclasses IOException with the {@link Throwable} constructors missing before Java 6. If you are using Java 6,
+ * consider this class deprecated and use {@link IOException}.
+ * 
+ * @author <a href="http://commons.apache.org/io/">Apache Commons IO</a>
+ * @version $Id$
+ * @since Commons IO 1.4
+ */
+public class IOExceptionWithCause extends IOException {
+
+    /**
+     * Defines the serial version UID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructs a new instance with the given message and cause.
+     * <p>
+     * As specified in {@link Throwable}, the message in the given <code>cause</code> is not used in this instance's
+     * message.
+     * </p>
+     * 
+     * @param message
+     *            the message (see {@link #getMessage()})
+     * @param cause
+     *            the cause (see {@link #getCause()}). A <code>null</code> value is allowed.
+     */
+    public IOExceptionWithCause(String message, Throwable cause) {
+        super(message);
+        this.initCause(cause);
+    }
+
+    /**
+     * Constructs a new instance with the given cause.
+     * <p>
+     * The message is set to <code>cause==null ? null : cause.toString()</code>, which by default contains the class
+     * and message of <code>cause</code>. This constructor is useful for call sites that just wrap another throwable.
+     * </p>
+     * 
+     * @param cause
+     *            the cause (see {@link #getCause()}). A <code>null</code> value is allowed.
+     */
+    public IOExceptionWithCause(Throwable cause) {
+        super(cause == null ? null : cause.toString());
+        this.initCause(cause);
+    }
+
+}
diff --git a/src/org/apache/commons/io/IOUtils.java b/src/org/apache/commons/io/IOUtils.java
index 20dc908..523cfb6 100644
--- a/src/org/apache/commons/io/IOUtils.java
+++ b/src/org/apache/commons/io/IOUtils.java
@@ -1,1277 +1,1277 @@
-/*

- * 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.commons.io;

-

-import java.io.BufferedInputStream;

-import java.io.BufferedReader;

-import java.io.ByteArrayInputStream;

-import java.io.CharArrayWriter;

-import java.io.File;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.InputStreamReader;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.io.PrintWriter;

-import java.io.Reader;

-import java.io.StringWriter;

-import java.io.Writer;

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Iterator;

-import java.util.List;

-

-import org.apache.commons.io.output.ByteArrayOutputStream;

-

-/**

- * General IO stream manipulation utilities.

- * <p>

- * This class provides static utility methods for input/output operations.

- * <ul>

- * <li>closeQuietly - these methods close a stream ignoring nulls and exceptions

- * <li>toXxx/read - these methods read data from a stream

- * <li>write - these methods write data to a stream

- * <li>copy - these methods copy all the data from one stream to another

- * <li>contentEquals - these methods compare the content of two streams

- * </ul>

- * <p>

- * The byte-to-char methods and char-to-byte methods involve a conversion step.

- * Two methods are provided in each case, one that uses the platform default

- * encoding and the other which allows you to specify an encoding. You are

- * encouraged to always specify an encoding because relying on the platform

- * default can lead to unexpected results, for example when moving from

- * development to production.

- * <p>

- * All the methods in this class that read a stream are buffered internally.

- * This means that there is no cause to use a <code>BufferedInputStream</code>

- * or <code>BufferedReader</code>. The default buffer size of 4K has been shown

- * to be efficient in tests.

- * <p>

- * Wherever possible, the methods in this class do <em>not</em> flush or close

- * the stream. This is to avoid making non-portable assumptions about the

- * streams' origin and further use. Thus the caller is still responsible for

- * closing streams after use.

- * <p>

- * Origin of code: Excalibur.

- *

- * @author Peter Donald

- * @author Jeff Turner

- * @author Matthew Hawthorne

- * @author Stephen Colebourne

- * @author Gareth Davis

- * @author Ian Springer

- * @author Niall Pemberton

- * @author Sandy McArthur

- * @version $Id: IOUtils.java 481854 2006-12-03 18:30:07Z scolebourne $

- */

-public class IOUtils {

-    // NOTE: This class is focussed on InputStream, OutputStream, Reader and

-    // Writer. Each method should take at least one of these as a parameter,

-    // or return one of them.

-

-    /**

-     * The Unix directory separator character.

-     */

-    public static final char DIR_SEPARATOR_UNIX = '/';

-    /**

-     * The Windows directory separator character.

-     */

-    public static final char DIR_SEPARATOR_WINDOWS = '\\';

-    /**

-     * The system directory separator character.

-     */

-    public static final char DIR_SEPARATOR = File.separatorChar;

-    /**

-     * The Unix line separator string.

-     */

-    public static final String LINE_SEPARATOR_UNIX = "\n";

-    /**

-     * The Windows line separator string.

-     */

-    public static final String LINE_SEPARATOR_WINDOWS = "\r\n";

-    /**

-     * The system line separator string.

-     */

-    public static final String LINE_SEPARATOR;

-    static {

-        // avoid security issues

-        StringWriter buf = new StringWriter(4);

-        PrintWriter out = new PrintWriter(buf);

-        out.println();

-        LINE_SEPARATOR = buf.toString();

-    }

-

-    /**

-     * The default buffer size to use.

-     */

-    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

-

-    /**

-     * Instances should NOT be constructed in standard programming.

-     */

-    public IOUtils() {

-        super();

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Unconditionally close an <code>Reader</code>.

-     * <p>

-     * Equivalent to {@link Reader#close()}, except any exceptions will be ignored.

-     * This is typically used in finally blocks.

-     *

-     * @param input  the Reader to close, may be null or already closed

-     */

-    public static void closeQuietly(Reader input) {

-        try {

-            if (input != null) {

-                input.close();

-            }

-        } catch (IOException ioe) {

-            // ignore

-        }

-    }

-

-    /**

-     * Unconditionally close a <code>Writer</code>.

-     * <p>

-     * Equivalent to {@link Writer#close()}, except any exceptions will be ignored.

-     * This is typically used in finally blocks.

-     *

-     * @param output  the Writer to close, may be null or already closed

-     */

-    public static void closeQuietly(Writer output) {

-        try {

-            if (output != null) {

-                output.close();

-            }

-        } catch (IOException ioe) {

-            // ignore

-        }

-    }

-

-    /**

-     * Unconditionally close an <code>InputStream</code>.

-     * <p>

-     * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.

-     * This is typically used in finally blocks.

-     *

-     * @param input  the InputStream to close, may be null or already closed

-     */

-    public static void closeQuietly(InputStream input) {

-        try {

-            if (input != null) {

-                input.close();

-            }

-        } catch (IOException ioe) {

-            // ignore

-        }

-    }

-

-    /**

-     * Unconditionally close an <code>OutputStream</code>.

-     * <p>

-     * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.

-     * This is typically used in finally blocks.

-     *

-     * @param output  the OutputStream to close, may be null or already closed

-     */

-    public static void closeQuietly(OutputStream output) {

-        try {

-            if (output != null) {

-                output.close();

-            }

-        } catch (IOException ioe) {

-            // ignore

-        }

-    }

-

-    // read toByteArray

-    //-----------------------------------------------------------------------

-    /**

-     * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @return the requested byte array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static byte[] toByteArray(InputStream input) throws IOException {

-        ByteArrayOutputStream output = new ByteArrayOutputStream();

-        copy(input, output);

-        return output.toByteArray();

-    }

-

-    /**

-     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>

-     * using the default character encoding of the platform.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @return the requested byte array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static byte[] toByteArray(Reader input) throws IOException {

-        ByteArrayOutputStream output = new ByteArrayOutputStream();

-        copy(input, output);

-        return output.toByteArray();

-    }

-

-    /**

-     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @param encoding  the encoding to use, null means platform default

-     * @return the requested byte array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static byte[] toByteArray(Reader input, String encoding)

-            throws IOException {

-        ByteArrayOutputStream output = new ByteArrayOutputStream();

-        copy(input, output, encoding);

-        return output.toByteArray();

-    }

-

-    /**

-     * Get the contents of a <code>String</code> as a <code>byte[]</code>

-     * using the default character encoding of the platform.

-     * <p>

-     * This is the same as {@link String#getBytes()}.

-     *

-     * @param input  the <code>String</code> to convert

-     * @return the requested byte array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs (never occurs)

-     * @deprecated Use {@link String#getBytes()}

-     */

-    @Deprecated

-    public static byte[] toByteArray(String input) throws IOException {

-        return input.getBytes();

-    }

-

-    // read char[]

-    //-----------------------------------------------------------------------

-    /**

-     * Get the contents of an <code>InputStream</code> as a character array

-     * using the default character encoding of the platform.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param is  the <code>InputStream</code> to read from

-     * @return the requested character array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static char[] toCharArray(InputStream is) throws IOException {

-        CharArrayWriter output = new CharArrayWriter();

-        copy(is, output);

-        return output.toCharArray();

-    }

-

-    /**

-     * Get the contents of an <code>InputStream</code> as a character array

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param is  the <code>InputStream</code> to read from

-     * @param encoding  the encoding to use, null means platform default

-     * @return the requested character array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static char[] toCharArray(InputStream is, String encoding)

-            throws IOException {

-        CharArrayWriter output = new CharArrayWriter();

-        copy(is, output, encoding);

-        return output.toCharArray();

-    }

-

-    /**

-     * Get the contents of a <code>Reader</code> as a character array.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @return the requested character array

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static char[] toCharArray(Reader input) throws IOException {

-        CharArrayWriter sw = new CharArrayWriter();

-        copy(input, sw);

-        return sw.toCharArray();

-    }

-

-    // read toString

-    //-----------------------------------------------------------------------

-    /**

-     * Get the contents of an <code>InputStream</code> as a String

-     * using the default character encoding of the platform.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @return the requested String

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static String toString(InputStream input) throws IOException {

-        StringWriter sw = new StringWriter();

-        copy(input, sw);

-        return sw.toString();

-    }

-

-    /**

-     * Get the contents of an <code>InputStream</code> as a String

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @param encoding  the encoding to use, null means platform default

-     * @return the requested String

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static String toString(InputStream input, String encoding)

-            throws IOException {

-        StringWriter sw = new StringWriter();

-        copy(input, sw, encoding);

-        return sw.toString();

-    }

-

-    /**

-     * Get the contents of a <code>Reader</code> as a String.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @return the requested String

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static String toString(Reader input) throws IOException {

-        StringWriter sw = new StringWriter();

-        copy(input, sw);

-        return sw.toString();

-    }

-

-    /**

-     * Get the contents of a <code>byte[]</code> as a String

-     * using the default character encoding of the platform.

-     *

-     * @param input the byte array to read from

-     * @return the requested String

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs (never occurs)

-     * @deprecated Use {@link String#String(byte[])}

-     */

-    @Deprecated

-    public static String toString(byte[] input) throws IOException {

-        return new String(input);

-    }

-

-    /**

-     * Get the contents of a <code>byte[]</code> as a String

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     *

-     * @param input the byte array to read from

-     * @param encoding  the encoding to use, null means platform default

-     * @return the requested String

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs (never occurs)

-     * @deprecated Use {@link String#String(byte[],String)}

-     */

-    @Deprecated

-    public static String toString(byte[] input, String encoding)

-            throws IOException {

-        if (encoding == null) {

-            return new String(input);

-        } else {

-            return new String(input, encoding);

-        }

-    }

-

-    // readLines

-    //-----------------------------------------------------------------------

-    /**

-     * Get the contents of an <code>InputStream</code> as a list of Strings,

-     * one entry per line, using the default character encoding of the platform.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from, not null

-     * @return the list of Strings, never null

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static List<String> readLines(InputStream input) throws IOException {

-        InputStreamReader reader = new InputStreamReader(input);

-        return readLines(reader);

-    }

-

-    /**

-     * Get the contents of an <code>InputStream</code> as a list of Strings,

-     * one entry per line, using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from, not null

-     * @param encoding  the encoding to use, null means platform default

-     * @return the list of Strings, never null

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static List<String> readLines(InputStream input, String encoding) throws IOException {

-        if (encoding == null) {

-            return readLines(input);

-        } else {

-            InputStreamReader reader = new InputStreamReader(input, encoding);

-            return readLines(reader);

-        }

-    }

-

-    /**

-     * Get the contents of a <code>Reader</code> as a list of Strings,

-     * one entry per line.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from, not null

-     * @return the list of Strings, never null

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static List<String> readLines(Reader input) throws IOException {

-        BufferedReader reader = new BufferedReader(input);

-        List<String> list = new ArrayList<String>();

-        String line = reader.readLine();

-        while (line != null) {

-            list.add(line);

-            line = reader.readLine();

-        }

-        return list;

-    }

-

-    // lineIterator

-    //-----------------------------------------------------------------------

-    /**

-     * Return an Iterator for the lines in a <code>Reader</code>.

-     * <p>

-     * <code>LineIterator</code> holds a reference to the open

-     * <code>Reader</code> specified here. When you have finished with the

-     * iterator you should close the reader to free internal resources.

-     * This can be done by closing the reader directly, or by calling

-     * {@link LineIterator#close()} or {@link LineIterator#closeQuietly(LineIterator)}.

-     * <p>

-     * The recommended usage pattern is:

-     * <pre>

-     * try {

-     *   LineIterator it = IOUtils.lineIterator(reader);

-     *   while (it.hasNext()) {

-     *     String line = it.nextLine();

-     *     /// do something with line

-     *   }

-     * } finally {

-     *   IOUtils.closeQuietly(reader);

-     * }

-     * </pre>

-     *

-     * @param reader  the <code>Reader</code> to read from, not null

-     * @return an Iterator of the lines in the reader, never null

-     * @throws IllegalArgumentException if the reader is null

-     * @since Commons IO 1.2

-     */

-    public static LineIterator lineIterator(Reader reader) {

-        return new LineIterator(reader);

-    }

-

-    /**

-     * Return an Iterator for the lines in an <code>InputStream</code>, using

-     * the character encoding specified (or default encoding if null).

-     * <p>

-     * <code>LineIterator</code> holds a reference to the open

-     * <code>InputStream</code> specified here. When you have finished with

-     * the iterator you should close the stream to free internal resources.

-     * This can be done by closing the stream directly, or by calling

-     * {@link LineIterator#close()} or {@link LineIterator#closeQuietly(LineIterator)}.

-     * <p>

-     * The recommended usage pattern is:

-     * <pre>

-     * try {

-     *   LineIterator it = IOUtils.lineIterator(stream, "UTF-8");

-     *   while (it.hasNext()) {

-     *     String line = it.nextLine();

-     *     /// do something with line

-     *   }

-     * } finally {

-     *   IOUtils.closeQuietly(stream);

-     * }

-     * </pre>

-     *

-     * @param input  the <code>InputStream</code> to read from, not null

-     * @param encoding  the encoding to use, null means platform default

-     * @return an Iterator of the lines in the reader, never null

-     * @throws IllegalArgumentException if the input is null

-     * @throws IOException if an I/O error occurs, such as if the encoding is invalid

-     * @since Commons IO 1.2

-     */

-    public static LineIterator lineIterator(InputStream input, String encoding)

-                     throws IOException {

-        Reader reader = null;

-        if (encoding == null) {

-            reader = new InputStreamReader(input);

-        } else {

-            reader = new InputStreamReader(input, encoding);

-        }

-        return new LineIterator(reader);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Convert the specified string to an input stream, encoded as bytes

-     * using the default character encoding of the platform.

-     *

-     * @param input the string to convert

-     * @return an input stream

-     * @since Commons IO 1.1

-     */

-    public static InputStream toInputStream(String input) {

-        byte[] bytes = input.getBytes();

-        return new ByteArrayInputStream(bytes);

-    }

-

-    /**

-     * Convert the specified string to an input stream, encoded as bytes

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     *

-     * @param input the string to convert

-     * @param encoding the encoding to use, null means platform default

-     * @throws IOException if the encoding is invalid

-     * @return an input stream

-     * @since Commons IO 1.1

-     */

-    public static InputStream toInputStream(String input, String encoding) throws IOException {

-        byte[] bytes = encoding != null ? input.getBytes(encoding) : input.getBytes();

-        return new ByteArrayInputStream(bytes);

-    }

-

-    // write byte[]

-    //-----------------------------------------------------------------------

-    /**

-     * Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code>.

-     *

-     * @param data  the byte array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(byte[] data, OutputStream output)

-            throws IOException {

-        if (data != null) {

-            output.write(data);

-        }

-    }

-

-    /**

-     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>

-     * using the default character encoding of the platform.

-     * <p>

-     * This method uses {@link String#String(byte[])}.

-     *

-     * @param data  the byte array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>Writer</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(byte[] data, Writer output) throws IOException {

-        if (data != null) {

-            output.write(new String(data));

-        }

-    }

-

-    /**

-     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>

-     * using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method uses {@link String#String(byte[], String)}.

-     *

-     * @param data  the byte array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>Writer</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(byte[] data, Writer output, String encoding)

-            throws IOException {

-        if (data != null) {

-            if (encoding == null) {

-                write(data, output);

-            } else {

-                output.write(new String(data, encoding));

-            }

-        }

-    }

-

-    // write char[]

-    //-----------------------------------------------------------------------

-    /**

-     * Writes chars from a <code>char[]</code> to a <code>Writer</code>

-     * using the default character encoding of the platform.

-     *

-     * @param data  the char array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>Writer</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(char[] data, Writer output) throws IOException {

-        if (data != null) {

-            output.write(data);

-        }

-    }

-

-    /**

-     * Writes chars from a <code>char[]</code> to bytes on an

-     * <code>OutputStream</code>.

-     * <p>

-     * This method uses {@link String#String(char[])} and

-     * {@link String#getBytes()}.

-     *

-     * @param data  the char array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(char[] data, OutputStream output)

-            throws IOException {

-        if (data != null) {

-            output.write(new String(data).getBytes());

-        }

-    }

-

-    /**

-     * Writes chars from a <code>char[]</code> to bytes on an

-     * <code>OutputStream</code> using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method uses {@link String#String(char[])} and

-     * {@link String#getBytes(String)}.

-     *

-     * @param data  the char array to write, do not modify during output,

-     * null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(char[] data, OutputStream output, String encoding)

-            throws IOException {

-        if (data != null) {

-            if (encoding == null) {

-                write(data, output);

-            } else {

-                output.write(new String(data).getBytes(encoding));

-            }

-        }

-    }

-

-    // write String

-    //-----------------------------------------------------------------------

-    /**

-     * Writes chars from a <code>String</code> to a <code>Writer</code>.

-     *

-     * @param data  the <code>String</code> to write, null ignored

-     * @param output  the <code>Writer</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(String data, Writer output) throws IOException {

-        if (data != null) {

-            output.write(data);

-        }

-    }

-

-    /**

-     * Writes chars from a <code>String</code> to bytes on an

-     * <code>OutputStream</code> using the default character encoding of the

-     * platform.

-     * <p>

-     * This method uses {@link String#getBytes()}.

-     *

-     * @param data  the <code>String</code> to write, null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(String data, OutputStream output)

-            throws IOException {

-        if (data != null) {

-            output.write(data.getBytes());

-        }

-    }

-

-    /**

-     * Writes chars from a <code>String</code> to bytes on an

-     * <code>OutputStream</code> using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method uses {@link String#getBytes(String)}.

-     *

-     * @param data  the <code>String</code> to write, null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(String data, OutputStream output, String encoding)

-            throws IOException {

-        if (data != null) {

-            if (encoding == null) {

-                write(data, output);

-            } else {

-                output.write(data.getBytes(encoding));

-            }

-        }

-    }

-

-    // write StringBuffer

-    //-----------------------------------------------------------------------

-    /**

-     * Writes chars from a <code>StringBuffer</code> to a <code>Writer</code>.

-     *

-     * @param data  the <code>StringBuffer</code> to write, null ignored

-     * @param output  the <code>Writer</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(StringBuffer data, Writer output)

-            throws IOException {

-        if (data != null) {

-            output.write(data.toString());

-        }

-    }

-

-    /**

-     * Writes chars from a <code>StringBuffer</code> to bytes on an

-     * <code>OutputStream</code> using the default character encoding of the

-     * platform.

-     * <p>

-     * This method uses {@link String#getBytes()}.

-     *

-     * @param data  the <code>StringBuffer</code> to write, null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(StringBuffer data, OutputStream output)

-            throws IOException {

-        if (data != null) {

-            output.write(data.toString().getBytes());

-        }

-    }

-

-    /**

-     * Writes chars from a <code>StringBuffer</code> to bytes on an

-     * <code>OutputStream</code> using the specified character encoding.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method uses {@link String#getBytes(String)}.

-     *

-     * @param data  the <code>StringBuffer</code> to write, null ignored

-     * @param output  the <code>OutputStream</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void write(StringBuffer data, OutputStream output,

-            String encoding) throws IOException {

-        if (data != null) {

-            if (encoding == null) {

-                write(data, output);

-            } else {

-                output.write(data.toString().getBytes(encoding));

-            }

-        }

-    }

-

-    // writeLines

-    //-----------------------------------------------------------------------

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * an <code>OutputStream</code> line by line, using the default character

-     * encoding of the platform and the specified line ending.

-     *

-     * @param lines  the lines to write, null entries produce blank lines

-     * @param lineEnding  the line separator to use, null is system default

-     * @param output  the <code>OutputStream</code> to write to, not null, not closed

-     * @throws NullPointerException if the output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void writeLines(Collection<Object> lines, String lineEnding,

-            OutputStream output) throws IOException {

-        if (lines == null) {

-            return;

-        }

-        if (lineEnding == null) {

-            lineEnding = LINE_SEPARATOR;

-        }

-        for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {

-            Object line = it.next();

-            if (line != null) {

-                output.write(line.toString().getBytes());

-            }

-            output.write(lineEnding.getBytes());

-        }

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * an <code>OutputStream</code> line by line, using the specified character

-     * encoding and the specified line ending.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     *

-     * @param lines  the lines to write, null entries produce blank lines

-     * @param lineEnding  the line separator to use, null is system default

-     * @param output  the <code>OutputStream</code> to write to, not null, not closed

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if the output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void writeLines(Collection<Object> lines, String lineEnding,

-            OutputStream output, String encoding) throws IOException {

-        if (encoding == null) {

-            writeLines(lines, lineEnding, output);

-        } else {

-            if (lines == null) {

-                return;

-            }

-            if (lineEnding == null) {

-                lineEnding = LINE_SEPARATOR;

-            }

-            for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {

-                Object line = it.next();

-                if (line != null) {

-                    output.write(line.toString().getBytes(encoding));

-                }

-                output.write(lineEnding.getBytes(encoding));

-            }

-        }

-    }

-

-    /**

-     * Writes the <code>toString()</code> value of each item in a collection to

-     * a <code>Writer</code> line by line, using the specified line ending.

-     *

-     * @param lines  the lines to write, null entries produce blank lines

-     * @param lineEnding  the line separator to use, null is system default

-     * @param writer  the <code>Writer</code> to write to, not null, not closed

-     * @throws NullPointerException if the input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void writeLines(Collection<Object> lines, String lineEnding,

-            Writer writer) throws IOException {

-        if (lines == null) {

-            return;

-        }

-        if (lineEnding == null) {

-            lineEnding = LINE_SEPARATOR;

-        }

-        for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {

-            Object line = it.next();

-            if (line != null) {

-                writer.write(line.toString());

-            }

-            writer.write(lineEnding);

-        }

-    }

-

-    // copy from InputStream

-    //-----------------------------------------------------------------------

-    /**

-     * Copy bytes from an <code>InputStream</code> to an

-     * <code>OutputStream</code>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     * <p>

-     * Large streams (over 2GB) will return a bytes copied value of

-     * <code>-1</code> after the copy has completed since the correct

-     * number of bytes cannot be returned as an int. For large streams

-     * use the <code>copyLarge(InputStream, OutputStream)</code> method.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @param output  the <code>OutputStream</code> to write to

-     * @return the number of bytes copied

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @throws ArithmeticException if the byte count is too large

-     * @since Commons IO 1.1

-     */

-    public static int copy(InputStream input, OutputStream output) throws IOException {

-        long count = copyLarge(input, output);

-        if (count > Integer.MAX_VALUE) {

-            return -1;

-        }

-        return (int) count;

-    }

-

-    /**

-     * Copy bytes from a large (over 2GB) <code>InputStream</code> to an

-     * <code>OutputStream</code>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @param output  the <code>OutputStream</code> to write to

-     * @return the number of bytes copied

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.3

-     */

-    public static long copyLarge(InputStream input, OutputStream output)

-            throws IOException {

-        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

-        long count = 0;

-        int n = 0;

-        while (-1 != (n = input.read(buffer))) {

-            output.write(buffer, 0, n);

-            count += n;

-        }

-        return count;

-    }

-

-    /**

-     * Copy bytes from an <code>InputStream</code> to chars on a

-     * <code>Writer</code> using the default character encoding of the platform.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     * <p>

-     * This method uses {@link InputStreamReader}.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @param output  the <code>Writer</code> to write to

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void copy(InputStream input, Writer output)

-            throws IOException {

-        InputStreamReader in = new InputStreamReader(input);

-        copy(in, output);

-    }

-

-    /**

-     * Copy bytes from an <code>InputStream</code> to chars on a

-     * <code>Writer</code> using the specified character encoding.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedInputStream</code>.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * This method uses {@link InputStreamReader}.

-     *

-     * @param input  the <code>InputStream</code> to read from

-     * @param output  the <code>Writer</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void copy(InputStream input, Writer output, String encoding)

-            throws IOException {

-        if (encoding == null) {

-            copy(input, output);

-        } else {

-            InputStreamReader in = new InputStreamReader(input, encoding);

-            copy(in, output);

-        }

-    }

-

-    // copy from Reader

-    //-----------------------------------------------------------------------

-    /**

-     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     * <p>

-     * Large streams (over 2GB) will return a chars copied value of

-     * <code>-1</code> after the copy has completed since the correct

-     * number of chars cannot be returned as an int. For large streams

-     * use the <code>copyLarge(Reader, Writer)</code> method.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @param output  the <code>Writer</code> to write to

-     * @return the number of characters copied

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @throws ArithmeticException if the character count is too large

-     * @since Commons IO 1.1

-     */

-    public static int copy(Reader input, Writer output) throws IOException {

-        long count = copyLarge(input, output);

-        if (count > Integer.MAX_VALUE) {

-            return -1;

-        }

-        return (int) count;

-    }

-

-    /**

-     * Copy chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @param output  the <code>Writer</code> to write to

-     * @return the number of characters copied

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.3

-     */

-    public static long copyLarge(Reader input, Writer output) throws IOException {

-        char[] buffer = new char[DEFAULT_BUFFER_SIZE];

-        long count = 0;

-        int n = 0;

-        while (-1 != (n = input.read(buffer))) {

-            output.write(buffer, 0, n);

-            count += n;

-        }

-        return count;

-    }

-

-    /**

-     * Copy chars from a <code>Reader</code> to bytes on an

-     * <code>OutputStream</code> using the default character encoding of the

-     * platform, and calling flush.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     * <p>

-     * Due to the implementation of OutputStreamWriter, this method performs a

-     * flush.

-     * <p>

-     * This method uses {@link OutputStreamWriter}.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @param output  the <code>OutputStream</code> to write to

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void copy(Reader input, OutputStream output)

-            throws IOException {

-        OutputStreamWriter out = new OutputStreamWriter(output);

-        copy(input, out);

-        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we

-        // have to flush here.

-        out.flush();

-    }

-

-    /**

-     * Copy chars from a <code>Reader</code> to bytes on an

-     * <code>OutputStream</code> using the specified character encoding, and

-     * calling flush.

-     * <p>

-     * This method buffers the input internally, so there is no need to use a

-     * <code>BufferedReader</code>.

-     * <p>

-     * Character encoding names can be found at

-     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.

-     * <p>

-     * Due to the implementation of OutputStreamWriter, this method performs a

-     * flush.

-     * <p>

-     * This method uses {@link OutputStreamWriter}.

-     *

-     * @param input  the <code>Reader</code> to read from

-     * @param output  the <code>OutputStream</code> to write to

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if the input or output is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static void copy(Reader input, OutputStream output, String encoding)

-            throws IOException {

-        if (encoding == null) {

-            copy(input, output);

-        } else {

-            OutputStreamWriter out = new OutputStreamWriter(output, encoding);

-            copy(input, out);

-            // XXX Unless anyone is planning on rewriting OutputStreamWriter,

-            // we have to flush here.

-            out.flush();

-        }

-    }

-

-    // content equals

-    //-----------------------------------------------------------------------

-    /**

-     * Compare the contents of two Streams to determine if they are equal or

-     * not.

-     * <p>

-     * This method buffers the input internally using

-     * <code>BufferedInputStream</code> if they are not already buffered.

-     *

-     * @param input1  the first stream

-     * @param input2  the second stream

-     * @return true if the content of the streams are equal or they both don't

-     * exist, false otherwise

-     * @throws NullPointerException if either input is null

-     * @throws IOException if an I/O error occurs

-     */

-    public static boolean contentEquals(InputStream input1, InputStream input2)

-            throws IOException {

-        if (!(input1 instanceof BufferedInputStream)) {

-            input1 = new BufferedInputStream(input1);

-        }

-        if (!(input2 instanceof BufferedInputStream)) {

-            input2 = new BufferedInputStream(input2);

-        }

-

-        int ch = input1.read();

-        while (-1 != ch) {

-            int ch2 = input2.read();

-            if (ch != ch2) {

-                return false;

-            }

-            ch = input1.read();

-        }

-

-        int ch2 = input2.read();

-        return (ch2 == -1);

-    }

-

-    /**

-     * Compare the contents of two Readers to determine if they are equal or

-     * not.

-     * <p>

-     * This method buffers the input internally using

-     * <code>BufferedReader</code> if they are not already buffered.

-     *

-     * @param input1  the first reader

-     * @param input2  the second reader

-     * @return true if the content of the readers are equal or they both don't

-     * exist, false otherwise

-     * @throws NullPointerException if either input is null

-     * @throws IOException if an I/O error occurs

-     * @since Commons IO 1.1

-     */

-    public static boolean contentEquals(Reader input1, Reader input2)

-            throws IOException {

-        if (!(input1 instanceof BufferedReader)) {

-            input1 = new BufferedReader(input1);

-        }

-        if (!(input2 instanceof BufferedReader)) {

-            input2 = new BufferedReader(input2);

-        }

-

-        int ch = input1.read();

-        while (-1 != ch) {

-            int ch2 = input2.read();

-            if (ch != ch2) {

-                return false;

-            }

-            ch = input1.read();

-        }

-

-        int ch2 = input2.read();

-        return (ch2 == -1);

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+/**
+ * General IO stream manipulation utilities.
+ * <p>
+ * This class provides static utility methods for input/output operations.
+ * <ul>
+ * <li>closeQuietly - these methods close a stream ignoring nulls and exceptions
+ * <li>toXxx/read - these methods read data from a stream
+ * <li>write - these methods write data to a stream
+ * <li>copy - these methods copy all the data from one stream to another
+ * <li>contentEquals - these methods compare the content of two streams
+ * </ul>
+ * <p>
+ * The byte-to-char methods and char-to-byte methods involve a conversion step.
+ * Two methods are provided in each case, one that uses the platform default
+ * encoding and the other which allows you to specify an encoding. You are
+ * encouraged to always specify an encoding because relying on the platform
+ * default can lead to unexpected results, for example when moving from
+ * development to production.
+ * <p>
+ * All the methods in this class that read a stream are buffered internally.
+ * This means that there is no cause to use a <code>BufferedInputStream</code>
+ * or <code>BufferedReader</code>. The default buffer size of 4K has been shown
+ * to be efficient in tests.
+ * <p>
+ * Wherever possible, the methods in this class do <em>not</em> flush or close
+ * the stream. This is to avoid making non-portable assumptions about the
+ * streams' origin and further use. Thus the caller is still responsible for
+ * closing streams after use.
+ * <p>
+ * Origin of code: Excalibur.
+ *
+ * @author Peter Donald
+ * @author Jeff Turner
+ * @author Matthew Hawthorne
+ * @author Stephen Colebourne
+ * @author Gareth Davis
+ * @author Ian Springer
+ * @author Niall Pemberton
+ * @author Sandy McArthur
+ * @version $Id: IOUtils.java 481854 2006-12-03 18:30:07Z scolebourne $
+ */
+public class IOUtils {
+    // NOTE: This class is focussed on InputStream, OutputStream, Reader and
+    // Writer. Each method should take at least one of these as a parameter,
+    // or return one of them.
+
+    /**
+     * The Unix directory separator character.
+     */
+    public static final char DIR_SEPARATOR_UNIX = '/';
+    /**
+     * The Windows directory separator character.
+     */
+    public static final char DIR_SEPARATOR_WINDOWS = '\\';
+    /**
+     * The system directory separator character.
+     */
+    public static final char DIR_SEPARATOR = File.separatorChar;
+    /**
+     * The Unix line separator string.
+     */
+    public static final String LINE_SEPARATOR_UNIX = "\n";
+    /**
+     * The Windows line separator string.
+     */
+    public static final String LINE_SEPARATOR_WINDOWS = "\r\n";
+    /**
+     * The system line separator string.
+     */
+    public static final String LINE_SEPARATOR;
+    static {
+        // avoid security issues
+        StringWriter buf = new StringWriter(4);
+        PrintWriter out = new PrintWriter(buf);
+        out.println();
+        LINE_SEPARATOR = buf.toString();
+    }
+
+    /**
+     * The default buffer size to use.
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public IOUtils() {
+        super();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Unconditionally close an <code>Reader</code>.
+     * <p>
+     * Equivalent to {@link Reader#close()}, except any exceptions will be ignored.
+     * This is typically used in finally blocks.
+     *
+     * @param input  the Reader to close, may be null or already closed
+     */
+    public static void closeQuietly(Reader input) {
+        try {
+            if (input != null) {
+                input.close();
+            }
+        } catch (IOException ioe) {
+            // ignore
+        }
+    }
+
+    /**
+     * Unconditionally close a <code>Writer</code>.
+     * <p>
+     * Equivalent to {@link Writer#close()}, except any exceptions will be ignored.
+     * This is typically used in finally blocks.
+     *
+     * @param output  the Writer to close, may be null or already closed
+     */
+    public static void closeQuietly(Writer output) {
+        try {
+            if (output != null) {
+                output.close();
+            }
+        } catch (IOException ioe) {
+            // ignore
+        }
+    }
+
+    /**
+     * Unconditionally close an <code>InputStream</code>.
+     * <p>
+     * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
+     * This is typically used in finally blocks.
+     *
+     * @param input  the InputStream to close, may be null or already closed
+     */
+    public static void closeQuietly(InputStream input) {
+        try {
+            if (input != null) {
+                input.close();
+            }
+        } catch (IOException ioe) {
+            // ignore
+        }
+    }
+
+    /**
+     * Unconditionally close an <code>OutputStream</code>.
+     * <p>
+     * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
+     * This is typically used in finally blocks.
+     *
+     * @param output  the OutputStream to close, may be null or already closed
+     */
+    public static void closeQuietly(OutputStream output) {
+        try {
+            if (output != null) {
+                output.close();
+            }
+        } catch (IOException ioe) {
+            // ignore
+        }
+    }
+
+    // read toByteArray
+    //-----------------------------------------------------------------------
+    /**
+     * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static byte[] toByteArray(InputStream input) throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy(input, output);
+        return output.toByteArray();
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>
+     * using the default character encoding of the platform.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static byte[] toByteArray(Reader input) throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy(input, output);
+        return output.toByteArray();
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @param encoding  the encoding to use, null means platform default
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static byte[] toByteArray(Reader input, String encoding)
+            throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy(input, output, encoding);
+        return output.toByteArray();
+    }
+
+    /**
+     * Get the contents of a <code>String</code> as a <code>byte[]</code>
+     * using the default character encoding of the platform.
+     * <p>
+     * This is the same as {@link String#getBytes()}.
+     *
+     * @param input  the <code>String</code> to convert
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs (never occurs)
+     * @deprecated Use {@link String#getBytes()}
+     */
+    @Deprecated
+    public static byte[] toByteArray(String input) throws IOException {
+        return input.getBytes();
+    }
+
+    // read char[]
+    //-----------------------------------------------------------------------
+    /**
+     * Get the contents of an <code>InputStream</code> as a character array
+     * using the default character encoding of the platform.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param is  the <code>InputStream</code> to read from
+     * @return the requested character array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static char[] toCharArray(InputStream is) throws IOException {
+        CharArrayWriter output = new CharArrayWriter();
+        copy(is, output);
+        return output.toCharArray();
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a character array
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param is  the <code>InputStream</code> to read from
+     * @param encoding  the encoding to use, null means platform default
+     * @return the requested character array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static char[] toCharArray(InputStream is, String encoding)
+            throws IOException {
+        CharArrayWriter output = new CharArrayWriter();
+        copy(is, output, encoding);
+        return output.toCharArray();
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a character array.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @return the requested character array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static char[] toCharArray(Reader input) throws IOException {
+        CharArrayWriter sw = new CharArrayWriter();
+        copy(input, sw);
+        return sw.toCharArray();
+    }
+
+    // read toString
+    //-----------------------------------------------------------------------
+    /**
+     * Get the contents of an <code>InputStream</code> as a String
+     * using the default character encoding of the platform.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static String toString(InputStream input) throws IOException {
+        StringWriter sw = new StringWriter();
+        copy(input, sw);
+        return sw.toString();
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a String
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param encoding  the encoding to use, null means platform default
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static String toString(InputStream input, String encoding)
+            throws IOException {
+        StringWriter sw = new StringWriter();
+        copy(input, sw, encoding);
+        return sw.toString();
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a String.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static String toString(Reader input) throws IOException {
+        StringWriter sw = new StringWriter();
+        copy(input, sw);
+        return sw.toString();
+    }
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String
+     * using the default character encoding of the platform.
+     *
+     * @param input the byte array to read from
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs (never occurs)
+     * @deprecated Use {@link String#String(byte[])}
+     */
+    @Deprecated
+    public static String toString(byte[] input) throws IOException {
+        return new String(input);
+    }
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     *
+     * @param input the byte array to read from
+     * @param encoding  the encoding to use, null means platform default
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs (never occurs)
+     * @deprecated Use {@link String#String(byte[],String)}
+     */
+    @Deprecated
+    public static String toString(byte[] input, String encoding)
+            throws IOException {
+        if (encoding == null) {
+            return new String(input);
+        } else {
+            return new String(input, encoding);
+        }
+    }
+
+    // readLines
+    //-----------------------------------------------------------------------
+    /**
+     * Get the contents of an <code>InputStream</code> as a list of Strings,
+     * one entry per line, using the default character encoding of the platform.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from, not null
+     * @return the list of Strings, never null
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static List<String> readLines(InputStream input) throws IOException {
+        InputStreamReader reader = new InputStreamReader(input);
+        return readLines(reader);
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a list of Strings,
+     * one entry per line, using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from, not null
+     * @param encoding  the encoding to use, null means platform default
+     * @return the list of Strings, never null
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static List<String> readLines(InputStream input, String encoding) throws IOException {
+        if (encoding == null) {
+            return readLines(input);
+        } else {
+            InputStreamReader reader = new InputStreamReader(input, encoding);
+            return readLines(reader);
+        }
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a list of Strings,
+     * one entry per line.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from, not null
+     * @return the list of Strings, never null
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static List<String> readLines(Reader input) throws IOException {
+        BufferedReader reader = new BufferedReader(input);
+        List<String> list = new ArrayList<String>();
+        String line = reader.readLine();
+        while (line != null) {
+            list.add(line);
+            line = reader.readLine();
+        }
+        return list;
+    }
+
+    // lineIterator
+    //-----------------------------------------------------------------------
+    /**
+     * Return an Iterator for the lines in a <code>Reader</code>.
+     * <p>
+     * <code>LineIterator</code> holds a reference to the open
+     * <code>Reader</code> specified here. When you have finished with the
+     * iterator you should close the reader to free internal resources.
+     * This can be done by closing the reader directly, or by calling
+     * {@link LineIterator#close()} or {@link LineIterator#closeQuietly(LineIterator)}.
+     * <p>
+     * The recommended usage pattern is:
+     * <pre>
+     * try {
+     *   LineIterator it = IOUtils.lineIterator(reader);
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   IOUtils.closeQuietly(reader);
+     * }
+     * </pre>
+     *
+     * @param reader  the <code>Reader</code> to read from, not null
+     * @return an Iterator of the lines in the reader, never null
+     * @throws IllegalArgumentException if the reader is null
+     * @since Commons IO 1.2
+     */
+    public static LineIterator lineIterator(Reader reader) {
+        return new LineIterator(reader);
+    }
+
+    /**
+     * Return an Iterator for the lines in an <code>InputStream</code>, using
+     * the character encoding specified (or default encoding if null).
+     * <p>
+     * <code>LineIterator</code> holds a reference to the open
+     * <code>InputStream</code> specified here. When you have finished with
+     * the iterator you should close the stream to free internal resources.
+     * This can be done by closing the stream directly, or by calling
+     * {@link LineIterator#close()} or {@link LineIterator#closeQuietly(LineIterator)}.
+     * <p>
+     * The recommended usage pattern is:
+     * <pre>
+     * try {
+     *   LineIterator it = IOUtils.lineIterator(stream, "UTF-8");
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   IOUtils.closeQuietly(stream);
+     * }
+     * </pre>
+     *
+     * @param input  the <code>InputStream</code> to read from, not null
+     * @param encoding  the encoding to use, null means platform default
+     * @return an Iterator of the lines in the reader, never null
+     * @throws IllegalArgumentException if the input is null
+     * @throws IOException if an I/O error occurs, such as if the encoding is invalid
+     * @since Commons IO 1.2
+     */
+    public static LineIterator lineIterator(InputStream input, String encoding)
+                     throws IOException {
+        Reader reader = null;
+        if (encoding == null) {
+            reader = new InputStreamReader(input);
+        } else {
+            reader = new InputStreamReader(input, encoding);
+        }
+        return new LineIterator(reader);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Convert the specified string to an input stream, encoded as bytes
+     * using the default character encoding of the platform.
+     *
+     * @param input the string to convert
+     * @return an input stream
+     * @since Commons IO 1.1
+     */
+    public static InputStream toInputStream(String input) {
+        byte[] bytes = input.getBytes();
+        return new ByteArrayInputStream(bytes);
+    }
+
+    /**
+     * Convert the specified string to an input stream, encoded as bytes
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     *
+     * @param input the string to convert
+     * @param encoding the encoding to use, null means platform default
+     * @throws IOException if the encoding is invalid
+     * @return an input stream
+     * @since Commons IO 1.1
+     */
+    public static InputStream toInputStream(String input, String encoding) throws IOException {
+        byte[] bytes = encoding != null ? input.getBytes(encoding) : input.getBytes();
+        return new ByteArrayInputStream(bytes);
+    }
+
+    // write byte[]
+    //-----------------------------------------------------------------------
+    /**
+     * Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
+     *
+     * @param data  the byte array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(byte[] data, OutputStream output)
+            throws IOException {
+        if (data != null) {
+            output.write(data);
+        }
+    }
+
+    /**
+     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
+     * using the default character encoding of the platform.
+     * <p>
+     * This method uses {@link String#String(byte[])}.
+     *
+     * @param data  the byte array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>Writer</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(byte[] data, Writer output) throws IOException {
+        if (data != null) {
+            output.write(new String(data));
+        }
+    }
+
+    /**
+     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
+     * using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method uses {@link String#String(byte[], String)}.
+     *
+     * @param data  the byte array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>Writer</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(byte[] data, Writer output, String encoding)
+            throws IOException {
+        if (data != null) {
+            if (encoding == null) {
+                write(data, output);
+            } else {
+                output.write(new String(data, encoding));
+            }
+        }
+    }
+
+    // write char[]
+    //-----------------------------------------------------------------------
+    /**
+     * Writes chars from a <code>char[]</code> to a <code>Writer</code>
+     * using the default character encoding of the platform.
+     *
+     * @param data  the char array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>Writer</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(char[] data, Writer output) throws IOException {
+        if (data != null) {
+            output.write(data);
+        }
+    }
+
+    /**
+     * Writes chars from a <code>char[]</code> to bytes on an
+     * <code>OutputStream</code>.
+     * <p>
+     * This method uses {@link String#String(char[])} and
+     * {@link String#getBytes()}.
+     *
+     * @param data  the char array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(char[] data, OutputStream output)
+            throws IOException {
+        if (data != null) {
+            output.write(new String(data).getBytes());
+        }
+    }
+
+    /**
+     * Writes chars from a <code>char[]</code> to bytes on an
+     * <code>OutputStream</code> using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method uses {@link String#String(char[])} and
+     * {@link String#getBytes(String)}.
+     *
+     * @param data  the char array to write, do not modify during output,
+     * null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(char[] data, OutputStream output, String encoding)
+            throws IOException {
+        if (data != null) {
+            if (encoding == null) {
+                write(data, output);
+            } else {
+                output.write(new String(data).getBytes(encoding));
+            }
+        }
+    }
+
+    // write String
+    //-----------------------------------------------------------------------
+    /**
+     * Writes chars from a <code>String</code> to a <code>Writer</code>.
+     *
+     * @param data  the <code>String</code> to write, null ignored
+     * @param output  the <code>Writer</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(String data, Writer output) throws IOException {
+        if (data != null) {
+            output.write(data);
+        }
+    }
+
+    /**
+     * Writes chars from a <code>String</code> to bytes on an
+     * <code>OutputStream</code> using the default character encoding of the
+     * platform.
+     * <p>
+     * This method uses {@link String#getBytes()}.
+     *
+     * @param data  the <code>String</code> to write, null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(String data, OutputStream output)
+            throws IOException {
+        if (data != null) {
+            output.write(data.getBytes());
+        }
+    }
+
+    /**
+     * Writes chars from a <code>String</code> to bytes on an
+     * <code>OutputStream</code> using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method uses {@link String#getBytes(String)}.
+     *
+     * @param data  the <code>String</code> to write, null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(String data, OutputStream output, String encoding)
+            throws IOException {
+        if (data != null) {
+            if (encoding == null) {
+                write(data, output);
+            } else {
+                output.write(data.getBytes(encoding));
+            }
+        }
+    }
+
+    // write StringBuffer
+    //-----------------------------------------------------------------------
+    /**
+     * Writes chars from a <code>StringBuffer</code> to a <code>Writer</code>.
+     *
+     * @param data  the <code>StringBuffer</code> to write, null ignored
+     * @param output  the <code>Writer</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(StringBuffer data, Writer output)
+            throws IOException {
+        if (data != null) {
+            output.write(data.toString());
+        }
+    }
+
+    /**
+     * Writes chars from a <code>StringBuffer</code> to bytes on an
+     * <code>OutputStream</code> using the default character encoding of the
+     * platform.
+     * <p>
+     * This method uses {@link String#getBytes()}.
+     *
+     * @param data  the <code>StringBuffer</code> to write, null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(StringBuffer data, OutputStream output)
+            throws IOException {
+        if (data != null) {
+            output.write(data.toString().getBytes());
+        }
+    }
+
+    /**
+     * Writes chars from a <code>StringBuffer</code> to bytes on an
+     * <code>OutputStream</code> using the specified character encoding.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method uses {@link String#getBytes(String)}.
+     *
+     * @param data  the <code>StringBuffer</code> to write, null ignored
+     * @param output  the <code>OutputStream</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void write(StringBuffer data, OutputStream output,
+            String encoding) throws IOException {
+        if (data != null) {
+            if (encoding == null) {
+                write(data, output);
+            } else {
+                output.write(data.toString().getBytes(encoding));
+            }
+        }
+    }
+
+    // writeLines
+    //-----------------------------------------------------------------------
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * an <code>OutputStream</code> line by line, using the default character
+     * encoding of the platform and the specified line ending.
+     *
+     * @param lines  the lines to write, null entries produce blank lines
+     * @param lineEnding  the line separator to use, null is system default
+     * @param output  the <code>OutputStream</code> to write to, not null, not closed
+     * @throws NullPointerException if the output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void writeLines(Collection<Object> lines, String lineEnding,
+            OutputStream output) throws IOException {
+        if (lines == null) {
+            return;
+        }
+        if (lineEnding == null) {
+            lineEnding = LINE_SEPARATOR;
+        }
+        for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+            Object line = it.next();
+            if (line != null) {
+                output.write(line.toString().getBytes());
+            }
+            output.write(lineEnding.getBytes());
+        }
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * an <code>OutputStream</code> line by line, using the specified character
+     * encoding and the specified line ending.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     *
+     * @param lines  the lines to write, null entries produce blank lines
+     * @param lineEnding  the line separator to use, null is system default
+     * @param output  the <code>OutputStream</code> to write to, not null, not closed
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if the output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void writeLines(Collection<Object> lines, String lineEnding,
+            OutputStream output, String encoding) throws IOException {
+        if (encoding == null) {
+            writeLines(lines, lineEnding, output);
+        } else {
+            if (lines == null) {
+                return;
+            }
+            if (lineEnding == null) {
+                lineEnding = LINE_SEPARATOR;
+            }
+            for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+                Object line = it.next();
+                if (line != null) {
+                    output.write(line.toString().getBytes(encoding));
+                }
+                output.write(lineEnding.getBytes(encoding));
+            }
+        }
+    }
+
+    /**
+     * Writes the <code>toString()</code> value of each item in a collection to
+     * a <code>Writer</code> line by line, using the specified line ending.
+     *
+     * @param lines  the lines to write, null entries produce blank lines
+     * @param lineEnding  the line separator to use, null is system default
+     * @param writer  the <code>Writer</code> to write to, not null, not closed
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void writeLines(Collection<Object> lines, String lineEnding,
+            Writer writer) throws IOException {
+        if (lines == null) {
+            return;
+        }
+        if (lineEnding == null) {
+            lineEnding = LINE_SEPARATOR;
+        }
+        for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+            Object line = it.next();
+            if (line != null) {
+                writer.write(line.toString());
+            }
+            writer.write(lineEnding);
+        }
+    }
+
+    // copy from InputStream
+    //-----------------------------------------------------------------------
+    /**
+     * Copy bytes from an <code>InputStream</code> to an
+     * <code>OutputStream</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     * <p>
+     * Large streams (over 2GB) will return a bytes copied value of
+     * <code>-1</code> after the copy has completed since the correct
+     * number of bytes cannot be returned as an int. For large streams
+     * use the <code>copyLarge(InputStream, OutputStream)</code> method.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param output  the <code>OutputStream</code> to write to
+     * @return the number of bytes copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @throws ArithmeticException if the byte count is too large
+     * @since Commons IO 1.1
+     */
+    public static int copy(InputStream input, OutputStream output) throws IOException {
+        long count = copyLarge(input, output);
+        if (count > Integer.MAX_VALUE) {
+            return -1;
+        }
+        return (int) count;
+    }
+
+    /**
+     * Copy bytes from a large (over 2GB) <code>InputStream</code> to an
+     * <code>OutputStream</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param output  the <code>OutputStream</code> to write to
+     * @return the number of bytes copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.3
+     */
+    public static long copyLarge(InputStream input, OutputStream output)
+            throws IOException {
+        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+        long count = 0;
+        int n = 0;
+        while (-1 != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code> using the default character encoding of the platform.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     * <p>
+     * This method uses {@link InputStreamReader}.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param output  the <code>Writer</code> to write to
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void copy(InputStream input, Writer output)
+            throws IOException {
+        InputStreamReader in = new InputStreamReader(input);
+        copy(in, output);
+    }
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code> using the specified character encoding.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * This method uses {@link InputStreamReader}.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param output  the <code>Writer</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void copy(InputStream input, Writer output, String encoding)
+            throws IOException {
+        if (encoding == null) {
+            copy(input, output);
+        } else {
+            InputStreamReader in = new InputStreamReader(input, encoding);
+            copy(in, output);
+        }
+    }
+
+    // copy from Reader
+    //-----------------------------------------------------------------------
+    /**
+     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p>
+     * Large streams (over 2GB) will return a chars copied value of
+     * <code>-1</code> after the copy has completed since the correct
+     * number of chars cannot be returned as an int. For large streams
+     * use the <code>copyLarge(Reader, Writer)</code> method.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @param output  the <code>Writer</code> to write to
+     * @return the number of characters copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @throws ArithmeticException if the character count is too large
+     * @since Commons IO 1.1
+     */
+    public static int copy(Reader input, Writer output) throws IOException {
+        long count = copyLarge(input, output);
+        if (count > Integer.MAX_VALUE) {
+            return -1;
+        }
+        return (int) count;
+    }
+
+    /**
+     * Copy chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @param output  the <code>Writer</code> to write to
+     * @return the number of characters copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.3
+     */
+    public static long copyLarge(Reader input, Writer output) throws IOException {
+        char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+        long count = 0;
+        int n = 0;
+        while (-1 != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    /**
+     * Copy chars from a <code>Reader</code> to bytes on an
+     * <code>OutputStream</code> using the default character encoding of the
+     * platform, and calling flush.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p>
+     * Due to the implementation of OutputStreamWriter, this method performs a
+     * flush.
+     * <p>
+     * This method uses {@link OutputStreamWriter}.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @param output  the <code>OutputStream</code> to write to
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void copy(Reader input, OutputStream output)
+            throws IOException {
+        OutputStreamWriter out = new OutputStreamWriter(output);
+        copy(input, out);
+        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
+        // have to flush here.
+        out.flush();
+    }
+
+    /**
+     * Copy chars from a <code>Reader</code> to bytes on an
+     * <code>OutputStream</code> using the specified character encoding, and
+     * calling flush.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p>
+     * Character encoding names can be found at
+     * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+     * <p>
+     * Due to the implementation of OutputStreamWriter, this method performs a
+     * flush.
+     * <p>
+     * This method uses {@link OutputStreamWriter}.
+     *
+     * @param input  the <code>Reader</code> to read from
+     * @param output  the <code>OutputStream</code> to write to
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static void copy(Reader input, OutputStream output, String encoding)
+            throws IOException {
+        if (encoding == null) {
+            copy(input, output);
+        } else {
+            OutputStreamWriter out = new OutputStreamWriter(output, encoding);
+            copy(input, out);
+            // XXX Unless anyone is planning on rewriting OutputStreamWriter,
+            // we have to flush here.
+            out.flush();
+        }
+    }
+
+    // content equals
+    //-----------------------------------------------------------------------
+    /**
+     * Compare the contents of two Streams to determine if they are equal or
+     * not.
+     * <p>
+     * This method buffers the input internally using
+     * <code>BufferedInputStream</code> if they are not already buffered.
+     *
+     * @param input1  the first stream
+     * @param input2  the second stream
+     * @return true if the content of the streams are equal or they both don't
+     * exist, false otherwise
+     * @throws NullPointerException if either input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static boolean contentEquals(InputStream input1, InputStream input2)
+            throws IOException {
+        if (!(input1 instanceof BufferedInputStream)) {
+            input1 = new BufferedInputStream(input1);
+        }
+        if (!(input2 instanceof BufferedInputStream)) {
+            input2 = new BufferedInputStream(input2);
+        }
+
+        int ch = input1.read();
+        while (-1 != ch) {
+            int ch2 = input2.read();
+            if (ch != ch2) {
+                return false;
+            }
+            ch = input1.read();
+        }
+
+        int ch2 = input2.read();
+        return (ch2 == -1);
+    }
+
+    /**
+     * Compare the contents of two Readers to determine if they are equal or
+     * not.
+     * <p>
+     * This method buffers the input internally using
+     * <code>BufferedReader</code> if they are not already buffered.
+     *
+     * @param input1  the first reader
+     * @param input2  the second reader
+     * @return true if the content of the readers are equal or they both don't
+     * exist, false otherwise
+     * @throws NullPointerException if either input is null
+     * @throws IOException if an I/O error occurs
+     * @since Commons IO 1.1
+     */
+    public static boolean contentEquals(Reader input1, Reader input2)
+            throws IOException {
+        if (!(input1 instanceof BufferedReader)) {
+            input1 = new BufferedReader(input1);
+        }
+        if (!(input2 instanceof BufferedReader)) {
+            input2 = new BufferedReader(input2);
+        }
+
+        int ch = input1.read();
+        while (-1 != ch) {
+            int ch2 = input2.read();
+            if (ch != ch2) {
+                return false;
+            }
+            ch = input1.read();
+        }
+
+        int ch2 = input2.read();
+        return (ch2 == -1);
+    }
+
+}
diff --git a/src/org/apache/commons/io/LineIterator.java b/src/org/apache/commons/io/LineIterator.java
index eac47d2..2bec43b 100644
--- a/src/org/apache/commons/io/LineIterator.java
+++ b/src/org/apache/commons/io/LineIterator.java
@@ -1,181 +1,181 @@
-/*

- * 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.commons.io;

-

-import java.io.BufferedReader;

-import java.io.IOException;

-import java.io.Reader;

-import java.util.Iterator;

-import java.util.NoSuchElementException;

-

-/**

- * An Iterator over the lines in a <code>Reader</code>.

- * <p>

- * <code>LineIterator</code> holds a reference to an open <code>Reader</code>.

- * When you have finished with the iterator you should close the reader

- * to free internal resources. This can be done by closing the reader directly,

- * or by calling the {@link #close()} or {@link #closeQuietly(LineIterator)}

- * method on the iterator.

- * <p>

- * The recommended usage pattern is:

- * <pre>

- * LineIterator it = FileUtils.lineIterator(file, "UTF-8");

- * try {

- *   while (it.hasNext()) {

- *     String line = it.nextLine();

- *     /// do something with line

- *   }

- * } finally {

- *   LineIterator.closeQuietly(iterator);

- * }

- * </pre>

- *

- * @author Niall Pemberton

- * @author Stephen Colebourne

- * @author Sandy McArthur

- * @version $Id: LineIterator.java 437567 2006-08-28 06:39:07Z bayard $

- * @since Commons IO 1.2

- */

-public class LineIterator implements Iterator {

-

-    /** The reader that is being read. */

-    private final BufferedReader bufferedReader;

-    /** The current line. */

-    private String cachedLine;

-    /** A flag indicating if the iterator has been fully read. */

-    private boolean finished = false;

-

-    /**

-     * Constructs an iterator of the lines for a <code>Reader</code>.

-     *

-     * @param reader the <code>Reader</code> to read from, not null

-     * @throws IllegalArgumentException if the reader is null

-     */

-    public LineIterator(final Reader reader) throws IllegalArgumentException {

-        if (reader == null) {

-            throw new IllegalArgumentException("Reader must not be null");

-        }

-        if (reader instanceof BufferedReader) {

-            bufferedReader = (BufferedReader) reader;

-        } else {

-            bufferedReader = new BufferedReader(reader);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Indicates whether the <code>Reader</code> has more lines.

-     * If there is an <code>IOException</code> then {@link #close()} will

-     * be called on this instance.

-     *

-     * @return <code>true</code> if the Reader has more lines

-     * @throws IllegalStateException if an IO exception occurs

-     */

-    public boolean hasNext() {

-        if (cachedLine != null) {

-            return true;

-        } else if (finished) {

-            return false;

-        } else {

-            try {

-                while (true) {

-                    String line = bufferedReader.readLine();

-                    if (line == null) {

-                        finished = true;

-                        return false;

-                    } else if (isValidLine(line)) {

-                        cachedLine = line;

-                        return true;

-                    }

-                }

-            } catch(IOException ioe) {

-                close();

-                throw new IllegalStateException(ioe.toString());

-            }

-        }

-    }

-

-    /**

-     * Overridable method to validate each line that is returned.

-     *

-     * @param line  the line that is to be validated

-     * @return true if valid, false to remove from the iterator

-     */

-    protected boolean isValidLine(String line) {

-        return true;

-    }

-

-    /**

-     * Returns the next line in the wrapped <code>Reader</code>.

-     *

-     * @return the next line from the input

-     * @throws NoSuchElementException if there is no line to return

-     */

-    public Object next() {

-        return nextLine();

-    }

-

-    /**

-     * Returns the next line in the wrapped <code>Reader</code>.

-     *

-     * @return the next line from the input

-     * @throws NoSuchElementException if there is no line to return

-     */

-    public String nextLine() {

-        if (!hasNext()) {

-            throw new NoSuchElementException("No more lines");

-        }

-        String currentLine = cachedLine;

-        cachedLine = null;

-        return currentLine;        

-    }

-

-    /**

-     * Closes the underlying <code>Reader</code> quietly.

-     * This method is useful if you only want to process the first few

-     * lines of a larger file. If you do not close the iterator

-     * then the <code>Reader</code> remains open.

-     * This method can safely be called multiple times.

-     */

-    public void close() {

-        finished = true;

-        IOUtils.closeQuietly(bufferedReader);

-        cachedLine = null;

-    }

-

-    /**

-     * Unsupported.

-     *

-     * @throws UnsupportedOperationException always

-     */

-    public void remove() {

-        throw new UnsupportedOperationException("Remove unsupported on LineIterator");

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Closes the iterator, handling null and ignoring exceptions.

-     *

-     * @param iterator  the iterator to close

-     */

-    public static void closeQuietly(LineIterator iterator) {

-        if (iterator != null) {

-            iterator.close();

-        }

-    }

-

-}

+/*
+ * 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.commons.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * An Iterator over the lines in a <code>Reader</code>.
+ * <p>
+ * <code>LineIterator</code> holds a reference to an open <code>Reader</code>.
+ * When you have finished with the iterator you should close the reader
+ * to free internal resources. This can be done by closing the reader directly,
+ * or by calling the {@link #close()} or {@link #closeQuietly(LineIterator)}
+ * method on the iterator.
+ * <p>
+ * The recommended usage pattern is:
+ * <pre>
+ * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
+ * try {
+ *   while (it.hasNext()) {
+ *     String line = it.nextLine();
+ *     /// do something with line
+ *   }
+ * } finally {
+ *   LineIterator.closeQuietly(iterator);
+ * }
+ * </pre>
+ *
+ * @author Niall Pemberton
+ * @author Stephen Colebourne
+ * @author Sandy McArthur
+ * @version $Id: LineIterator.java 437567 2006-08-28 06:39:07Z bayard $
+ * @since Commons IO 1.2
+ */
+public class LineIterator implements Iterator {
+
+    /** The reader that is being read. */
+    private final BufferedReader bufferedReader;
+    /** The current line. */
+    private String cachedLine;
+    /** A flag indicating if the iterator has been fully read. */
+    private boolean finished = false;
+
+    /**
+     * Constructs an iterator of the lines for a <code>Reader</code>.
+     *
+     * @param reader the <code>Reader</code> to read from, not null
+     * @throws IllegalArgumentException if the reader is null
+     */
+    public LineIterator(final Reader reader) throws IllegalArgumentException {
+        if (reader == null) {
+            throw new IllegalArgumentException("Reader must not be null");
+        }
+        if (reader instanceof BufferedReader) {
+            bufferedReader = (BufferedReader) reader;
+        } else {
+            bufferedReader = new BufferedReader(reader);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Indicates whether the <code>Reader</code> has more lines.
+     * If there is an <code>IOException</code> then {@link #close()} will
+     * be called on this instance.
+     *
+     * @return <code>true</code> if the Reader has more lines
+     * @throws IllegalStateException if an IO exception occurs
+     */
+    public boolean hasNext() {
+        if (cachedLine != null) {
+            return true;
+        } else if (finished) {
+            return false;
+        } else {
+            try {
+                while (true) {
+                    String line = bufferedReader.readLine();
+                    if (line == null) {
+                        finished = true;
+                        return false;
+                    } else if (isValidLine(line)) {
+                        cachedLine = line;
+                        return true;
+                    }
+                }
+            } catch(IOException ioe) {
+                close();
+                throw new IllegalStateException(ioe.toString());
+            }
+        }
+    }
+
+    /**
+     * Overridable method to validate each line that is returned.
+     *
+     * @param line  the line that is to be validated
+     * @return true if valid, false to remove from the iterator
+     */
+    protected boolean isValidLine(String line) {
+        return true;
+    }
+
+    /**
+     * Returns the next line in the wrapped <code>Reader</code>.
+     *
+     * @return the next line from the input
+     * @throws NoSuchElementException if there is no line to return
+     */
+    public Object next() {
+        return nextLine();
+    }
+
+    /**
+     * Returns the next line in the wrapped <code>Reader</code>.
+     *
+     * @return the next line from the input
+     * @throws NoSuchElementException if there is no line to return
+     */
+    public String nextLine() {
+        if (!hasNext()) {
+            throw new NoSuchElementException("No more lines");
+        }
+        String currentLine = cachedLine;
+        cachedLine = null;
+        return currentLine;        
+    }
+
+    /**
+     * Closes the underlying <code>Reader</code> quietly.
+     * This method is useful if you only want to process the first few
+     * lines of a larger file. If you do not close the iterator
+     * then the <code>Reader</code> remains open.
+     * This method can safely be called multiple times.
+     */
+    public void close() {
+        finished = true;
+        IOUtils.closeQuietly(bufferedReader);
+        cachedLine = null;
+    }
+
+    /**
+     * Unsupported.
+     *
+     * @throws UnsupportedOperationException always
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("Remove unsupported on LineIterator");
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Closes the iterator, handling null and ignoring exceptions.
+     *
+     * @param iterator  the iterator to close
+     */
+    public static void closeQuietly(LineIterator iterator) {
+        if (iterator != null) {
+            iterator.close();
+        }
+    }
+
+}
diff --git a/src/org/apache/commons/io/comparator/DefaultFileComparator.java b/src/org/apache/commons/io/comparator/DefaultFileComparator.java
index 8de3cc4..19cada2 100644
--- a/src/org/apache/commons/io/comparator/DefaultFileComparator.java
+++ b/src/org/apache/commons/io/comparator/DefaultFileComparator.java
@@ -1,66 +1,66 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-/**

- * Compare two files using the <b>default</b> {@link File#compareTo(File)} method.

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by using the default file comparison.

- * <p>

- * Example of sorting a list of files using the

- * {@link #DEFAULT_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, DefaultFileComparator.DEFAULT_COMPARATOR);

- * </pre>

- * <p>

- * Example of doing a <i>reverse</i> sort of an array of files using the

- * {@link #DEFAULT_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, DefaultFileComparator.DEFAULT_REVERSE);

- * </pre>

- * <p>

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class DefaultFileComparator implements Comparator<File>, Serializable {

-

-    /** Singleton default comparator instance */

-    public static final Comparator<File> DEFAULT_COMPARATOR = new DefaultFileComparator();

-

-    /** Singleton reverse default comparator instance */

-    public static final Comparator<File> DEFAULT_REVERSE = new ReverseComparator<File>(DEFAULT_COMPARATOR);

-

-    /**

-     * Compare the two files using the {@link File#compareTo(File)} method.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return the result of calling file1's

-     * {@link File#compareTo(File)} with file2 as the parameter.

-     */

-    public int compare(File file1, File file2) {

-        return file1.compareTo(file2);

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Compare two files using the <b>default</b> {@link File#compareTo(File)} method.
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by using the default file comparison.
+ * <p>
+ * Example of sorting a list of files using the
+ * {@link #DEFAULT_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, DefaultFileComparator.DEFAULT_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of doing a <i>reverse</i> sort of an array of files using the
+ * {@link #DEFAULT_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, DefaultFileComparator.DEFAULT_REVERSE);
+ * </pre>
+ * <p>
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class DefaultFileComparator implements Comparator<File>, Serializable {
+
+    /** Singleton default comparator instance */
+    public static final Comparator<File> DEFAULT_COMPARATOR = new DefaultFileComparator();
+
+    /** Singleton reverse default comparator instance */
+    public static final Comparator<File> DEFAULT_REVERSE = new ReverseComparator<File>(DEFAULT_COMPARATOR);
+
+    /**
+     * Compare the two files using the {@link File#compareTo(File)} method.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return the result of calling file1's
+     * {@link File#compareTo(File)} with file2 as the parameter.
+     */
+    public int compare(File file1, File file2) {
+        return file1.compareTo(file2);
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/ExtensionFileComparator.java b/src/org/apache/commons/io/comparator/ExtensionFileComparator.java
index 5797b02..d097167 100644
--- a/src/org/apache/commons/io/comparator/ExtensionFileComparator.java
+++ b/src/org/apache/commons/io/comparator/ExtensionFileComparator.java
@@ -1,110 +1,110 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-import org.apache.commons.io.FilenameUtils;

-import org.apache.commons.io.IOCase;

-

-/**

- * Compare the file name <b>extensions</b> for order

- * (see {@link FilenameUtils#getExtension(String)}).

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by their file extension either in a case-sensitive, case-insensitive or

- * system dependant case sensitive way. A number of singleton instances

- * are provided for the various case sensitivity options (using {@link IOCase})

- * and the reverse of those options.

- * <p>

- * Example of a <i>case-sensitive</i> file extension sort using the

- * {@link #EXTENSION_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, ExtensionFileComparator.EXTENSION_COMPARATOR);

- * </pre>

- * <p>

- * Example of a <i>reverse case-insensitive</i> file extension sort using the

- * {@link #EXTENSION_INSENSITIVE_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, ExtensionFileComparator.EXTENSION_INSENSITIVE_REVERSE);

- * </pre>

- * <p>

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class ExtensionFileComparator implements Comparator<File>, Serializable {

-

-    /** Case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> EXTENSION_COMPARATOR = new ExtensionFileComparator();

-

-    /** Reverse case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> EXTENSION_REVERSE = new ReverseComparator<File>(EXTENSION_COMPARATOR);

-

-    /** Case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> EXTENSION_INSENSITIVE_COMPARATOR = new ExtensionFileComparator(IOCase.INSENSITIVE);

-

-    /** Reverse case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> EXTENSION_INSENSITIVE_REVERSE

-                                                = new ReverseComparator<File>(EXTENSION_INSENSITIVE_COMPARATOR);

-

-    /** System sensitive extension comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> EXTENSION_SYSTEM_COMPARATOR = new ExtensionFileComparator(IOCase.SYSTEM);

-

-    /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> EXTENSION_SYSTEM_REVERSE = new ReverseComparator<File>(EXTENSION_SYSTEM_COMPARATOR);

-

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Construct a case sensitive file extension comparator instance.

-     */

-    public ExtensionFileComparator() {

-        this.caseSensitivity = IOCase.SENSITIVE;

-    }

-

-    /**

-     * Construct a file extension comparator instance with the specified case-sensitivity.

-     *

-     * @param caseSensitivity how to handle case sensitivity, null means case-sensitive

-     */

-    public ExtensionFileComparator(IOCase caseSensitivity) {

-        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;

-    }

-

-    /**

-     * Compare the extensions of two files the specified case sensitivity.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return a negative value if the first file's extension

-     * is less than the second, zero if the extensions are the

-     * same and a positive value if the first files extension

-     * is greater than the second file.

-     *

-     */

-    public int compare(File file1, File file2) {

-        String suffix1 = FilenameUtils.getExtension(file1.getName());

-        String suffix2 = FilenameUtils.getExtension(file2.getName());

-        return caseSensitivity.checkCompareTo(suffix1, suffix2);

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOCase;
+
+/**
+ * Compare the file name <b>extensions</b> for order
+ * (see {@link FilenameUtils#getExtension(String)}).
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by their file extension either in a case-sensitive, case-insensitive or
+ * system dependant case sensitive way. A number of singleton instances
+ * are provided for the various case sensitivity options (using {@link IOCase})
+ * and the reverse of those options.
+ * <p>
+ * Example of a <i>case-sensitive</i> file extension sort using the
+ * {@link #EXTENSION_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, ExtensionFileComparator.EXTENSION_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of a <i>reverse case-insensitive</i> file extension sort using the
+ * {@link #EXTENSION_INSENSITIVE_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, ExtensionFileComparator.EXTENSION_INSENSITIVE_REVERSE);
+ * </pre>
+ * <p>
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class ExtensionFileComparator implements Comparator<File>, Serializable {
+
+    /** Case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> EXTENSION_COMPARATOR = new ExtensionFileComparator();
+
+    /** Reverse case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> EXTENSION_REVERSE = new ReverseComparator<File>(EXTENSION_COMPARATOR);
+
+    /** Case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> EXTENSION_INSENSITIVE_COMPARATOR = new ExtensionFileComparator(IOCase.INSENSITIVE);
+
+    /** Reverse case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> EXTENSION_INSENSITIVE_REVERSE
+                                                = new ReverseComparator<File>(EXTENSION_INSENSITIVE_COMPARATOR);
+
+    /** System sensitive extension comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> EXTENSION_SYSTEM_COMPARATOR = new ExtensionFileComparator(IOCase.SYSTEM);
+
+    /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> EXTENSION_SYSTEM_REVERSE = new ReverseComparator<File>(EXTENSION_SYSTEM_COMPARATOR);
+
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Construct a case sensitive file extension comparator instance.
+     */
+    public ExtensionFileComparator() {
+        this.caseSensitivity = IOCase.SENSITIVE;
+    }
+
+    /**
+     * Construct a file extension comparator instance with the specified case-sensitivity.
+     *
+     * @param caseSensitivity how to handle case sensitivity, null means case-sensitive
+     */
+    public ExtensionFileComparator(IOCase caseSensitivity) {
+        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
+    }
+
+    /**
+     * Compare the extensions of two files the specified case sensitivity.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return a negative value if the first file's extension
+     * is less than the second, zero if the extensions are the
+     * same and a positive value if the first files extension
+     * is greater than the second file.
+     *
+     */
+    public int compare(File file1, File file2) {
+        String suffix1 = FilenameUtils.getExtension(file1.getName());
+        String suffix2 = FilenameUtils.getExtension(file2.getName());
+        return caseSensitivity.checkCompareTo(suffix1, suffix2);
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java b/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java
index 0dcd0b2..5fe3fd8 100644
--- a/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java
+++ b/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java
@@ -1,77 +1,77 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-/**

- * Compare the <b>last modified date/time</b> of two files for order

- * (see {@link File#lastModified()}).

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by their last modified date/time.

- * <p>

- * Example of sorting a list of files using the

- * {@link #LASTMODIFIED_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);

- * </pre>

- * <p>

- * Example of doing a <i>reverse</i> sort of an array of files using the

- * {@link #LASTMODIFIED_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, LastModifiedFileComparator.LASTMODIFIED_REVERSE);

- * </pre>

- * <p>

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class LastModifiedFileComparator implements Comparator<File>, Serializable {

-

-    /** Last modified comparator instance */

-    public static final Comparator<File> LASTMODIFIED_COMPARATOR = new LastModifiedFileComparator();

-

-    /** Reverse last modified comparator instance */

-    public static final Comparator<File> LASTMODIFIED_REVERSE = new ReverseComparator<File>(LASTMODIFIED_COMPARATOR);

-

-    /**

-     * Compare the last the last modified date/time of two files.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return a negative value if the first file's lastmodified date/time

-     * is less than the second, zero if the lastmodified date/time are the

-     * same and a positive value if the first files lastmodified date/time

-     * is greater than the second file.

-     *

-     */

-    public int compare(File file1, File file2) {

-        long result = file1.lastModified() - file2.lastModified();

-        if (result < 0) {

-            return -1;

-        } else if (result > 0) {

-            return 1;

-        } else {

-            return 0;

-        }

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Compare the <b>last modified date/time</b> of two files for order
+ * (see {@link File#lastModified()}).
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by their last modified date/time.
+ * <p>
+ * Example of sorting a list of files using the
+ * {@link #LASTMODIFIED_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of doing a <i>reverse</i> sort of an array of files using the
+ * {@link #LASTMODIFIED_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
+ * </pre>
+ * <p>
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class LastModifiedFileComparator implements Comparator<File>, Serializable {
+
+    /** Last modified comparator instance */
+    public static final Comparator<File> LASTMODIFIED_COMPARATOR = new LastModifiedFileComparator();
+
+    /** Reverse last modified comparator instance */
+    public static final Comparator<File> LASTMODIFIED_REVERSE = new ReverseComparator<File>(LASTMODIFIED_COMPARATOR);
+
+    /**
+     * Compare the last the last modified date/time of two files.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return a negative value if the first file's lastmodified date/time
+     * is less than the second, zero if the lastmodified date/time are the
+     * same and a positive value if the first files lastmodified date/time
+     * is greater than the second file.
+     *
+     */
+    public int compare(File file1, File file2) {
+        long result = file1.lastModified() - file2.lastModified();
+        if (result < 0) {
+            return -1;
+        } else if (result > 0) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/NameFileComparator.java b/src/org/apache/commons/io/comparator/NameFileComparator.java
index eb64fb5..e29898b 100644
--- a/src/org/apache/commons/io/comparator/NameFileComparator.java
+++ b/src/org/apache/commons/io/comparator/NameFileComparator.java
@@ -1,104 +1,104 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Compare the <b>names</b> of two files for order (see {@link File#getName()}).

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by their name either in a case-sensitive, case-insensitive or

- * system dependant case sensitive way. A number of singleton instances

- * are provided for the various case sensitivity options (using {@link IOCase})

- * and the reverse of those options.

- * <p>

- * Example of a <i>case-sensitive</i> file name sort using the

- * {@link #NAME_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, NameFileComparator.NAME_COMPARATOR);

- * </pre>

- * <p>

- * Example of a <i>reverse case-insensitive</i> file name sort using the

- * {@link #NAME_INSENSITIVE_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, NameFileComparator.NAME_INSENSITIVE_REVERSE);

- * </pre>

- * <p>

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class NameFileComparator implements Comparator<File>, Serializable {

-

-    /** Case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> NAME_COMPARATOR = new NameFileComparator();

-

-    /** Reverse case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> NAME_REVERSE = new ReverseComparator<File>(NAME_COMPARATOR);

-

-    /** Case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> NAME_INSENSITIVE_COMPARATOR = new NameFileComparator(IOCase.INSENSITIVE);

-

-    /** Reverse case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> NAME_INSENSITIVE_REVERSE = new ReverseComparator<File>(NAME_INSENSITIVE_COMPARATOR);

-

-    /** System sensitive name comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> NAME_SYSTEM_COMPARATOR = new NameFileComparator(IOCase.SYSTEM);

-

-    /** Reverse system sensitive name comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> NAME_SYSTEM_REVERSE = new ReverseComparator<File>(NAME_SYSTEM_COMPARATOR);

-

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Construct a case sensitive file name comparator instance.

-     */

-    public NameFileComparator() {

-        this.caseSensitivity = IOCase.SENSITIVE;

-    }

-

-    /**

-     * Construct a file name comparator instance with the specified case-sensitivity.

-     *

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     */

-    public NameFileComparator(IOCase caseSensitivity) {

-        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;

-    }

-

-    /**

-     * Compare the names of two files with the specified case sensitivity.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return a negative value if the first file's name

-     * is less than the second, zero if the names are the

-     * same and a positive value if the first files name

-     * is greater than the second file.

-     */

-    public int compare(File file1, File file2) {

-        return caseSensitivity.checkCompareTo(file1.getName(), file2.getName());

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Compare the <b>names</b> of two files for order (see {@link File#getName()}).
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by their name either in a case-sensitive, case-insensitive or
+ * system dependant case sensitive way. A number of singleton instances
+ * are provided for the various case sensitivity options (using {@link IOCase})
+ * and the reverse of those options.
+ * <p>
+ * Example of a <i>case-sensitive</i> file name sort using the
+ * {@link #NAME_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, NameFileComparator.NAME_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of a <i>reverse case-insensitive</i> file name sort using the
+ * {@link #NAME_INSENSITIVE_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, NameFileComparator.NAME_INSENSITIVE_REVERSE);
+ * </pre>
+ * <p>
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class NameFileComparator implements Comparator<File>, Serializable {
+
+    /** Case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> NAME_COMPARATOR = new NameFileComparator();
+
+    /** Reverse case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> NAME_REVERSE = new ReverseComparator<File>(NAME_COMPARATOR);
+
+    /** Case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> NAME_INSENSITIVE_COMPARATOR = new NameFileComparator(IOCase.INSENSITIVE);
+
+    /** Reverse case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> NAME_INSENSITIVE_REVERSE = new ReverseComparator<File>(NAME_INSENSITIVE_COMPARATOR);
+
+    /** System sensitive name comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> NAME_SYSTEM_COMPARATOR = new NameFileComparator(IOCase.SYSTEM);
+
+    /** Reverse system sensitive name comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> NAME_SYSTEM_REVERSE = new ReverseComparator<File>(NAME_SYSTEM_COMPARATOR);
+
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Construct a case sensitive file name comparator instance.
+     */
+    public NameFileComparator() {
+        this.caseSensitivity = IOCase.SENSITIVE;
+    }
+
+    /**
+     * Construct a file name comparator instance with the specified case-sensitivity.
+     *
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     */
+    public NameFileComparator(IOCase caseSensitivity) {
+        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
+    }
+
+    /**
+     * Compare the names of two files with the specified case sensitivity.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return a negative value if the first file's name
+     * is less than the second, zero if the names are the
+     * same and a positive value if the first files name
+     * is greater than the second file.
+     */
+    public int compare(File file1, File file2) {
+        return caseSensitivity.checkCompareTo(file1.getName(), file2.getName());
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/PathFileComparator.java b/src/org/apache/commons/io/comparator/PathFileComparator.java
index e931138..f9d4937 100644
--- a/src/org/apache/commons/io/comparator/PathFileComparator.java
+++ b/src/org/apache/commons/io/comparator/PathFileComparator.java
@@ -1,105 +1,105 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Compare the <b>path</b> of two files for order (see {@link File#getPath()}).

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by their path either in a case-sensitive, case-insensitive or

- * system dependant case sensitive way. A number of singleton instances

- * are provided for the various case sensitivity options (using {@link IOCase})

- * and the reverse of those options.

- * <p>

- * Example of a <i>case-sensitive</i> file path sort using the

- * {@link #PATH_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, PathFileComparator.PATH_COMPARATOR);

- * </pre>

- * <p>

- * Example of a <i>reverse case-insensitive</i> file path sort using the

- * {@link #PATH_INSENSITIVE_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, PathFileComparator.PATH_INSENSITIVE_REVERSE);

- * </pre>

- * <p>

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class PathFileComparator implements Comparator<File>, Serializable {

-

-    /** Case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> PATH_COMPARATOR = new PathFileComparator();

-

-    /** Reverse case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */

-    public static final Comparator<File> PATH_REVERSE = new ReverseComparator<File>(PATH_COMPARATOR);

-

-    /** Case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> PATH_INSENSITIVE_COMPARATOR = new PathFileComparator(IOCase.INSENSITIVE);

-

-    /** Reverse case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */

-    public static final Comparator<File> PATH_INSENSITIVE_REVERSE = new ReverseComparator<File>(PATH_INSENSITIVE_COMPARATOR);

-

-    /** System sensitive path comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> PATH_SYSTEM_COMPARATOR = new PathFileComparator(IOCase.SYSTEM);

-

-    /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */

-    public static final Comparator<File> PATH_SYSTEM_REVERSE = new ReverseComparator<File>(PATH_SYSTEM_COMPARATOR);

-

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Construct a case sensitive file path comparator instance.

-     */

-    public PathFileComparator() {

-        this.caseSensitivity = IOCase.SENSITIVE;

-    }

-

-    /**

-     * Construct a file path comparator instance with the specified case-sensitivity.

-     *

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     */

-    public PathFileComparator(IOCase caseSensitivity) {

-        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;

-    }

-

-    /**

-     * Compare the paths of two files the specified case sensitivity.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return a negative value if the first file's path

-     * is less than the second, zero if the paths are the

-     * same and a positive value if the first files path

-     * is greater than the second file.

-     *

-     */

-    public int compare(File file1, File file2) {

-        return caseSensitivity.checkCompareTo(file1.getPath(), file2.getPath());

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Compare the <b>path</b> of two files for order (see {@link File#getPath()}).
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by their path either in a case-sensitive, case-insensitive or
+ * system dependant case sensitive way. A number of singleton instances
+ * are provided for the various case sensitivity options (using {@link IOCase})
+ * and the reverse of those options.
+ * <p>
+ * Example of a <i>case-sensitive</i> file path sort using the
+ * {@link #PATH_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, PathFileComparator.PATH_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of a <i>reverse case-insensitive</i> file path sort using the
+ * {@link #PATH_INSENSITIVE_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, PathFileComparator.PATH_INSENSITIVE_REVERSE);
+ * </pre>
+ * <p>
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class PathFileComparator implements Comparator<File>, Serializable {
+
+    /** Case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> PATH_COMPARATOR = new PathFileComparator();
+
+    /** Reverse case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */
+    public static final Comparator<File> PATH_REVERSE = new ReverseComparator<File>(PATH_COMPARATOR);
+
+    /** Case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> PATH_INSENSITIVE_COMPARATOR = new PathFileComparator(IOCase.INSENSITIVE);
+
+    /** Reverse case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */
+    public static final Comparator<File> PATH_INSENSITIVE_REVERSE = new ReverseComparator<File>(PATH_INSENSITIVE_COMPARATOR);
+
+    /** System sensitive path comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> PATH_SYSTEM_COMPARATOR = new PathFileComparator(IOCase.SYSTEM);
+
+    /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */
+    public static final Comparator<File> PATH_SYSTEM_REVERSE = new ReverseComparator<File>(PATH_SYSTEM_COMPARATOR);
+
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Construct a case sensitive file path comparator instance.
+     */
+    public PathFileComparator() {
+        this.caseSensitivity = IOCase.SENSITIVE;
+    }
+
+    /**
+     * Construct a file path comparator instance with the specified case-sensitivity.
+     *
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     */
+    public PathFileComparator(IOCase caseSensitivity) {
+        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
+    }
+
+    /**
+     * Compare the paths of two files the specified case sensitivity.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return a negative value if the first file's path
+     * is less than the second, zero if the paths are the
+     * same and a positive value if the first files path
+     * is greater than the second file.
+     *
+     */
+    public int compare(File file1, File file2) {
+        return caseSensitivity.checkCompareTo(file1.getPath(), file2.getPath());
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/ReverseComparator.java b/src/org/apache/commons/io/comparator/ReverseComparator.java
index ab0b38c..fe7cb6b 100644
--- a/src/org/apache/commons/io/comparator/ReverseComparator.java
+++ b/src/org/apache/commons/io/comparator/ReverseComparator.java
@@ -1,57 +1,57 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.Serializable;

-import java.util.Comparator;

-

-/**

- * Reverses the result of comparing two objects using

- * the delegate {@link Comparator}.

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-class ReverseComparator<T> implements Comparator<T>, Serializable {

-

-    private final Comparator<T> delegate;

-

-    /**

-     * Construct an instance with the sepecified delegate {@link Comparator}.

-     *

-     * @param delegate The comparator to delegate to

-     */

-    public ReverseComparator(Comparator<T> delegate) {

-        if (delegate == null) {

-            throw new IllegalArgumentException("Delegate comparator is missing");

-        }

-        this.delegate = delegate;

-    }

-

-    /**

-     * Compare using the delegate Comparator, but reversing the result.

-     *

-     * @param obj1 The first object to compare

-     * @param obj2 The second object to compare

-     * @return the result from the delegate {@link Comparator#compare(Object, Object)}

-     * reversing the value (i.e. positive becomes negative and vice versa)

-     */

-    public int compare(T obj1, T obj2) {

-        return delegate.compare(obj2, obj1); // parameters switched round

-    }

-

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Reverses the result of comparing two objects using
+ * the delegate {@link Comparator}.
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+class ReverseComparator<T> implements Comparator<T>, Serializable {
+
+    private final Comparator<T> delegate;
+
+    /**
+     * Construct an instance with the sepecified delegate {@link Comparator}.
+     *
+     * @param delegate The comparator to delegate to
+     */
+    public ReverseComparator(Comparator<T> delegate) {
+        if (delegate == null) {
+            throw new IllegalArgumentException("Delegate comparator is missing");
+        }
+        this.delegate = delegate;
+    }
+
+    /**
+     * Compare using the delegate Comparator, but reversing the result.
+     *
+     * @param obj1 The first object to compare
+     * @param obj2 The second object to compare
+     * @return the result from the delegate {@link Comparator#compare(Object, Object)}
+     * reversing the value (i.e. positive becomes negative and vice versa)
+     */
+    public int compare(T obj1, T obj2) {
+        return delegate.compare(obj2, obj1); // parameters switched round
+    }
+
+}
diff --git a/src/org/apache/commons/io/comparator/SizeFileComparator.java b/src/org/apache/commons/io/comparator/SizeFileComparator.java
index 609c159..a34d9e2 100644
--- a/src/org/apache/commons/io/comparator/SizeFileComparator.java
+++ b/src/org/apache/commons/io/comparator/SizeFileComparator.java
@@ -1,130 +1,130 @@
-/*

- * 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.commons.io.comparator;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Comparator;

-

-import org.apache.commons.io.FileUtils;

-

-/**

- * Compare the <b>length/size</b> of two files for order (see

- * {@link File#length()} and {@link FileUtils#sizeOfDirectory(File)}).

- * <p>

- * This comparator can be used to sort lists or arrays of files

- * by their length/size.

- * <p>

- * Example of sorting a list of files using the

- * {@link #SIZE_COMPARATOR} singleton instance:

- * <pre>

- *       List&lt;File&gt; list = ...

- *       Collections.sort(list, LengthFileComparator.LENGTH_COMPARATOR);

- * </pre>

- * <p>

- * Example of doing a <i>reverse</i> sort of an array of files using the

- * {@link #SIZE_REVERSE} singleton instance:

- * <pre>

- *       File[] array = ...

- *       Arrays.sort(array, LengthFileComparator.LENGTH_REVERSE);

- * </pre>

- * <p>

- * <strong>N.B.</strong> Directories are treated as <b>zero size</b> unless

- * <code>sumDirectoryContents</code> is <code>true</code>.

- *

- * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class SizeFileComparator implements Comparator<File>, Serializable {

-

-    /** Size comparator instance - directories are treated as zero size */

-    public static final Comparator<File> SIZE_COMPARATOR = new SizeFileComparator();

-

-    /** Reverse size comparator instance - directories are treated as zero size */

-    public static final Comparator<File> SIZE_REVERSE = new ReverseComparator<File>(SIZE_COMPARATOR);

-

-    /**

-     * Size comparator instance which sums the size of a directory's contents

-     * using {@link FileUtils#sizeOfDirectory(File)}

-     */

-    public static final Comparator<File> SIZE_SUMDIR_COMPARATOR = new SizeFileComparator(true);

-

-    /**

-     * Reverse size comparator instance which sums the size of a directory's contents

-     * using {@link FileUtils#sizeOfDirectory(File)}

-     */

-    public static final Comparator<File> SIZE_SUMDIR_REVERSE = new ReverseComparator<File>(SIZE_SUMDIR_COMPARATOR);

-

-    /** Whether the sum of the directory's contents should be calculated. */

-    private final boolean sumDirectoryContents;

-

-    /**

-     * Construct a file size comparator instance (directories treated as zero size).

-     */

-    public SizeFileComparator() {

-        this.sumDirectoryContents = false;

-    }

-

-    /**

-     * Construct a file size comparator instance specifying whether the size of

-     * the directory contents should be aggregated.

-     * <p>

-     * If the <code>sumDirectoryContents</code> is <code>true</code> The size of

-     * directories is calculated using  {@link FileUtils#sizeOfDirectory(File)}.

-     *

-     * @param sumDirectoryContents <code>true</code> if the sum of the directoryies contents

-     *  should be calculated, otherwise <code>false</code> if directories should be treated

-     *  as size zero (see {@link FileUtils#sizeOfDirectory(File)}).

-     */

-    public SizeFileComparator(boolean sumDirectoryContents) {

-        this.sumDirectoryContents = sumDirectoryContents;

-    }

-

-    /**

-     * Compare the length of two files.

-     *

-     * @param obj1 The first file to compare

-     * @param obj2 The second file to compare

-     * @return a negative value if the first file's length

-     * is less than the second, zero if the lengths are the

-     * same and a positive value if the first files length

-     * is greater than the second file.

-     *

-     */

-    public int compare(File file1, File file2) {

-        long size1 = 0;

-        if (file1.isDirectory()) {

-            size1 = sumDirectoryContents && file1.exists() ? FileUtils.sizeOfDirectory(file1) : 0;

-        } else {

-            size1 = file1.length();

-        }

-        long size2 = 0;

-        if (file2.isDirectory()) {

-            size2 = sumDirectoryContents && file2.exists() ? FileUtils.sizeOfDirectory(file2) : 0;

-        } else {

-            size2 = file2.length();

-        }

-        long result = size1 - size2;

-        if (result < 0) {

-            return -1;

-        } else if (result > 0) {

-            return 1;

-        } else {

-            return 0;

-        }

-    }

-}

+/*
+ * 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.commons.io.comparator;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.io.FileUtils;
+
+/**
+ * Compare the <b>length/size</b> of two files for order (see
+ * {@link File#length()} and {@link FileUtils#sizeOfDirectory(File)}).
+ * <p>
+ * This comparator can be used to sort lists or arrays of files
+ * by their length/size.
+ * <p>
+ * Example of sorting a list of files using the
+ * {@link #SIZE_COMPARATOR} singleton instance:
+ * <pre>
+ *       List&lt;File&gt; list = ...
+ *       Collections.sort(list, LengthFileComparator.LENGTH_COMPARATOR);
+ * </pre>
+ * <p>
+ * Example of doing a <i>reverse</i> sort of an array of files using the
+ * {@link #SIZE_REVERSE} singleton instance:
+ * <pre>
+ *       File[] array = ...
+ *       Arrays.sort(array, LengthFileComparator.LENGTH_REVERSE);
+ * </pre>
+ * <p>
+ * <strong>N.B.</strong> Directories are treated as <b>zero size</b> unless
+ * <code>sumDirectoryContents</code> is <code>true</code>.
+ *
+ * @version $Revision: 609243 $ $Date: 2008-01-06 00:30:42 +0000 (Sun, 06 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class SizeFileComparator implements Comparator<File>, Serializable {
+
+    /** Size comparator instance - directories are treated as zero size */
+    public static final Comparator<File> SIZE_COMPARATOR = new SizeFileComparator();
+
+    /** Reverse size comparator instance - directories are treated as zero size */
+    public static final Comparator<File> SIZE_REVERSE = new ReverseComparator<File>(SIZE_COMPARATOR);
+
+    /**
+     * Size comparator instance which sums the size of a directory's contents
+     * using {@link FileUtils#sizeOfDirectory(File)}
+     */
+    public static final Comparator<File> SIZE_SUMDIR_COMPARATOR = new SizeFileComparator(true);
+
+    /**
+     * Reverse size comparator instance which sums the size of a directory's contents
+     * using {@link FileUtils#sizeOfDirectory(File)}
+     */
+    public static final Comparator<File> SIZE_SUMDIR_REVERSE = new ReverseComparator<File>(SIZE_SUMDIR_COMPARATOR);
+
+    /** Whether the sum of the directory's contents should be calculated. */
+    private final boolean sumDirectoryContents;
+
+    /**
+     * Construct a file size comparator instance (directories treated as zero size).
+     */
+    public SizeFileComparator() {
+        this.sumDirectoryContents = false;
+    }
+
+    /**
+     * Construct a file size comparator instance specifying whether the size of
+     * the directory contents should be aggregated.
+     * <p>
+     * If the <code>sumDirectoryContents</code> is <code>true</code> The size of
+     * directories is calculated using  {@link FileUtils#sizeOfDirectory(File)}.
+     *
+     * @param sumDirectoryContents <code>true</code> if the sum of the directoryies contents
+     *  should be calculated, otherwise <code>false</code> if directories should be treated
+     *  as size zero (see {@link FileUtils#sizeOfDirectory(File)}).
+     */
+    public SizeFileComparator(boolean sumDirectoryContents) {
+        this.sumDirectoryContents = sumDirectoryContents;
+    }
+
+    /**
+     * Compare the length of two files.
+     *
+     * @param obj1 The first file to compare
+     * @param obj2 The second file to compare
+     * @return a negative value if the first file's length
+     * is less than the second, zero if the lengths are the
+     * same and a positive value if the first files length
+     * is greater than the second file.
+     *
+     */
+    public int compare(File file1, File file2) {
+        long size1 = 0;
+        if (file1.isDirectory()) {
+            size1 = sumDirectoryContents && file1.exists() ? FileUtils.sizeOfDirectory(file1) : 0;
+        } else {
+            size1 = file1.length();
+        }
+        long size2 = 0;
+        if (file2.isDirectory()) {
+            size2 = sumDirectoryContents && file2.exists() ? FileUtils.sizeOfDirectory(file2) : 0;
+        } else {
+            size2 = file2.length();
+        }
+        long result = size1 - size2;
+        if (result < 0) {
+            return -1;
+        } else if (result > 0) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/src/org/apache/commons/io/comparator/package.html b/src/org/apache/commons/io/comparator/package.html
index a2f756f..fe81e2f 100644
--- a/src/org/apache/commons/io/comparator/package.html
+++ b/src/org/apache/commons/io/comparator/package.html
@@ -1,25 +1,25 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body>

-<p>This package provides various {@link java.util.Comparator} implementations

-for {@link java.io.File}s.

-</p>

-

-</body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body>
+<p>This package provides various {@link java.util.Comparator} implementations
+for {@link java.io.File}s.
+</p>
+
+</body>
+</html>
diff --git a/src/org/apache/commons/io/filefilter/AbstractFileFilter.java b/src/org/apache/commons/io/filefilter/AbstractFileFilter.java
index 9e188f8..b0dfcfa 100644
--- a/src/org/apache/commons/io/filefilter/AbstractFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/AbstractFileFilter.java
@@ -1,67 +1,67 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-

-/**

- * An abstract class which implements the Java FileFilter and FilenameFilter 

- * interfaces via the IOFileFilter interface.

- * <p>

- * Note that a subclass <b>must</b> override one of the accept methods,

- * otherwise your class will infinitely loop.

- *

- * @since Commons IO 1.0

- * @version $Revision: 539231 $ $Date: 2007-05-18 04:10:33 +0100 (Fri, 18 May 2007) $

- * 

- * @author Stephen Colebourne

- */

-public abstract class AbstractFileFilter implements IOFileFilter {

-

-    /**

-     * Checks to see if the File should be accepted by this filter.

-     * 

-     * @param file  the File to check

-     * @return true if this file matches the test

-     */

-    public boolean accept(File file) {

-        return accept(file.getParentFile(), file.getName());

-    }

-

-    /**

-     * Checks to see if the File should be accepted by this filter.

-     * 

-     * @param dir  the directory File to check

-     * @param name  the filename within the directory to check

-     * @return true if this file matches the test

-     */

-    public boolean accept(File dir, String name) {

-        return accept(new File(dir, name));

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    public String toString() {

-        String name = getClass().getName();

-        int period = name.lastIndexOf('.');

-        return (period > 0 ? name.substring(period + 1) : name);

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+
+/**
+ * An abstract class which implements the Java FileFilter and FilenameFilter 
+ * interfaces via the IOFileFilter interface.
+ * <p>
+ * Note that a subclass <b>must</b> override one of the accept methods,
+ * otherwise your class will infinitely loop.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 539231 $ $Date: 2007-05-18 04:10:33 +0100 (Fri, 18 May 2007) $
+ * 
+ * @author Stephen Colebourne
+ */
+public abstract class AbstractFileFilter implements IOFileFilter {
+
+    /**
+     * Checks to see if the File should be accepted by this filter.
+     * 
+     * @param file  the File to check
+     * @return true if this file matches the test
+     */
+    public boolean accept(File file) {
+        return accept(file.getParentFile(), file.getName());
+    }
+
+    /**
+     * Checks to see if the File should be accepted by this filter.
+     * 
+     * @param dir  the directory File to check
+     * @param name  the filename within the directory to check
+     * @return true if this file matches the test
+     */
+    public boolean accept(File dir, String name) {
+        return accept(new File(dir, name));
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    public String toString() {
+        String name = getClass().getName();
+        int period = name.lastIndexOf('.');
+        return (period > 0 ? name.substring(period + 1) : name);
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/AgeFileFilter.java b/src/org/apache/commons/io/filefilter/AgeFileFilter.java
index ab73cb8..0139e68 100644
--- a/src/org/apache/commons/io/filefilter/AgeFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/AgeFileFilter.java
@@ -1,150 +1,150 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.Date;

-

-import org.apache.commons.io.FileUtils;

-

-/**

- * Filters files based on a cutoff time, can filter either newer

- * files or files equal to or older.

- * <p>

- * For example, to print all files and directories in the

- * current directory older than one day:

- *

- * <pre>

- * File dir = new File(".");

- * // We are interested in files older than one day

- * long cutoff = System.currentTimeMillis() - (24 * 60 * 60 * 1000);

- * String[] files = dir.list( new AgeFileFilter(cutoff) );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @author Rahul Akolkar

- * @version $Id: AgeFileFilter.java 606381 2007-12-22 02:03:16Z ggregory $

- * @since Commons IO 1.2

- */

-public class AgeFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The cutoff time threshold. */

-    private final long cutoff;

-    /** Whether the files accepted will be older or newer. */

-    private final boolean acceptOlder;

-

-    /**

-     * Constructs a new age file filter for files equal to or older than

-     * a certain cutoff

-     *

-     * @param cutoff  the threshold age of the files

-     */

-    public AgeFileFilter(long cutoff) {

-        this(cutoff, true);

-    }

-

-    /**

-     * Constructs a new age file filter for files on any one side

-     * of a certain cutoff.

-     *

-     * @param cutoff  the threshold age of the files

-     * @param acceptOlder  if true, older files (at or before the cutoff)

-     * are accepted, else newer ones (after the cutoff).

-     */

-    public AgeFileFilter(long cutoff, boolean acceptOlder) {

-        this.acceptOlder = acceptOlder;

-        this.cutoff = cutoff;

-    }

-

-    /**

-     * Constructs a new age file filter for files older than (at or before)

-     * a certain cutoff date.

-     *

-     * @param cutoffDate  the threshold age of the files

-     */

-    public AgeFileFilter(Date cutoffDate) {

-        this(cutoffDate, true);

-    }

-

-    /**

-     * Constructs a new age file filter for files on any one side

-     * of a certain cutoff date.

-     *

-     * @param cutoffDate  the threshold age of the files

-     * @param acceptOlder  if true, older files (at or before the cutoff)

-     * are accepted, else newer ones (after the cutoff).

-     */

-    public AgeFileFilter(Date cutoffDate, boolean acceptOlder) {

-        this(cutoffDate.getTime(), acceptOlder);

-    }

-

-    /**

-     * Constructs a new age file filter for files older than (at or before)

-     * a certain File (whose last modification time will be used as reference).

-     *

-     * @param cutoffReference  the file whose last modification

-     *        time is usesd as the threshold age of the files

-     */

-    public AgeFileFilter(File cutoffReference) {

-        this(cutoffReference, true);

-    }

-

-    /**

-     * Constructs a new age file filter for files on any one side

-     * of a certain File (whose last modification time will be used as

-     * reference).

-     *

-     * @param cutoffReference  the file whose last modification

-     *        time is usesd as the threshold age of the files

-     * @param acceptOlder  if true, older files (at or before the cutoff)

-     * are accepted, else newer ones (after the cutoff).

-     */

-    public AgeFileFilter(File cutoffReference, boolean acceptOlder) {

-        this(cutoffReference.lastModified(), acceptOlder);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks to see if the last modification of the file matches cutoff

-     * favorably.

-     * <p>

-     * If last modification time equals cutoff and newer files are required,

-     * file <b>IS NOT</b> selected.

-     * If last modification time equals cutoff and older files are required,

-     * file <b>IS</b> selected.

-     *

-     * @param file  the File to check

-     * @return true if the filename matches

-     */

-    public boolean accept(File file) {

-        boolean newer = FileUtils.isFileNewer(file, cutoff);

-        return acceptOlder ? !newer : newer;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    public String toString() {

-        String condition = acceptOlder ? "<=" : ">";

-        return super.toString() + "(" + condition + cutoff + ")";

-    }

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Date;
+
+import org.apache.commons.io.FileUtils;
+
+/**
+ * Filters files based on a cutoff time, can filter either newer
+ * files or files equal to or older.
+ * <p>
+ * For example, to print all files and directories in the
+ * current directory older than one day:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * // We are interested in files older than one day
+ * long cutoff = System.currentTimeMillis() - (24 * 60 * 60 * 1000);
+ * String[] files = dir.list( new AgeFileFilter(cutoff) );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @author Rahul Akolkar
+ * @version $Id: AgeFileFilter.java 606381 2007-12-22 02:03:16Z ggregory $
+ * @since Commons IO 1.2
+ */
+public class AgeFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The cutoff time threshold. */
+    private final long cutoff;
+    /** Whether the files accepted will be older or newer. */
+    private final boolean acceptOlder;
+
+    /**
+     * Constructs a new age file filter for files equal to or older than
+     * a certain cutoff
+     *
+     * @param cutoff  the threshold age of the files
+     */
+    public AgeFileFilter(long cutoff) {
+        this(cutoff, true);
+    }
+
+    /**
+     * Constructs a new age file filter for files on any one side
+     * of a certain cutoff.
+     *
+     * @param cutoff  the threshold age of the files
+     * @param acceptOlder  if true, older files (at or before the cutoff)
+     * are accepted, else newer ones (after the cutoff).
+     */
+    public AgeFileFilter(long cutoff, boolean acceptOlder) {
+        this.acceptOlder = acceptOlder;
+        this.cutoff = cutoff;
+    }
+
+    /**
+     * Constructs a new age file filter for files older than (at or before)
+     * a certain cutoff date.
+     *
+     * @param cutoffDate  the threshold age of the files
+     */
+    public AgeFileFilter(Date cutoffDate) {
+        this(cutoffDate, true);
+    }
+
+    /**
+     * Constructs a new age file filter for files on any one side
+     * of a certain cutoff date.
+     *
+     * @param cutoffDate  the threshold age of the files
+     * @param acceptOlder  if true, older files (at or before the cutoff)
+     * are accepted, else newer ones (after the cutoff).
+     */
+    public AgeFileFilter(Date cutoffDate, boolean acceptOlder) {
+        this(cutoffDate.getTime(), acceptOlder);
+    }
+
+    /**
+     * Constructs a new age file filter for files older than (at or before)
+     * a certain File (whose last modification time will be used as reference).
+     *
+     * @param cutoffReference  the file whose last modification
+     *        time is usesd as the threshold age of the files
+     */
+    public AgeFileFilter(File cutoffReference) {
+        this(cutoffReference, true);
+    }
+
+    /**
+     * Constructs a new age file filter for files on any one side
+     * of a certain File (whose last modification time will be used as
+     * reference).
+     *
+     * @param cutoffReference  the file whose last modification
+     *        time is usesd as the threshold age of the files
+     * @param acceptOlder  if true, older files (at or before the cutoff)
+     * are accepted, else newer ones (after the cutoff).
+     */
+    public AgeFileFilter(File cutoffReference, boolean acceptOlder) {
+        this(cutoffReference.lastModified(), acceptOlder);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks to see if the last modification of the file matches cutoff
+     * favorably.
+     * <p>
+     * If last modification time equals cutoff and newer files are required,
+     * file <b>IS NOT</b> selected.
+     * If last modification time equals cutoff and older files are required,
+     * file <b>IS</b> selected.
+     *
+     * @param file  the File to check
+     * @return true if the filename matches
+     */
+    public boolean accept(File file) {
+        boolean newer = FileUtils.isFileNewer(file, cutoff);
+        return acceptOlder ? !newer : newer;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    public String toString() {
+        String condition = acceptOlder ? "<=" : ">";
+        return super.toString() + "(" + condition + cutoff + ")";
+    }
+}
diff --git a/src/org/apache/commons/io/filefilter/AndFileFilter.java b/src/org/apache/commons/io/filefilter/AndFileFilter.java
index 9f3bb06..883a391 100644
--- a/src/org/apache/commons/io/filefilter/AndFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/AndFileFilter.java
@@ -1,171 +1,171 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * A {@link java.io.FileFilter} providing conditional AND logic across a list of

- * file filters. This filter returns <code>true</code> if all filters in the

- * list return <code>true</code>. Otherwise, it returns <code>false</code>.

- * Checking of the file filter list stops when the first filter returns

- * <code>false</code>.

- *

- * @since Commons IO 1.0

- * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- *

- * @author Steven Caswell

- */

-public class AndFileFilter

-        extends AbstractFileFilter

-        implements ConditionalFileFilter, Serializable {

-

-    /** The list of file filters. */

-    private List<IOFileFilter> fileFilters;

-

-    /**

-     * Constructs a new instance of <code>AndFileFilter</code>.

-     *

-     * @since Commons IO 1.1

-     */

-    public AndFileFilter() {

-        this.fileFilters = new ArrayList<IOFileFilter>();

-    }

-

-    /**

-     * Constructs a new instance of <code>AndFileFilter</code>

-     * with the specified list of filters.

-     *

-     * @param fileFilters  a List of IOFileFilter instances, copied, null ignored

-     * @since Commons IO 1.1

-     */

-    public AndFileFilter(final List<IOFileFilter> fileFilters) {

-        if (fileFilters == null) {

-            this.fileFilters = new ArrayList<IOFileFilter>();

-        } else {

-            this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);

-        }

-    }

-

-    /**

-     * Constructs a new file filter that ANDs the result of two other filters.

-     *

-     * @param filter1  the first filter, must not be null

-     * @param filter2  the second filter, must not be null

-     * @throws IllegalArgumentException if either filter is null

-     */

-    public AndFileFilter(IOFileFilter filter1, IOFileFilter filter2) {

-        if (filter1 == null || filter2 == null) {

-            throw new IllegalArgumentException("The filters must not be null");

-        }

-        this.fileFilters = new ArrayList<IOFileFilter>();

-        addFileFilter(filter1);

-        addFileFilter(filter2);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void addFileFilter(final IOFileFilter ioFileFilter) {

-        this.fileFilters.add(ioFileFilter);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public List<IOFileFilter> getFileFilters() {

-        return Collections.unmodifiableList(this.fileFilters);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public boolean removeFileFilter(final IOFileFilter ioFileFilter) {

-        return this.fileFilters.remove(ioFileFilter);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    @Override

-    public void setFileFilters(final List<IOFileFilter> fileFilters) {

-        this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    @Override

-    public boolean accept(final File file) {

-        if (this.fileFilters.size() == 0) {

-            return false;

-        }

-        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {

-            IOFileFilter fileFilter = iter.next();

-            if (!fileFilter.accept(file)) {

-                return false;

-            }

-        }

-        return true;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    @Override

-    public boolean accept(final File file, final String name) {

-        if (this.fileFilters.size() == 0) {

-            return false;

-        }

-        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {

-            IOFileFilter fileFilter = iter.next();

-            if (!fileFilter.accept(file, name)) {

-                return false;

-            }

-        }

-        return true;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (fileFilters != null) {

-            for (int i = 0; i < fileFilters.size(); i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                Object filter = fileFilters.get(i);

-                buffer.append(filter == null ? "null" : filter.toString());

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A {@link java.io.FileFilter} providing conditional AND logic across a list of
+ * file filters. This filter returns <code>true</code> if all filters in the
+ * list return <code>true</code>. Otherwise, it returns <code>false</code>.
+ * Checking of the file filter list stops when the first filter returns
+ * <code>false</code>.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ *
+ * @author Steven Caswell
+ */
+public class AndFileFilter
+        extends AbstractFileFilter
+        implements ConditionalFileFilter, Serializable {
+
+    /** The list of file filters. */
+    private List<IOFileFilter> fileFilters;
+
+    /**
+     * Constructs a new instance of <code>AndFileFilter</code>.
+     *
+     * @since Commons IO 1.1
+     */
+    public AndFileFilter() {
+        this.fileFilters = new ArrayList<IOFileFilter>();
+    }
+
+    /**
+     * Constructs a new instance of <code>AndFileFilter</code>
+     * with the specified list of filters.
+     *
+     * @param fileFilters  a List of IOFileFilter instances, copied, null ignored
+     * @since Commons IO 1.1
+     */
+    public AndFileFilter(final List<IOFileFilter> fileFilters) {
+        if (fileFilters == null) {
+            this.fileFilters = new ArrayList<IOFileFilter>();
+        } else {
+            this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);
+        }
+    }
+
+    /**
+     * Constructs a new file filter that ANDs the result of two other filters.
+     *
+     * @param filter1  the first filter, must not be null
+     * @param filter2  the second filter, must not be null
+     * @throws IllegalArgumentException if either filter is null
+     */
+    public AndFileFilter(IOFileFilter filter1, IOFileFilter filter2) {
+        if (filter1 == null || filter2 == null) {
+            throw new IllegalArgumentException("The filters must not be null");
+        }
+        this.fileFilters = new ArrayList<IOFileFilter>();
+        addFileFilter(filter1);
+        addFileFilter(filter2);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addFileFilter(final IOFileFilter ioFileFilter) {
+        this.fileFilters.add(ioFileFilter);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<IOFileFilter> getFileFilters() {
+        return Collections.unmodifiableList(this.fileFilters);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean removeFileFilter(final IOFileFilter ioFileFilter) {
+        return this.fileFilters.remove(ioFileFilter);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setFileFilters(final List<IOFileFilter> fileFilters) {
+        this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(final File file) {
+        if (this.fileFilters.size() == 0) {
+            return false;
+        }
+        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {
+            IOFileFilter fileFilter = iter.next();
+            if (!fileFilter.accept(file)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(final File file, final String name) {
+        if (this.fileFilters.size() == 0) {
+            return false;
+        }
+        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {
+            IOFileFilter fileFilter = iter.next();
+            if (!fileFilter.accept(file, name)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (fileFilters != null) {
+            for (int i = 0; i < fileFilters.size(); i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                Object filter = fileFilters.get(i);
+                buffer.append(filter == null ? "null" : filter.toString());
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/CanReadFileFilter.java b/src/org/apache/commons/io/filefilter/CanReadFileFilter.java
index a9c1325..1744af0 100644
--- a/src/org/apache/commons/io/filefilter/CanReadFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/CanReadFileFilter.java
@@ -1,92 +1,92 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts <code>File</code>s that can be read.

- * <p>

- * Example, showing how to print out a list of the 

- * current directory's <i>readable</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( CanReadFileFilter.CAN_READ );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * Example, showing how to print out a list of the 

- * current directory's <i>un-readable</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( CanReadFileFilter.CANNOT_READ );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * Example, showing how to print out a list of the 

- * current directory's <i>read-only</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( CanReadFileFilter.READ_ONLY );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 587916 $

- */

-public class CanReadFileFilter extends AbstractFileFilter implements Serializable {

-    

-    /** Singleton instance of <i>readable</i> filter */

-    public static final IOFileFilter CAN_READ = new CanReadFileFilter();

-

-    /** Singleton instance of not <i>readable</i> filter */

-    public static final IOFileFilter CANNOT_READ = new NotFileFilter(CAN_READ);

-    

-    /** Singleton instance of <i>read-only</i> filter */

-    public static final IOFileFilter READ_ONLY = new AndFileFilter(CAN_READ,

-                                                CanWriteFileFilter.CANNOT_WRITE);

-    

-    /**

-     * Restrictive consructor.

-     */

-    protected CanReadFileFilter() {

-    }

-    

-    /**

-     * Checks to see if the file can be read.

-     * 

-     * @param file  the File to check.

-     * @return <code>true</code> if the file can be

-     *  read, otherwise <code>false</code>.

-     */

-    public boolean accept(File file) {

-        return file.canRead();

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts <code>File</code>s that can be read.
+ * <p>
+ * Example, showing how to print out a list of the 
+ * current directory's <i>readable</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.CAN_READ );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the 
+ * current directory's <i>un-readable</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.CANNOT_READ );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the 
+ * current directory's <i>read-only</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.READ_ONLY );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 587916 $
+ */
+public class CanReadFileFilter extends AbstractFileFilter implements Serializable {
+    
+    /** Singleton instance of <i>readable</i> filter */
+    public static final IOFileFilter CAN_READ = new CanReadFileFilter();
+
+    /** Singleton instance of not <i>readable</i> filter */
+    public static final IOFileFilter CANNOT_READ = new NotFileFilter(CAN_READ);
+    
+    /** Singleton instance of <i>read-only</i> filter */
+    public static final IOFileFilter READ_ONLY = new AndFileFilter(CAN_READ,
+                                                CanWriteFileFilter.CANNOT_WRITE);
+    
+    /**
+     * Restrictive consructor.
+     */
+    protected CanReadFileFilter() {
+    }
+    
+    /**
+     * Checks to see if the file can be read.
+     * 
+     * @param file  the File to check.
+     * @return <code>true</code> if the file can be
+     *  read, otherwise <code>false</code>.
+     */
+    public boolean accept(File file) {
+        return file.canRead();
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/CanWriteFileFilter.java b/src/org/apache/commons/io/filefilter/CanWriteFileFilter.java
index da664f2..b91ecca 100644
--- a/src/org/apache/commons/io/filefilter/CanWriteFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/CanWriteFileFilter.java
@@ -1,80 +1,80 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts <code>File</code>s that can be written to.

- * <p>

- * Example, showing how to print out a list of the

- * current directory's <i>writable</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( CanWriteFileFilter.CAN_WRITE );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * Example, showing how to print out a list of the

- * current directory's <i>un-writable</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( CanWriteFileFilter.CANNOT_WRITE );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * <b>N.B.</b> For read-only files, use 

- *    <code>CanReadFileFilter.READ_ONLY</code>.

- *

- * @since Commons IO 1.3

- * @version $Revision: 587916 $

- */

-public class CanWriteFileFilter extends AbstractFileFilter implements Serializable {

-    

-    /** Singleton instance of <i>writable</i> filter */

-    public static final IOFileFilter CAN_WRITE = new CanWriteFileFilter();

-

-    /** Singleton instance of not <i>writable</i> filter */

-    public static final IOFileFilter CANNOT_WRITE = new NotFileFilter(CAN_WRITE);

-

-    /**

-     * Restrictive consructor.

-     */

-    protected CanWriteFileFilter() {

-    }

-    

-    /**

-     * Checks to see if the file can be written to.

-     * 

-     * @param file  the File to check

-     * @return <code>true</code> if the file can be

-     *  written to, otherwise <code>false</code>.

-     */

-    public boolean accept(File file) {

-        return file.canWrite();

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts <code>File</code>s that can be written to.
+ * <p>
+ * Example, showing how to print out a list of the
+ * current directory's <i>writable</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( CanWriteFileFilter.CAN_WRITE );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the
+ * current directory's <i>un-writable</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( CanWriteFileFilter.CANNOT_WRITE );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * <b>N.B.</b> For read-only files, use 
+ *    <code>CanReadFileFilter.READ_ONLY</code>.
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 587916 $
+ */
+public class CanWriteFileFilter extends AbstractFileFilter implements Serializable {
+    
+    /** Singleton instance of <i>writable</i> filter */
+    public static final IOFileFilter CAN_WRITE = new CanWriteFileFilter();
+
+    /** Singleton instance of not <i>writable</i> filter */
+    public static final IOFileFilter CANNOT_WRITE = new NotFileFilter(CAN_WRITE);
+
+    /**
+     * Restrictive consructor.
+     */
+    protected CanWriteFileFilter() {
+    }
+    
+    /**
+     * Checks to see if the file can be written to.
+     * 
+     * @param file  the File to check
+     * @return <code>true</code> if the file can be
+     *  written to, otherwise <code>false</code>.
+     */
+    public boolean accept(File file) {
+        return file.canWrite();
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/ConditionalFileFilter.java b/src/org/apache/commons/io/filefilter/ConditionalFileFilter.java
index b1c4813..0c2f7cb 100644
--- a/src/org/apache/commons/io/filefilter/ConditionalFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/ConditionalFileFilter.java
@@ -1,67 +1,67 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.util.List;

-

-/**

- * Defines operations for conditional file filters.

- *

- * @since Commons IO 1.1

- * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $

- *

- * @author Steven Caswell

- */

-public interface ConditionalFileFilter {

-

-    /**

-     * Adds the specified file filter to the list of file filters at the end of

-     * the list.

-     *

-     * @param ioFileFilter the filter to be added

-     * @since Commons IO 1.1

-     */

-    public void addFileFilter(IOFileFilter ioFileFilter);

-

-    /**

-     * Returns this conditional file filter's list of file filters.

-     *

-     * @return the file filter list

-     * @since Commons IO 1.1

-     */

-    public List<IOFileFilter> getFileFilters();

-

-    /**

-     * Removes the specified file filter.

-     *

-     * @param ioFileFilter filter to be removed

-     * @return <code>true</code> if the filter was found in the list,

-     * <code>false</code> otherwise

-     * @since Commons IO 1.1

-     */

-    public boolean removeFileFilter(IOFileFilter ioFileFilter);

-

-    /**

-     * Sets the list of file filters, replacing any previously configured

-     * file filters on this filter.

-     *

-     * @param fileFilters the list of filters

-     * @since Commons IO 1.1

-     */

-    public void setFileFilters(List<IOFileFilter> fileFilters);

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.util.List;
+
+/**
+ * Defines operations for conditional file filters.
+ *
+ * @since Commons IO 1.1
+ * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $
+ *
+ * @author Steven Caswell
+ */
+public interface ConditionalFileFilter {
+
+    /**
+     * Adds the specified file filter to the list of file filters at the end of
+     * the list.
+     *
+     * @param ioFileFilter the filter to be added
+     * @since Commons IO 1.1
+     */
+    public void addFileFilter(IOFileFilter ioFileFilter);
+
+    /**
+     * Returns this conditional file filter's list of file filters.
+     *
+     * @return the file filter list
+     * @since Commons IO 1.1
+     */
+    public List<IOFileFilter> getFileFilters();
+
+    /**
+     * Removes the specified file filter.
+     *
+     * @param ioFileFilter filter to be removed
+     * @return <code>true</code> if the filter was found in the list,
+     * <code>false</code> otherwise
+     * @since Commons IO 1.1
+     */
+    public boolean removeFileFilter(IOFileFilter ioFileFilter);
+
+    /**
+     * Sets the list of file filters, replacing any previously configured
+     * file filters on this filter.
+     *
+     * @param fileFilters the list of filters
+     * @since Commons IO 1.1
+     */
+    public void setFileFilters(List<IOFileFilter> fileFilters);
+
+}
diff --git a/src/org/apache/commons/io/filefilter/DelegateFileFilter.java b/src/org/apache/commons/io/filefilter/DelegateFileFilter.java
index c2d67c4..1c86d7e 100644
--- a/src/org/apache/commons/io/filefilter/DelegateFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/DelegateFileFilter.java
@@ -1,104 +1,104 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.FileFilter;

-import java.io.FilenameFilter;

-import java.io.Serializable;

-

-/**

- * This class turns a Java FileFilter or FilenameFilter into an IO FileFilter.

- * 

- * @since Commons IO 1.0

- * @version $Revision: 591058 $ $Date: 2007-11-01 15:47:05 +0000 (Thu, 01 Nov 2007) $

- * 

- * @author Stephen Colebourne

- */

-public class DelegateFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The Filename filter */

-    private final FilenameFilter filenameFilter;

-    /** The File filter */

-    private final FileFilter fileFilter;

-

-    /**

-     * Constructs a delegate file filter around an existing FilenameFilter.

-     * 

-     * @param filter  the filter to decorate

-     */

-    public DelegateFileFilter(FilenameFilter filter) {

-        if (filter == null) {

-            throw new IllegalArgumentException("The FilenameFilter must not be null");

-        }

-        this.filenameFilter = filter;

-        this.fileFilter = null;

-    }

-

-    /**

-     * Constructs a delegate file filter around an existing FileFilter.

-     * 

-     * @param filter  the filter to decorate

-     */

-    public DelegateFileFilter(FileFilter filter) {

-        if (filter == null) {

-            throw new IllegalArgumentException("The FileFilter must not be null");

-        }

-        this.fileFilter = filter;

-        this.filenameFilter = null;

-    }

-

-    /**

-     * Checks the filter.

-     * 

-     * @param file  the file to check

-     * @return true if the filter matches

-     */

-    public boolean accept(File file) {

-        if (fileFilter != null) {

-            return fileFilter.accept(file);

-        } else {

-            return super.accept(file);

-        }

-    }

-

-    /**

-     * Checks the filter.

-     * 

-     * @param dir  the directory

-     * @param name  the filename in the directory

-     * @return true if the filter matches

-     */

-    public boolean accept(File dir, String name) {

-        if (filenameFilter != null) {

-            return filenameFilter.accept(dir, name);

-        } else {

-            return super.accept(dir, name);

-        }

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    public String toString() {

-        String delegate = (fileFilter != null ? fileFilter.toString() : filenameFilter.toString()); 

-        return super.toString() + "(" + delegate + ")";

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.io.Serializable;
+
+/**
+ * This class turns a Java FileFilter or FilenameFilter into an IO FileFilter.
+ * 
+ * @since Commons IO 1.0
+ * @version $Revision: 591058 $ $Date: 2007-11-01 15:47:05 +0000 (Thu, 01 Nov 2007) $
+ * 
+ * @author Stephen Colebourne
+ */
+public class DelegateFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The Filename filter */
+    private final FilenameFilter filenameFilter;
+    /** The File filter */
+    private final FileFilter fileFilter;
+
+    /**
+     * Constructs a delegate file filter around an existing FilenameFilter.
+     * 
+     * @param filter  the filter to decorate
+     */
+    public DelegateFileFilter(FilenameFilter filter) {
+        if (filter == null) {
+            throw new IllegalArgumentException("The FilenameFilter must not be null");
+        }
+        this.filenameFilter = filter;
+        this.fileFilter = null;
+    }
+
+    /**
+     * Constructs a delegate file filter around an existing FileFilter.
+     * 
+     * @param filter  the filter to decorate
+     */
+    public DelegateFileFilter(FileFilter filter) {
+        if (filter == null) {
+            throw new IllegalArgumentException("The FileFilter must not be null");
+        }
+        this.fileFilter = filter;
+        this.filenameFilter = null;
+    }
+
+    /**
+     * Checks the filter.
+     * 
+     * @param file  the file to check
+     * @return true if the filter matches
+     */
+    public boolean accept(File file) {
+        if (fileFilter != null) {
+            return fileFilter.accept(file);
+        } else {
+            return super.accept(file);
+        }
+    }
+
+    /**
+     * Checks the filter.
+     * 
+     * @param dir  the directory
+     * @param name  the filename in the directory
+     * @return true if the filter matches
+     */
+    public boolean accept(File dir, String name) {
+        if (filenameFilter != null) {
+            return filenameFilter.accept(dir, name);
+        } else {
+            return super.accept(dir, name);
+        }
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    public String toString() {
+        String delegate = (fileFilter != null ? fileFilter.toString() : filenameFilter.toString()); 
+        return super.toString() + "(" + delegate + ")";
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/DirectoryFileFilter.java b/src/org/apache/commons/io/filefilter/DirectoryFileFilter.java
index 3412e7b..a1a44f5 100644
--- a/src/org/apache/commons/io/filefilter/DirectoryFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/DirectoryFileFilter.java
@@ -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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts <code>File</code>s that are directories.

- * <p>

- * For example, here is how to print out a list of the 

- * current directory's subdirectories:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( DirectoryFileFilter.INSTANCE );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.0

- * @version $Revision: 587916 $ $Date: 2007-10-24 16:53:07 +0100 (Wed, 24 Oct 2007) $

- *

- * @author Stephen Colebourne

- * @author Peter Donald

- */

-public class DirectoryFileFilter extends AbstractFileFilter implements Serializable {

-

-    /**

-     * Singleton instance of directory filter.

-     * @since Commons IO 1.3

-     */

-    public static final IOFileFilter DIRECTORY = new DirectoryFileFilter();

-    /**

-     * Singleton instance of directory filter.

-     * Please use the identical DirectoryFileFilter.DIRECTORY constant.

-     * The new name is more JDK 1.5 friendly as it doesn't clash with other

-     * values when using static imports.

-     */

-    public static final IOFileFilter INSTANCE = DIRECTORY;

-

-    /**

-     * Restrictive consructor.

-     */

-    protected DirectoryFileFilter() {

-    }

-

-    /**

-     * Checks to see if the file is a directory.

-     *

-     * @param file  the File to check

-     * @return true if the file is a directory

-     */

-    public boolean accept(File file) {

-        return file.isDirectory();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts <code>File</code>s that are directories.
+ * <p>
+ * For example, here is how to print out a list of the 
+ * current directory's subdirectories:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( DirectoryFileFilter.INSTANCE );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 587916 $ $Date: 2007-10-24 16:53:07 +0100 (Wed, 24 Oct 2007) $
+ *
+ * @author Stephen Colebourne
+ * @author Peter Donald
+ */
+public class DirectoryFileFilter extends AbstractFileFilter implements Serializable {
+
+    /**
+     * Singleton instance of directory filter.
+     * @since Commons IO 1.3
+     */
+    public static final IOFileFilter DIRECTORY = new DirectoryFileFilter();
+    /**
+     * Singleton instance of directory filter.
+     * Please use the identical DirectoryFileFilter.DIRECTORY constant.
+     * The new name is more JDK 1.5 friendly as it doesn't clash with other
+     * values when using static imports.
+     */
+    public static final IOFileFilter INSTANCE = DIRECTORY;
+
+    /**
+     * Restrictive consructor.
+     */
+    protected DirectoryFileFilter() {
+    }
+
+    /**
+     * Checks to see if the file is a directory.
+     *
+     * @param file  the File to check
+     * @return true if the file is a directory
+     */
+    public boolean accept(File file) {
+        return file.isDirectory();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/EmptyFileFilter.java b/src/org/apache/commons/io/filefilter/EmptyFileFilter.java
index e88a862..f5219f4 100644
--- a/src/org/apache/commons/io/filefilter/EmptyFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/EmptyFileFilter.java
@@ -1,84 +1,84 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts files or directories that are empty.

- * <p>

- * If the <code>File</code> is a directory it checks that

- * it contains no files.

- * <p>

- * Example, showing how to print out a list of the 

- * current directory's empty files/directories:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( EmptyFileFilter.EMPTY );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * Example, showing how to print out a list of the 

- * current directory's non-empty files/directories:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( EmptyFileFilter.NOT_EMPTY );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 587916 $

- */

-public class EmptyFileFilter extends AbstractFileFilter implements Serializable {

-    

-    /** Singleton instance of <i>empty</i> filter */

-    public static final IOFileFilter EMPTY = new EmptyFileFilter();

-    

-    /** Singleton instance of <i>not-empty</i> filter */

-    public static final IOFileFilter NOT_EMPTY = new NotFileFilter(EMPTY);

-    

-    /**

-     * Restrictive consructor.

-     */

-    protected EmptyFileFilter() {

-    }

-    

-    /**

-     * Checks to see if the file is empty.

-     * 

-     * @param file  the file or directory to check

-     * @return <code>true</code> if the file or directory

-     *  is <i>empty</i>, otherwise <code>false</code>.

-     */

-    public boolean accept(File file) {

-        if (file.isDirectory()) {

-            File[] files = file.listFiles();

-            return (files == null || files.length == 0);

-        } else {

-            return (file.length() == 0);

-        }

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts files or directories that are empty.
+ * <p>
+ * If the <code>File</code> is a directory it checks that
+ * it contains no files.
+ * <p>
+ * Example, showing how to print out a list of the 
+ * current directory's empty files/directories:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( EmptyFileFilter.EMPTY );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the 
+ * current directory's non-empty files/directories:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( EmptyFileFilter.NOT_EMPTY );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 587916 $
+ */
+public class EmptyFileFilter extends AbstractFileFilter implements Serializable {
+    
+    /** Singleton instance of <i>empty</i> filter */
+    public static final IOFileFilter EMPTY = new EmptyFileFilter();
+    
+    /** Singleton instance of <i>not-empty</i> filter */
+    public static final IOFileFilter NOT_EMPTY = new NotFileFilter(EMPTY);
+    
+    /**
+     * Restrictive consructor.
+     */
+    protected EmptyFileFilter() {
+    }
+    
+    /**
+     * Checks to see if the file is empty.
+     * 
+     * @param file  the file or directory to check
+     * @return <code>true</code> if the file or directory
+     *  is <i>empty</i>, otherwise <code>false</code>.
+     */
+    public boolean accept(File file) {
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            return (files == null || files.length == 0);
+        } else {
+            return (file.length() == 0);
+        }
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/FalseFileFilter.java b/src/org/apache/commons/io/filefilter/FalseFileFilter.java
index 8a87d40..5a24c6a 100644
--- a/src/org/apache/commons/io/filefilter/FalseFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/FalseFileFilter.java
@@ -1,72 +1,72 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * A file filter that always returns false.

- *

- * @since Commons IO 1.0

- * @version $Revision: 587978 $ $Date: 2007-10-24 20:36:51 +0100 (Wed, 24 Oct 2007) $

- *

- * @author Stephen Colebourne

- */

-public class FalseFileFilter implements IOFileFilter, Serializable {

-

-    /**

-     * Singleton instance of false filter.

-     * @since Commons IO 1.3

-     */

-    public static final IOFileFilter FALSE = new FalseFileFilter();

-    /**

-     * Singleton instance of false filter.

-     * Please use the identical FalseFileFilter.FALSE constant.

-     * The new name is more JDK 1.5 friendly as it doesn't clash with other

-     * values when using static imports.

-     */

-    public static final IOFileFilter INSTANCE = FALSE;

-

-    /**

-     * Restrictive consructor.

-     */

-    protected FalseFileFilter() {

-    }

-

-    /**

-     * Returns false.

-     *

-     * @param file  the file to check

-     * @return false

-     */

-    public boolean accept(File file) {

-        return false;

-    }

-

-    /**

-     * Returns false.

-     *

-     * @param dir  the directory to check

-     * @param name  the filename

-     * @return false

-     */

-    public boolean accept(File dir, String name) {

-        return false;

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * A file filter that always returns false.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 587978 $ $Date: 2007-10-24 20:36:51 +0100 (Wed, 24 Oct 2007) $
+ *
+ * @author Stephen Colebourne
+ */
+public class FalseFileFilter implements IOFileFilter, Serializable {
+
+    /**
+     * Singleton instance of false filter.
+     * @since Commons IO 1.3
+     */
+    public static final IOFileFilter FALSE = new FalseFileFilter();
+    /**
+     * Singleton instance of false filter.
+     * Please use the identical FalseFileFilter.FALSE constant.
+     * The new name is more JDK 1.5 friendly as it doesn't clash with other
+     * values when using static imports.
+     */
+    public static final IOFileFilter INSTANCE = FALSE;
+
+    /**
+     * Restrictive consructor.
+     */
+    protected FalseFileFilter() {
+    }
+
+    /**
+     * Returns false.
+     *
+     * @param file  the file to check
+     * @return false
+     */
+    public boolean accept(File file) {
+        return false;
+    }
+
+    /**
+     * Returns false.
+     *
+     * @param dir  the directory to check
+     * @param name  the filename
+     * @return false
+     */
+    public boolean accept(File dir, String name) {
+        return false;
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/FileFileFilter.java b/src/org/apache/commons/io/filefilter/FileFileFilter.java
index 0d49edd..7547f4e 100644
--- a/src/org/apache/commons/io/filefilter/FileFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/FileFileFilter.java
@@ -1,60 +1,60 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts <code>File</code>s that are files (not directories).

- * <p>

- * For example, here is how to print out a list of the real files

- * within the current directory:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( FileFileFilter.FILE );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 155419 $ $Date: 2007-10-24 16:53:07 +0100 (Wed, 24 Oct 2007) $

- */

-public class FileFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** Singleton instance of file filter */

-    public static final IOFileFilter FILE = new FileFileFilter();

-

-    /**

-     * Restrictive consructor.

-     */

-    protected FileFileFilter() {

-    }

-

-    /**

-     * Checks to see if the file is a file.

-     *

-     * @param file  the File to check

-     * @return true if the file is a file

-     */

-    public boolean accept(File file) {

-        return file.isFile();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts <code>File</code>s that are files (not directories).
+ * <p>
+ * For example, here is how to print out a list of the real files
+ * within the current directory:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( FileFileFilter.FILE );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 155419 $ $Date: 2007-10-24 16:53:07 +0100 (Wed, 24 Oct 2007) $
+ */
+public class FileFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** Singleton instance of file filter */
+    public static final IOFileFilter FILE = new FileFileFilter();
+
+    /**
+     * Restrictive consructor.
+     */
+    protected FileFileFilter() {
+    }
+
+    /**
+     * Checks to see if the file is a file.
+     *
+     * @param file  the File to check
+     * @return true if the file is a file
+     */
+    public boolean accept(File file) {
+        return file.isFile();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/FileFilterUtils.java b/src/org/apache/commons/io/filefilter/FileFilterUtils.java
index 71c37b1..e09e28b 100644
--- a/src/org/apache/commons/io/filefilter/FileFilterUtils.java
+++ b/src/org/apache/commons/io/filefilter/FileFilterUtils.java
@@ -1,361 +1,361 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.FileFilter;

-import java.io.FilenameFilter;

-import java.util.Date;

-

-/**

- * Useful utilities for working with file filters. It provides access to all

- * file filter implementations in this package so you don't have to import

- * every class you use.

- * 

- * @since Commons IO 1.0

- * @version $Id: FileFilterUtils.java 609286 2008-01-06 10:01:26Z scolebourne $

- * 

- * @author Stephen Colebourne

- * @author Jeremias Maerki

- * @author Masato Tezuka

- * @author Rahul Akolkar

- */

-public class FileFilterUtils {

-    

-    /**

-     * FileFilterUtils is not normally instantiated.

-     */

-    public FileFilterUtils() {

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a filter that returns true if the filename starts with the specified text.

-     * 

-     * @param prefix  the filename prefix

-     * @return a prefix checking filter

-     */

-    public static IOFileFilter prefixFileFilter(String prefix) {

-        return new PrefixFileFilter(prefix);

-    }

-

-    /**

-     * Returns a filter that returns true if the filename ends with the specified text.

-     * 

-     * @param suffix  the filename suffix

-     * @return a suffix checking filter

-     */

-    public static IOFileFilter suffixFileFilter(String suffix) {

-        return new SuffixFileFilter(suffix);

-    }

-

-    /**

-     * Returns a filter that returns true if the filename matches the specified text.

-     * 

-     * @param name  the filename

-     * @return a name checking filter

-     */

-    public static IOFileFilter nameFileFilter(String name) {

-        return new NameFileFilter(name);

-    }

-

-    /**

-     * Returns a filter that checks if the file is a directory.

-     * 

-     * @return file filter that accepts only directories and not files

-     */

-    public static IOFileFilter directoryFileFilter() {

-        return DirectoryFileFilter.DIRECTORY;

-    }

-

-    /**

-     * Returns a filter that checks if the file is a file (and not a directory).

-     * 

-     * @return file filter that accepts only files and not directories

-     */

-    public static IOFileFilter fileFileFilter() {

-        return FileFileFilter.FILE;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a filter that ANDs the two specified filters.

-     * 

-     * @param filter1  the first filter

-     * @param filter2  the second filter

-     * @return a filter that ANDs the two specified filters

-     */

-    public static IOFileFilter andFileFilter(IOFileFilter filter1, IOFileFilter filter2) {

-        return new AndFileFilter(filter1, filter2);

-    }

-

-    /**

-     * Returns a filter that ORs the two specified filters.

-     * 

-     * @param filter1  the first filter

-     * @param filter2  the second filter

-     * @return a filter that ORs the two specified filters

-     */

-    public static IOFileFilter orFileFilter(IOFileFilter filter1, IOFileFilter filter2) {

-        return new OrFileFilter(filter1, filter2);

-    }

-

-    /**

-     * Returns a filter that NOTs the specified filter.

-     * 

-     * @param filter  the filter to invert

-     * @return a filter that NOTs the specified filter

-     */

-    public static IOFileFilter notFileFilter(IOFileFilter filter) {

-        return new NotFileFilter(filter);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a filter that always returns true.

-     * 

-     * @return a true filter

-     */

-    public static IOFileFilter trueFileFilter() {

-        return TrueFileFilter.TRUE;

-    }

-

-    /**

-     * Returns a filter that always returns false.

-     * 

-     * @return a false filter

-     */

-    public static IOFileFilter falseFileFilter() {

-        return FalseFileFilter.FALSE;

-    }

-    

-    //-----------------------------------------------------------------------

-    /**

-     * Returns an <code>IOFileFilter</code> that wraps the

-     * <code>FileFilter</code> instance.

-     * 

-     * @param filter  the filter to be wrapped

-     * @return a new filter that implements IOFileFilter

-     */

-    public static IOFileFilter asFileFilter(FileFilter filter) {

-        return new DelegateFileFilter(filter);

-    }

-

-    /**

-     * Returns an <code>IOFileFilter</code> that wraps the

-     * <code>FilenameFilter</code> instance.

-     * 

-     * @param filter  the filter to be wrapped

-     * @return a new filter that implements IOFileFilter

-     */

-    public static IOFileFilter asFileFilter(FilenameFilter filter) {

-        return new DelegateFileFilter(filter);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a filter that returns true if the file was last modified after

-     * the specified cutoff time.

-     *

-     * @param cutoff  the time threshold

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(long cutoff) {

-        return new AgeFileFilter(cutoff);

-    }

-

-    /**

-     * Returns a filter that filters files based on a cutoff time.

-     *

-     * @param cutoff  the time threshold

-     * @param acceptOlder  if true, older files get accepted, if false, newer

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(long cutoff, boolean acceptOlder) {

-        return new AgeFileFilter(cutoff, acceptOlder);

-    }

-

-    /**

-     * Returns a filter that returns true if the file was last modified after

-     * the specified cutoff date.

-     *

-     * @param cutoffDate  the time threshold

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(Date cutoffDate) {

-        return new AgeFileFilter(cutoffDate);

-    }

-

-    /**

-     * Returns a filter that filters files based on a cutoff date.

-     *

-     * @param cutoffDate  the time threshold

-     * @param acceptOlder  if true, older files get accepted, if false, newer

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(Date cutoffDate, boolean acceptOlder) {

-        return new AgeFileFilter(cutoffDate, acceptOlder);

-    }

-

-    /**

-     * Returns a filter that returns true if the file was last modified after

-     * the specified reference file.

-     *

-     * @param cutoffReference  the file whose last modification

-     *        time is usesd as the threshold age of the files

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(File cutoffReference) {

-        return new AgeFileFilter(cutoffReference);

-    }

-

-    /**

-     * Returns a filter that filters files based on a cutoff reference file.

-     *

-     * @param cutoffReference  the file whose last modification

-     *        time is usesd as the threshold age of the files

-     * @param acceptOlder  if true, older files get accepted, if false, newer

-     * @return an appropriately configured age file filter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter ageFileFilter(File cutoffReference, boolean acceptOlder) {

-        return new AgeFileFilter(cutoffReference, acceptOlder);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Returns a filter that returns true if the file is bigger than a certain size.

-     *

-     * @param threshold  the file size threshold

-     * @return an appropriately configured SizeFileFilter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter sizeFileFilter(long threshold) {

-        return new SizeFileFilter(threshold);

-    }

-

-    /**

-     * Returns a filter that filters based on file size.

-     *

-     * @param threshold  the file size threshold

-     * @param acceptLarger  if true, larger files get accepted, if false, smaller

-     * @return an appropriately configured SizeFileFilter

-     * @since Commons IO 1.2

-     */

-    public static IOFileFilter sizeFileFilter(long threshold, boolean acceptLarger) {

-        return new SizeFileFilter(threshold, acceptLarger);

-    }

-

-    /**

-     * Returns a filter that accepts files whose size is &gt;= minimum size

-     * and &lt;= maximum size.

-     *

-     * @param minSizeInclusive the minimum file size (inclusive)

-     * @param maxSizeInclusive the maximum file size (inclusive)

-     * @return an appropriately configured IOFileFilter

-     * @since Commons IO 1.3

-     */

-    public static IOFileFilter sizeRangeFileFilter(long minSizeInclusive, long maxSizeInclusive ) {

-        IOFileFilter minimumFilter = new SizeFileFilter(minSizeInclusive, true);

-        IOFileFilter maximumFilter = new SizeFileFilter(maxSizeInclusive + 1L, false);

-        return new AndFileFilter(minimumFilter, maximumFilter);

-    }

-

-    //-----------------------------------------------------------------------

-    /* Constructed on demand and then cached */

-    private static IOFileFilter cvsFilter;

-

-    /* Constructed on demand and then cached */

-    private static IOFileFilter svnFilter;

-

-    /**

-     * Decorates a filter to make it ignore CVS directories.

-     * Passing in <code>null</code> will return a filter that accepts everything

-     * except CVS directories.

-     * 

-     * @param filter  the filter to decorate, null means an unrestricted filter

-     * @return the decorated filter, never null

-     * @since Commons IO 1.1 (method existed but had bug in 1.0)

-     */

-    public static IOFileFilter makeCVSAware(IOFileFilter filter) {

-        if (cvsFilter == null) {

-            cvsFilter = notFileFilter(

-                andFileFilter(directoryFileFilter(), nameFileFilter("CVS")));

-        }

-        if (filter == null) {

-            return cvsFilter;

-        } else {

-            return andFileFilter(filter, cvsFilter);

-        }

-    }

-

-    /**

-     * Decorates a filter to make it ignore SVN directories.

-     * Passing in <code>null</code> will return a filter that accepts everything

-     * except SVN directories.

-     * 

-     * @param filter  the filter to decorate, null means an unrestricted filter

-     * @return the decorated filter, never null

-     * @since Commons IO 1.1

-     */

-    public static IOFileFilter makeSVNAware(IOFileFilter filter) {

-        if (svnFilter == null) {

-            svnFilter = notFileFilter(

-                andFileFilter(directoryFileFilter(), nameFileFilter(".svn")));

-        }

-        if (filter == null) {

-            return svnFilter;

-        } else {

-            return andFileFilter(filter, svnFilter);

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Decorates a filter so that it only applies to directories and not to files.

-     * 

-     * @param filter  the filter to decorate, null means an unrestricted filter

-     * @return the decorated filter, never null

-     * @since Commons IO 1.3

-     */

-    public static IOFileFilter makeDirectoryOnly(IOFileFilter filter) {

-        if (filter == null) {

-            return DirectoryFileFilter.DIRECTORY;

-        }

-        return new AndFileFilter(DirectoryFileFilter.DIRECTORY, filter);

-    }

-

-    /**

-     * Decorates a filter so that it only applies to files and not to directories.

-     * 

-     * @param filter  the filter to decorate, null means an unrestricted filter

-     * @return the decorated filter, never null

-     * @since Commons IO 1.3

-     */

-    public static IOFileFilter makeFileOnly(IOFileFilter filter) {

-        if (filter == null) {

-            return FileFileFilter.FILE;

-        }

-        return new AndFileFilter(FileFileFilter.FILE, filter);

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.util.Date;
+
+/**
+ * Useful utilities for working with file filters. It provides access to all
+ * file filter implementations in this package so you don't have to import
+ * every class you use.
+ * 
+ * @since Commons IO 1.0
+ * @version $Id: FileFilterUtils.java 609286 2008-01-06 10:01:26Z scolebourne $
+ * 
+ * @author Stephen Colebourne
+ * @author Jeremias Maerki
+ * @author Masato Tezuka
+ * @author Rahul Akolkar
+ */
+public class FileFilterUtils {
+    
+    /**
+     * FileFilterUtils is not normally instantiated.
+     */
+    public FileFilterUtils() {
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a filter that returns true if the filename starts with the specified text.
+     * 
+     * @param prefix  the filename prefix
+     * @return a prefix checking filter
+     */
+    public static IOFileFilter prefixFileFilter(String prefix) {
+        return new PrefixFileFilter(prefix);
+    }
+
+    /**
+     * Returns a filter that returns true if the filename ends with the specified text.
+     * 
+     * @param suffix  the filename suffix
+     * @return a suffix checking filter
+     */
+    public static IOFileFilter suffixFileFilter(String suffix) {
+        return new SuffixFileFilter(suffix);
+    }
+
+    /**
+     * Returns a filter that returns true if the filename matches the specified text.
+     * 
+     * @param name  the filename
+     * @return a name checking filter
+     */
+    public static IOFileFilter nameFileFilter(String name) {
+        return new NameFileFilter(name);
+    }
+
+    /**
+     * Returns a filter that checks if the file is a directory.
+     * 
+     * @return file filter that accepts only directories and not files
+     */
+    public static IOFileFilter directoryFileFilter() {
+        return DirectoryFileFilter.DIRECTORY;
+    }
+
+    /**
+     * Returns a filter that checks if the file is a file (and not a directory).
+     * 
+     * @return file filter that accepts only files and not directories
+     */
+    public static IOFileFilter fileFileFilter() {
+        return FileFileFilter.FILE;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a filter that ANDs the two specified filters.
+     * 
+     * @param filter1  the first filter
+     * @param filter2  the second filter
+     * @return a filter that ANDs the two specified filters
+     */
+    public static IOFileFilter andFileFilter(IOFileFilter filter1, IOFileFilter filter2) {
+        return new AndFileFilter(filter1, filter2);
+    }
+
+    /**
+     * Returns a filter that ORs the two specified filters.
+     * 
+     * @param filter1  the first filter
+     * @param filter2  the second filter
+     * @return a filter that ORs the two specified filters
+     */
+    public static IOFileFilter orFileFilter(IOFileFilter filter1, IOFileFilter filter2) {
+        return new OrFileFilter(filter1, filter2);
+    }
+
+    /**
+     * Returns a filter that NOTs the specified filter.
+     * 
+     * @param filter  the filter to invert
+     * @return a filter that NOTs the specified filter
+     */
+    public static IOFileFilter notFileFilter(IOFileFilter filter) {
+        return new NotFileFilter(filter);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a filter that always returns true.
+     * 
+     * @return a true filter
+     */
+    public static IOFileFilter trueFileFilter() {
+        return TrueFileFilter.TRUE;
+    }
+
+    /**
+     * Returns a filter that always returns false.
+     * 
+     * @return a false filter
+     */
+    public static IOFileFilter falseFileFilter() {
+        return FalseFileFilter.FALSE;
+    }
+    
+    //-----------------------------------------------------------------------
+    /**
+     * Returns an <code>IOFileFilter</code> that wraps the
+     * <code>FileFilter</code> instance.
+     * 
+     * @param filter  the filter to be wrapped
+     * @return a new filter that implements IOFileFilter
+     */
+    public static IOFileFilter asFileFilter(FileFilter filter) {
+        return new DelegateFileFilter(filter);
+    }
+
+    /**
+     * Returns an <code>IOFileFilter</code> that wraps the
+     * <code>FilenameFilter</code> instance.
+     * 
+     * @param filter  the filter to be wrapped
+     * @return a new filter that implements IOFileFilter
+     */
+    public static IOFileFilter asFileFilter(FilenameFilter filter) {
+        return new DelegateFileFilter(filter);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a filter that returns true if the file was last modified after
+     * the specified cutoff time.
+     *
+     * @param cutoff  the time threshold
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(long cutoff) {
+        return new AgeFileFilter(cutoff);
+    }
+
+    /**
+     * Returns a filter that filters files based on a cutoff time.
+     *
+     * @param cutoff  the time threshold
+     * @param acceptOlder  if true, older files get accepted, if false, newer
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(long cutoff, boolean acceptOlder) {
+        return new AgeFileFilter(cutoff, acceptOlder);
+    }
+
+    /**
+     * Returns a filter that returns true if the file was last modified after
+     * the specified cutoff date.
+     *
+     * @param cutoffDate  the time threshold
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(Date cutoffDate) {
+        return new AgeFileFilter(cutoffDate);
+    }
+
+    /**
+     * Returns a filter that filters files based on a cutoff date.
+     *
+     * @param cutoffDate  the time threshold
+     * @param acceptOlder  if true, older files get accepted, if false, newer
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(Date cutoffDate, boolean acceptOlder) {
+        return new AgeFileFilter(cutoffDate, acceptOlder);
+    }
+
+    /**
+     * Returns a filter that returns true if the file was last modified after
+     * the specified reference file.
+     *
+     * @param cutoffReference  the file whose last modification
+     *        time is usesd as the threshold age of the files
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(File cutoffReference) {
+        return new AgeFileFilter(cutoffReference);
+    }
+
+    /**
+     * Returns a filter that filters files based on a cutoff reference file.
+     *
+     * @param cutoffReference  the file whose last modification
+     *        time is usesd as the threshold age of the files
+     * @param acceptOlder  if true, older files get accepted, if false, newer
+     * @return an appropriately configured age file filter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter ageFileFilter(File cutoffReference, boolean acceptOlder) {
+        return new AgeFileFilter(cutoffReference, acceptOlder);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a filter that returns true if the file is bigger than a certain size.
+     *
+     * @param threshold  the file size threshold
+     * @return an appropriately configured SizeFileFilter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter sizeFileFilter(long threshold) {
+        return new SizeFileFilter(threshold);
+    }
+
+    /**
+     * Returns a filter that filters based on file size.
+     *
+     * @param threshold  the file size threshold
+     * @param acceptLarger  if true, larger files get accepted, if false, smaller
+     * @return an appropriately configured SizeFileFilter
+     * @since Commons IO 1.2
+     */
+    public static IOFileFilter sizeFileFilter(long threshold, boolean acceptLarger) {
+        return new SizeFileFilter(threshold, acceptLarger);
+    }
+
+    /**
+     * Returns a filter that accepts files whose size is &gt;= minimum size
+     * and &lt;= maximum size.
+     *
+     * @param minSizeInclusive the minimum file size (inclusive)
+     * @param maxSizeInclusive the maximum file size (inclusive)
+     * @return an appropriately configured IOFileFilter
+     * @since Commons IO 1.3
+     */
+    public static IOFileFilter sizeRangeFileFilter(long minSizeInclusive, long maxSizeInclusive ) {
+        IOFileFilter minimumFilter = new SizeFileFilter(minSizeInclusive, true);
+        IOFileFilter maximumFilter = new SizeFileFilter(maxSizeInclusive + 1L, false);
+        return new AndFileFilter(minimumFilter, maximumFilter);
+    }
+
+    //-----------------------------------------------------------------------
+    /* Constructed on demand and then cached */
+    private static IOFileFilter cvsFilter;
+
+    /* Constructed on demand and then cached */
+    private static IOFileFilter svnFilter;
+
+    /**
+     * Decorates a filter to make it ignore CVS directories.
+     * Passing in <code>null</code> will return a filter that accepts everything
+     * except CVS directories.
+     * 
+     * @param filter  the filter to decorate, null means an unrestricted filter
+     * @return the decorated filter, never null
+     * @since Commons IO 1.1 (method existed but had bug in 1.0)
+     */
+    public static IOFileFilter makeCVSAware(IOFileFilter filter) {
+        if (cvsFilter == null) {
+            cvsFilter = notFileFilter(
+                andFileFilter(directoryFileFilter(), nameFileFilter("CVS")));
+        }
+        if (filter == null) {
+            return cvsFilter;
+        } else {
+            return andFileFilter(filter, cvsFilter);
+        }
+    }
+
+    /**
+     * Decorates a filter to make it ignore SVN directories.
+     * Passing in <code>null</code> will return a filter that accepts everything
+     * except SVN directories.
+     * 
+     * @param filter  the filter to decorate, null means an unrestricted filter
+     * @return the decorated filter, never null
+     * @since Commons IO 1.1
+     */
+    public static IOFileFilter makeSVNAware(IOFileFilter filter) {
+        if (svnFilter == null) {
+            svnFilter = notFileFilter(
+                andFileFilter(directoryFileFilter(), nameFileFilter(".svn")));
+        }
+        if (filter == null) {
+            return svnFilter;
+        } else {
+            return andFileFilter(filter, svnFilter);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Decorates a filter so that it only applies to directories and not to files.
+     * 
+     * @param filter  the filter to decorate, null means an unrestricted filter
+     * @return the decorated filter, never null
+     * @since Commons IO 1.3
+     */
+    public static IOFileFilter makeDirectoryOnly(IOFileFilter filter) {
+        if (filter == null) {
+            return DirectoryFileFilter.DIRECTORY;
+        }
+        return new AndFileFilter(DirectoryFileFilter.DIRECTORY, filter);
+    }
+
+    /**
+     * Decorates a filter so that it only applies to files and not to directories.
+     * 
+     * @param filter  the filter to decorate, null means an unrestricted filter
+     * @return the decorated filter, never null
+     * @since Commons IO 1.3
+     */
+    public static IOFileFilter makeFileOnly(IOFileFilter filter) {
+        if (filter == null) {
+            return FileFileFilter.FILE;
+        }
+        return new AndFileFilter(FileFileFilter.FILE, filter);
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/HiddenFileFilter.java b/src/org/apache/commons/io/filefilter/HiddenFileFilter.java
index 244153d..9d90aa7 100644
--- a/src/org/apache/commons/io/filefilter/HiddenFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/HiddenFileFilter.java
@@ -1,76 +1,76 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter accepts <code>File</code>s that are hidden.

- * <p>

- * Example, showing how to print out a list of the

- * current directory's <i>hidden</i> files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( HiddenFileFilter.HIDDEN );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * <p>

- * Example, showing how to print out a list of the

- * current directory's <i>visible</i> (i.e. not hidden) files:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( HiddenFileFilter.VISIBLE );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 587916 $

- */

-public class HiddenFileFilter extends AbstractFileFilter implements Serializable {

-    

-    /** Singleton instance of <i>hidden</i> filter */

-    public static final IOFileFilter HIDDEN  = new HiddenFileFilter();

-    

-    /** Singleton instance of <i>visible</i> filter */

-    public static final IOFileFilter VISIBLE = new NotFileFilter(HIDDEN);

-    

-    /**

-     * Restrictive consructor.

-     */

-    protected HiddenFileFilter() {

-    }

-    

-    /**

-     * Checks to see if the file is hidden.

-     * 

-     * @param file  the File to check

-     * @return <code>true</code> if the file is

-     *  <i>hidden</i>, otherwise <code>false</code>.

-     */

-    public boolean accept(File file) {

-        return file.isHidden();

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter accepts <code>File</code>s that are hidden.
+ * <p>
+ * Example, showing how to print out a list of the
+ * current directory's <i>hidden</i> files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( HiddenFileFilter.HIDDEN );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the
+ * current directory's <i>visible</i> (i.e. not hidden) files:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( HiddenFileFilter.VISIBLE );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 587916 $
+ */
+public class HiddenFileFilter extends AbstractFileFilter implements Serializable {
+    
+    /** Singleton instance of <i>hidden</i> filter */
+    public static final IOFileFilter HIDDEN  = new HiddenFileFilter();
+    
+    /** Singleton instance of <i>visible</i> filter */
+    public static final IOFileFilter VISIBLE = new NotFileFilter(HIDDEN);
+    
+    /**
+     * Restrictive consructor.
+     */
+    protected HiddenFileFilter() {
+    }
+    
+    /**
+     * Checks to see if the file is hidden.
+     * 
+     * @param file  the File to check
+     * @return <code>true</code> if the file is
+     *  <i>hidden</i>, otherwise <code>false</code>.
+     */
+    public boolean accept(File file) {
+        return file.isHidden();
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/IOFileFilter.java b/src/org/apache/commons/io/filefilter/IOFileFilter.java
index 5ebd827..cd650c1 100644
--- a/src/org/apache/commons/io/filefilter/IOFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/IOFileFilter.java
@@ -1,55 +1,55 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.FileFilter;

-import java.io.FilenameFilter;

-

-/**

- * An interface which brings the FileFilter and FilenameFilter 

- * interfaces together.

- * 

- * @since Commons IO 1.0

- * @version $Revision: 471628 $ $Date: 2006-11-06 04:06:45 +0000 (Mon, 06 Nov 2006) $

- * 

- * @author Stephen Colebourne

- */

-public interface IOFileFilter extends FileFilter, FilenameFilter {

-

-    /**

-     * Checks to see if the File should be accepted by this filter.

-     * <p>

-     * Defined in {@link java.io.FileFilter}.

-     * 

-     * @param file  the File to check

-     * @return true if this file matches the test

-     */

-    public boolean accept(File file);

-

-    /**

-     * Checks to see if the File should be accepted by this filter.

-     * <p>

-     * Defined in {@link java.io.FilenameFilter}.

-     * 

-     * @param dir  the directory File to check

-     * @param name  the filename within the directory to check

-     * @return true if this file matches the test

-     */

-    public boolean accept(File dir, String name);

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+
+/**
+ * An interface which brings the FileFilter and FilenameFilter 
+ * interfaces together.
+ * 
+ * @since Commons IO 1.0
+ * @version $Revision: 471628 $ $Date: 2006-11-06 04:06:45 +0000 (Mon, 06 Nov 2006) $
+ * 
+ * @author Stephen Colebourne
+ */
+public interface IOFileFilter extends FileFilter, FilenameFilter {
+
+    /**
+     * Checks to see if the File should be accepted by this filter.
+     * <p>
+     * Defined in {@link java.io.FileFilter}.
+     * 
+     * @param file  the File to check
+     * @return true if this file matches the test
+     */
+    public boolean accept(File file);
+
+    /**
+     * Checks to see if the File should be accepted by this filter.
+     * <p>
+     * Defined in {@link java.io.FilenameFilter}.
+     * 
+     * @param dir  the directory File to check
+     * @param name  the filename within the directory to check
+     * @return true if this file matches the test
+     */
+    public boolean accept(File dir, String name);
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/NameFileFilter.java b/src/org/apache/commons/io/filefilter/NameFileFilter.java
index 19fe380..d24db13 100644
--- a/src/org/apache/commons/io/filefilter/NameFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/NameFileFilter.java
@@ -1,194 +1,194 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.List;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Filters filenames for a certain name.

- * <p>

- * For example, to print all files and directories in the

- * current directory whose name is <code>Test</code>:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( new NameFileFilter("Test") );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.0

- * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- *

- * @author Stephen Colebourne

- * @author Federico Barbieri

- * @author Serge Knystautas

- * @author Peter Donald

- */

-public class NameFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The filenames to search for */

-    private final String[] names;

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Constructs a new case-sensitive name file filter for a single name.

-     *

-     * @param name  the name to allow, must not be null

-     * @throws IllegalArgumentException if the name is null

-     */

-    public NameFileFilter(String name) {

-        this(name, null);

-    }

-

-    /**

-     * Construct a new name file filter specifying case-sensitivity.

-     *

-     * @param name  the name to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the name is null

-     */

-    public NameFileFilter(String name, IOCase caseSensitivity) {

-        if (name == null) {

-            throw new IllegalArgumentException("The wildcard must not be null");

-        }

-        this.names = new String[] {name};

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new case-sensitive name file filter for an array of names.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param names  the names to allow, must not be null

-     * @throws IllegalArgumentException if the names array is null

-     */

-    public NameFileFilter(String[] names) {

-        this(names, null);

-    }

-

-    /**

-     * Constructs a new name file filter for an array of names specifying case-sensitivity.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param names  the names to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the names array is null

-     */

-    public NameFileFilter(String[] names, IOCase caseSensitivity) {

-        if (names == null) {

-            throw new IllegalArgumentException("The array of names must not be null");

-        }

-        this.names = names;

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new case-sensitive name file filter for a list of names.

-     *

-     * @param names  the names to allow, must not be null

-     * @throws IllegalArgumentException if the name list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public NameFileFilter(List<String> names) {

-        this(names, null);

-    }

-

-    /**

-     * Constructs a new name file filter for a list of names specifying case-sensitivity.

-     *

-     * @param names  the names to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the name list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public NameFileFilter(List<String> names, IOCase caseSensitivity) {

-        if (names == null) {

-            throw new IllegalArgumentException("The list of names must not be null");

-        }

-        this.names = names.toArray(new String[names.size()]);

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks to see if the filename matches.

-     *

-     * @param file  the File to check

-     * @return true if the filename matches

-     */

-    @Override

-    public boolean accept(File file) {

-        String name = file.getName();

-        for (int i = 0; i < this.names.length; i++) {

-            if (caseSensitivity.checkEquals(name, names[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Checks to see if the filename matches.

-     *

-     * @param file  the File directory

-     * @param name  the filename

-     * @return true if the filename matches

-     */

-    @Override

-    public boolean accept(File file, String name) {

-        for (int i = 0; i < names.length; i++) {

-            if (caseSensitivity.checkEquals(name, names[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (names != null) {

-            for (int i = 0; i < names.length; i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                buffer.append(names[i]);

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Filters filenames for a certain name.
+ * <p>
+ * For example, to print all files and directories in the
+ * current directory whose name is <code>Test</code>:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( new NameFileFilter("Test") );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ *
+ * @author Stephen Colebourne
+ * @author Federico Barbieri
+ * @author Serge Knystautas
+ * @author Peter Donald
+ */
+public class NameFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The filenames to search for */
+    private final String[] names;
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Constructs a new case-sensitive name file filter for a single name.
+     *
+     * @param name  the name to allow, must not be null
+     * @throws IllegalArgumentException if the name is null
+     */
+    public NameFileFilter(String name) {
+        this(name, null);
+    }
+
+    /**
+     * Construct a new name file filter specifying case-sensitivity.
+     *
+     * @param name  the name to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the name is null
+     */
+    public NameFileFilter(String name, IOCase caseSensitivity) {
+        if (name == null) {
+            throw new IllegalArgumentException("The wildcard must not be null");
+        }
+        this.names = new String[] {name};
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new case-sensitive name file filter for an array of names.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param names  the names to allow, must not be null
+     * @throws IllegalArgumentException if the names array is null
+     */
+    public NameFileFilter(String[] names) {
+        this(names, null);
+    }
+
+    /**
+     * Constructs a new name file filter for an array of names specifying case-sensitivity.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param names  the names to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the names array is null
+     */
+    public NameFileFilter(String[] names, IOCase caseSensitivity) {
+        if (names == null) {
+            throw new IllegalArgumentException("The array of names must not be null");
+        }
+        this.names = names;
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new case-sensitive name file filter for a list of names.
+     *
+     * @param names  the names to allow, must not be null
+     * @throws IllegalArgumentException if the name list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public NameFileFilter(List<String> names) {
+        this(names, null);
+    }
+
+    /**
+     * Constructs a new name file filter for a list of names specifying case-sensitivity.
+     *
+     * @param names  the names to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the name list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public NameFileFilter(List<String> names, IOCase caseSensitivity) {
+        if (names == null) {
+            throw new IllegalArgumentException("The list of names must not be null");
+        }
+        this.names = names.toArray(new String[names.size()]);
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks to see if the filename matches.
+     *
+     * @param file  the File to check
+     * @return true if the filename matches
+     */
+    @Override
+    public boolean accept(File file) {
+        String name = file.getName();
+        for (int i = 0; i < this.names.length; i++) {
+            if (caseSensitivity.checkEquals(name, names[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if the filename matches.
+     *
+     * @param file  the File directory
+     * @param name  the filename
+     * @return true if the filename matches
+     */
+    @Override
+    public boolean accept(File file, String name) {
+        for (int i = 0; i < names.length; i++) {
+            if (caseSensitivity.checkEquals(name, names[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (names != null) {
+            for (int i = 0; i < names.length; i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                buffer.append(names[i]);
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/NotFileFilter.java b/src/org/apache/commons/io/filefilter/NotFileFilter.java
index 710c8ec..3991f5d 100644
--- a/src/org/apache/commons/io/filefilter/NotFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/NotFileFilter.java
@@ -1,78 +1,78 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * This filter produces a logical NOT of the filters specified.

- *

- * @since Commons IO 1.0

- * @version $Revision: 591058 $ $Date: 2007-11-01 15:47:05 +0000 (Thu, 01 Nov 2007) $

- * 

- * @author Stephen Colebourne

- */

-public class NotFileFilter extends AbstractFileFilter implements Serializable {

-    

-    /** The filter */

-    private final IOFileFilter filter;

-

-    /**

-     * Constructs a new file filter that NOTs the result of another filters.

-     * 

-     * @param filter  the filter, must not be null

-     * @throws IllegalArgumentException if the filter is null

-     */

-    public NotFileFilter(IOFileFilter filter) {

-        if (filter == null) {

-            throw new IllegalArgumentException("The filter must not be null");

-        }

-        this.filter = filter;

-    }

-

-    /**

-     * Checks to see if both filters are true.

-     * 

-     * @param file  the File to check

-     * @return true if the filter returns false

-     */

-    public boolean accept(File file) {

-        return ! filter.accept(file);

-    }

-    

-    /**

-     * Checks to see if both filters are true.

-     * 

-     * @param file  the File directory

-     * @param name  the filename

-     * @return true if the filter returns false

-     */

-    public boolean accept(File file, String name) {

-        return ! filter.accept(file, name);

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    public String toString() {

-        return super.toString() + "(" + filter.toString()  + ")";

-    }

-    

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * This filter produces a logical NOT of the filters specified.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 591058 $ $Date: 2007-11-01 15:47:05 +0000 (Thu, 01 Nov 2007) $
+ * 
+ * @author Stephen Colebourne
+ */
+public class NotFileFilter extends AbstractFileFilter implements Serializable {
+    
+    /** The filter */
+    private final IOFileFilter filter;
+
+    /**
+     * Constructs a new file filter that NOTs the result of another filters.
+     * 
+     * @param filter  the filter, must not be null
+     * @throws IllegalArgumentException if the filter is null
+     */
+    public NotFileFilter(IOFileFilter filter) {
+        if (filter == null) {
+            throw new IllegalArgumentException("The filter must not be null");
+        }
+        this.filter = filter;
+    }
+
+    /**
+     * Checks to see if both filters are true.
+     * 
+     * @param file  the File to check
+     * @return true if the filter returns false
+     */
+    public boolean accept(File file) {
+        return ! filter.accept(file);
+    }
+    
+    /**
+     * Checks to see if both filters are true.
+     * 
+     * @param file  the File directory
+     * @param name  the filename
+     * @return true if the filter returns false
+     */
+    public boolean accept(File file, String name) {
+        return ! filter.accept(file, name);
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    public String toString() {
+        return super.toString() + "(" + filter.toString()  + ")";
+    }
+    
+}
diff --git a/src/org/apache/commons/io/filefilter/OrFileFilter.java b/src/org/apache/commons/io/filefilter/OrFileFilter.java
index 520eefa..f8640be 100644
--- a/src/org/apache/commons/io/filefilter/OrFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/OrFileFilter.java
@@ -1,164 +1,164 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * A {@link java.io.FileFilter} providing conditional OR logic across a list of

- * file filters. This filter returns <code>true</code> if any filters in the

- * list return <code>true</code>. Otherwise, it returns <code>false</code>.

- * Checking of the file filter list stops when the first filter returns

- * <code>true</code>.

- *

- * @since Commons IO 1.0

- * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- *

- * @author Steven Caswell

- */

-public class OrFileFilter

-        extends AbstractFileFilter

-        implements ConditionalFileFilter, Serializable {

-

-    /** The list of file filters. */

-    private List<IOFileFilter> fileFilters;

-

-    /**

-     * Constructs a new instance of <code>OrFileFilter</code>.

-     *

-     * @since Commons IO 1.1

-     */

-    public OrFileFilter() {

-        this.fileFilters = new ArrayList<IOFileFilter>();

-    }

-

-    /**

-     * Constructs a new instance of <code>OrFileFilter</code>

-     * with the specified filters.

-     *

-     * @param fileFilters  the file filters for this filter, copied, null ignored

-     * @since Commons IO 1.1

-     */

-    public OrFileFilter(final List<IOFileFilter> fileFilters) {

-        if (fileFilters == null) {

-            this.fileFilters = new ArrayList<IOFileFilter>();

-        } else {

-            this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);

-        }

-    }

-

-    /**

-     * Constructs a new file filter that ORs the result of two other filters.

-     *

-     * @param filter1  the first filter, must not be null

-     * @param filter2  the second filter, must not be null

-     * @throws IllegalArgumentException if either filter is null

-     */

-    public OrFileFilter(IOFileFilter filter1, IOFileFilter filter2) {

-        if (filter1 == null || filter2 == null) {

-            throw new IllegalArgumentException("The filters must not be null");

-        }

-        this.fileFilters = new ArrayList<IOFileFilter>();

-        addFileFilter(filter1);

-        addFileFilter(filter2);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void addFileFilter(final IOFileFilter ioFileFilter) {

-        this.fileFilters.add(ioFileFilter);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public List<IOFileFilter> getFileFilters() {

-        return Collections.unmodifiableList(this.fileFilters);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public boolean removeFileFilter(IOFileFilter ioFileFilter) {

-        return this.fileFilters.remove(ioFileFilter);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void setFileFilters(final List<IOFileFilter> fileFilters) {

-        this.fileFilters = fileFilters;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    @Override

-    public boolean accept(final File file) {

-        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {

-            IOFileFilter fileFilter = iter.next();

-            if (fileFilter.accept(file)) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    @Override

-    public boolean accept(final File file, final String name) {

-        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {

-            IOFileFilter fileFilter = iter.next();

-            if (fileFilter.accept(file, name)) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (fileFilters != null) {

-            for (int i = 0; i < fileFilters.size(); i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                Object filter = fileFilters.get(i);

-                buffer.append(filter == null ? "null" : filter.toString());

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A {@link java.io.FileFilter} providing conditional OR logic across a list of
+ * file filters. This filter returns <code>true</code> if any filters in the
+ * list return <code>true</code>. Otherwise, it returns <code>false</code>.
+ * Checking of the file filter list stops when the first filter returns
+ * <code>true</code>.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ *
+ * @author Steven Caswell
+ */
+public class OrFileFilter
+        extends AbstractFileFilter
+        implements ConditionalFileFilter, Serializable {
+
+    /** The list of file filters. */
+    private List<IOFileFilter> fileFilters;
+
+    /**
+     * Constructs a new instance of <code>OrFileFilter</code>.
+     *
+     * @since Commons IO 1.1
+     */
+    public OrFileFilter() {
+        this.fileFilters = new ArrayList<IOFileFilter>();
+    }
+
+    /**
+     * Constructs a new instance of <code>OrFileFilter</code>
+     * with the specified filters.
+     *
+     * @param fileFilters  the file filters for this filter, copied, null ignored
+     * @since Commons IO 1.1
+     */
+    public OrFileFilter(final List<IOFileFilter> fileFilters) {
+        if (fileFilters == null) {
+            this.fileFilters = new ArrayList<IOFileFilter>();
+        } else {
+            this.fileFilters = new ArrayList<IOFileFilter>(fileFilters);
+        }
+    }
+
+    /**
+     * Constructs a new file filter that ORs the result of two other filters.
+     *
+     * @param filter1  the first filter, must not be null
+     * @param filter2  the second filter, must not be null
+     * @throws IllegalArgumentException if either filter is null
+     */
+    public OrFileFilter(IOFileFilter filter1, IOFileFilter filter2) {
+        if (filter1 == null || filter2 == null) {
+            throw new IllegalArgumentException("The filters must not be null");
+        }
+        this.fileFilters = new ArrayList<IOFileFilter>();
+        addFileFilter(filter1);
+        addFileFilter(filter2);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addFileFilter(final IOFileFilter ioFileFilter) {
+        this.fileFilters.add(ioFileFilter);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<IOFileFilter> getFileFilters() {
+        return Collections.unmodifiableList(this.fileFilters);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean removeFileFilter(IOFileFilter ioFileFilter) {
+        return this.fileFilters.remove(ioFileFilter);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setFileFilters(final List<IOFileFilter> fileFilters) {
+        this.fileFilters = fileFilters;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(final File file) {
+        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {
+            IOFileFilter fileFilter = iter.next();
+            if (fileFilter.accept(file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(final File file, final String name) {
+        for (Iterator<IOFileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) {
+            IOFileFilter fileFilter = iter.next();
+            if (fileFilter.accept(file, name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (fileFilters != null) {
+            for (int i = 0; i < fileFilters.size(); i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                Object filter = fileFilters.get(i);
+                buffer.append(filter == null ? "null" : filter.toString());
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/PrefixFileFilter.java b/src/org/apache/commons/io/filefilter/PrefixFileFilter.java
index 8c6b737..76d5865 100644
--- a/src/org/apache/commons/io/filefilter/PrefixFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/PrefixFileFilter.java
@@ -1,200 +1,200 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.List;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Filters filenames for a certain prefix.

- * <p>

- * For example, to print all files and directories in the

- * current directory whose name starts with <code>Test</code>:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( new PrefixFileFilter("Test") );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.0

- * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- *

- * @author Stephen Colebourne

- * @author Federico Barbieri

- * @author Serge Knystautas

- * @author Peter Donald

- */

-public class PrefixFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The filename prefixes to search for */

-    private final String[] prefixes;

-

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Constructs a new Prefix file filter for a single prefix.

-     *

-     * @param prefix  the prefix to allow, must not be null

-     * @throws IllegalArgumentException if the prefix is null

-     */

-    public PrefixFileFilter(String prefix) {

-        this(prefix, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Prefix file filter for a single prefix

-     * specifying case-sensitivity.

-     *

-     * @param prefix  the prefix to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the prefix is null

-     * @since Commons IO 1.4

-     */

-    public PrefixFileFilter(String prefix, IOCase caseSensitivity) {

-        if (prefix == null) {

-            throw new IllegalArgumentException("The prefix must not be null");

-        }

-        this.prefixes = new String[] {prefix};

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new Prefix file filter for any of an array of prefixes.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param prefixes  the prefixes to allow, must not be null

-     * @throws IllegalArgumentException if the prefix array is null

-     */

-    public PrefixFileFilter(String[] prefixes) {

-        this(prefixes, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Prefix file filter for any of an array of prefixes

-     * specifying case-sensitivity.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param prefixes  the prefixes to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the prefix is null

-     * @since Commons IO 1.4

-     */

-    public PrefixFileFilter(String[] prefixes, IOCase caseSensitivity) {

-        if (prefixes == null) {

-            throw new IllegalArgumentException("The array of prefixes must not be null");

-        }

-        this.prefixes = prefixes;

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new Prefix file filter for a list of prefixes.

-     *

-     * @param prefixes  the prefixes to allow, must not be null

-     * @throws IllegalArgumentException if the prefix list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public PrefixFileFilter(List<String> prefixes) {

-        this(prefixes, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Prefix file filter for a list of prefixes

-     * specifying case-sensitivity.

-     *

-     * @param prefixes  the prefixes to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the prefix list is null

-     * @throws ClassCastException if the list does not contain Strings

-     * @since Commons IO 1.4

-     */

-    public PrefixFileFilter(List<String> prefixes, IOCase caseSensitivity) {

-        if (prefixes == null) {

-            throw new IllegalArgumentException("The list of prefixes must not be null");

-        }

-        this.prefixes = prefixes.toArray(new String[prefixes.size()]);

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Checks to see if the filename starts with the prefix.

-     *

-     * @param file  the File to check

-     * @return true if the filename starts with one of our prefixes

-     */

-    @Override

-    public boolean accept(File file) {

-        String name = file.getName();

-        for (int i = 0; i < this.prefixes.length; i++) {

-            if (caseSensitivity.checkStartsWith(name, prefixes[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Checks to see if the filename starts with the prefix.

-     *

-     * @param file  the File directory

-     * @param name  the filename

-     * @return true if the filename starts with one of our prefixes

-     */

-    @Override

-    public boolean accept(File file, String name) {

-        for (int i = 0; i < prefixes.length; i++) {

-            if (caseSensitivity.checkStartsWith(name, prefixes[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (prefixes != null) {

-            for (int i = 0; i < prefixes.length; i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                buffer.append(prefixes[i]);

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Filters filenames for a certain prefix.
+ * <p>
+ * For example, to print all files and directories in the
+ * current directory whose name starts with <code>Test</code>:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( new PrefixFileFilter("Test") );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ *
+ * @author Stephen Colebourne
+ * @author Federico Barbieri
+ * @author Serge Knystautas
+ * @author Peter Donald
+ */
+public class PrefixFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The filename prefixes to search for */
+    private final String[] prefixes;
+
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Constructs a new Prefix file filter for a single prefix.
+     *
+     * @param prefix  the prefix to allow, must not be null
+     * @throws IllegalArgumentException if the prefix is null
+     */
+    public PrefixFileFilter(String prefix) {
+        this(prefix, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Prefix file filter for a single prefix
+     * specifying case-sensitivity.
+     *
+     * @param prefix  the prefix to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the prefix is null
+     * @since Commons IO 1.4
+     */
+    public PrefixFileFilter(String prefix, IOCase caseSensitivity) {
+        if (prefix == null) {
+            throw new IllegalArgumentException("The prefix must not be null");
+        }
+        this.prefixes = new String[] {prefix};
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new Prefix file filter for any of an array of prefixes.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param prefixes  the prefixes to allow, must not be null
+     * @throws IllegalArgumentException if the prefix array is null
+     */
+    public PrefixFileFilter(String[] prefixes) {
+        this(prefixes, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Prefix file filter for any of an array of prefixes
+     * specifying case-sensitivity.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param prefixes  the prefixes to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the prefix is null
+     * @since Commons IO 1.4
+     */
+    public PrefixFileFilter(String[] prefixes, IOCase caseSensitivity) {
+        if (prefixes == null) {
+            throw new IllegalArgumentException("The array of prefixes must not be null");
+        }
+        this.prefixes = prefixes;
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new Prefix file filter for a list of prefixes.
+     *
+     * @param prefixes  the prefixes to allow, must not be null
+     * @throws IllegalArgumentException if the prefix list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public PrefixFileFilter(List<String> prefixes) {
+        this(prefixes, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Prefix file filter for a list of prefixes
+     * specifying case-sensitivity.
+     *
+     * @param prefixes  the prefixes to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the prefix list is null
+     * @throws ClassCastException if the list does not contain Strings
+     * @since Commons IO 1.4
+     */
+    public PrefixFileFilter(List<String> prefixes, IOCase caseSensitivity) {
+        if (prefixes == null) {
+            throw new IllegalArgumentException("The list of prefixes must not be null");
+        }
+        this.prefixes = prefixes.toArray(new String[prefixes.size()]);
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Checks to see if the filename starts with the prefix.
+     *
+     * @param file  the File to check
+     * @return true if the filename starts with one of our prefixes
+     */
+    @Override
+    public boolean accept(File file) {
+        String name = file.getName();
+        for (int i = 0; i < this.prefixes.length; i++) {
+            if (caseSensitivity.checkStartsWith(name, prefixes[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if the filename starts with the prefix.
+     *
+     * @param file  the File directory
+     * @param name  the filename
+     * @return true if the filename starts with one of our prefixes
+     */
+    @Override
+    public boolean accept(File file, String name) {
+        for (int i = 0; i < prefixes.length; i++) {
+            if (caseSensitivity.checkStartsWith(name, prefixes[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (prefixes != null) {
+            for (int i = 0; i < prefixes.length; i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                buffer.append(prefixes[i]);
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/RegexFileFilter.java b/src/org/apache/commons/io/filefilter/RegexFileFilter.java
index b49a1bd..d47d834 100644
--- a/src/org/apache/commons/io/filefilter/RegexFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/RegexFileFilter.java
@@ -1,122 +1,122 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.regex.Pattern;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Filters files using supplied regular expression(s).

- * <p/>

- * See java.util.regex.Pattern for regex matching rules

- * <p/>

- *

- * <p/>

- * e.g.

- * <pre>

- * File dir = new File(".");

- * FileFilter fileFilter = new RegexFileFilter("^.*[tT]est(-\\d+)?\\.java$");

- * File[] files = dir.listFiles(fileFilter);

- * for (int i = 0; i < files.length; i++) {

- *   System.out.println(files[i]);

- * }

- * </pre>

- *

- * @author Oliver Siegmar

- * @version $Revision: 606381 $

- * @since Commons IO 1.4

- */

-public class RegexFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The regular expression pattern that will be used to match filenames */

-    private final Pattern pattern;

-

-    /**

-     * Construct a new regular expression filter.

-     *

-     * @param pattern regular string expression to match

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public RegexFileFilter(String pattern) {

-        if (pattern == null) {

-            throw new IllegalArgumentException("Pattern is missing");

-        }

-

-        this.pattern = Pattern.compile(pattern);

-    }

-

-    /**

-     * Construct a new regular expression filter with the specified flags case sensitivity.

-     *

-     * @param pattern regular string expression to match

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public RegexFileFilter(String pattern, IOCase caseSensitivity) {

-        if (pattern == null) {

-            throw new IllegalArgumentException("Pattern is missing");

-        }

-        int flags = 0;

-        if (caseSensitivity != null && !caseSensitivity.isCaseSensitive()) {

-            flags = Pattern.CASE_INSENSITIVE;

-        }

-        this.pattern = Pattern.compile(pattern, flags);

-    }

-

-    /**

-     * Construct a new regular expression filter with the specified flags.

-     *

-     * @param pattern regular string expression to match

-     * @param flags pattern flags - e.g. {@link Pattern#CASE_INSENSITIVE}

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public RegexFileFilter(String pattern, int flags) {

-        if (pattern == null) {

-            throw new IllegalArgumentException("Pattern is missing");

-        }

-        this.pattern = Pattern.compile(pattern, flags);

-    }

-

-    /**

-     * Construct a new regular expression filter for a compiled regular expression

-     *

-     * @param pattern regular expression to match

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public RegexFileFilter(Pattern pattern) {

-        if (pattern == null) {

-            throw new IllegalArgumentException("Pattern is missing");

-        }

-

-        this.pattern = pattern;

-    }

-

-    /**

-     * Checks to see if the filename matches one of the regular expressions.

-     *

-     * @param dir   the file directory

-     * @param name  the filename

-     * @return true if the filename matches one of the regular expressions

-     */

-    public boolean accept(File dir, String name) {

-        return (pattern.matcher(name).matches());

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Filters files using supplied regular expression(s).
+ * <p/>
+ * See java.util.regex.Pattern for regex matching rules
+ * <p/>
+ *
+ * <p/>
+ * e.g.
+ * <pre>
+ * File dir = new File(".");
+ * FileFilter fileFilter = new RegexFileFilter("^.*[tT]est(-\\d+)?\\.java$");
+ * File[] files = dir.listFiles(fileFilter);
+ * for (int i = 0; i < files.length; i++) {
+ *   System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @author Oliver Siegmar
+ * @version $Revision: 606381 $
+ * @since Commons IO 1.4
+ */
+public class RegexFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The regular expression pattern that will be used to match filenames */
+    private final Pattern pattern;
+
+    /**
+     * Construct a new regular expression filter.
+     *
+     * @param pattern regular string expression to match
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public RegexFileFilter(String pattern) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern is missing");
+        }
+
+        this.pattern = Pattern.compile(pattern);
+    }
+
+    /**
+     * Construct a new regular expression filter with the specified flags case sensitivity.
+     *
+     * @param pattern regular string expression to match
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public RegexFileFilter(String pattern, IOCase caseSensitivity) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern is missing");
+        }
+        int flags = 0;
+        if (caseSensitivity != null && !caseSensitivity.isCaseSensitive()) {
+            flags = Pattern.CASE_INSENSITIVE;
+        }
+        this.pattern = Pattern.compile(pattern, flags);
+    }
+
+    /**
+     * Construct a new regular expression filter with the specified flags.
+     *
+     * @param pattern regular string expression to match
+     * @param flags pattern flags - e.g. {@link Pattern#CASE_INSENSITIVE}
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public RegexFileFilter(String pattern, int flags) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern is missing");
+        }
+        this.pattern = Pattern.compile(pattern, flags);
+    }
+
+    /**
+     * Construct a new regular expression filter for a compiled regular expression
+     *
+     * @param pattern regular expression to match
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public RegexFileFilter(Pattern pattern) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern is missing");
+        }
+
+        this.pattern = pattern;
+    }
+
+    /**
+     * Checks to see if the filename matches one of the regular expressions.
+     *
+     * @param dir   the file directory
+     * @param name  the filename
+     * @return true if the filename matches one of the regular expressions
+     */
+    public boolean accept(File dir, String name) {
+        return (pattern.matcher(name).matches());
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/SizeFileFilter.java b/src/org/apache/commons/io/filefilter/SizeFileFilter.java
index 614e424..1e97a42 100644
--- a/src/org/apache/commons/io/filefilter/SizeFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/SizeFileFilter.java
@@ -1,103 +1,103 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * Filters files based on size, can filter either smaller files or

- * files equal to or larger than a given threshold.

- * <p>

- * For example, to print all files and directories in the

- * current directory whose size is greater than 1 MB:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( new SizeFileFilter(1024 * 1024) );

- * for ( int i = 0; i &lt; files.length; i++ ) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @author Rahul Akolkar

- * @version $Id: SizeFileFilter.java 591058 2007-11-01 15:47:05Z niallp $

- * @since Commons IO 1.2

- */

-public class SizeFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The size threshold. */

-    private final long size;

-    /** Whether the files accepted will be larger or smaller. */

-    private final boolean acceptLarger;

-

-    /**

-     * Constructs a new size file filter for files equal to or 

-     * larger than a certain size.

-     *

-     * @param size  the threshold size of the files

-     * @throws IllegalArgumentException if the size is negative

-     */

-    public SizeFileFilter(long size) {

-        this(size, true);

-    }

-

-    /**

-     * Constructs a new size file filter for files based on a certain size

-     * threshold.

-     *

-     * @param size  the threshold size of the files

-     * @param acceptLarger  if true, files equal to or larger are accepted,

-     * otherwise smaller ones (but not equal to)

-     * @throws IllegalArgumentException if the size is negative

-     */

-    public SizeFileFilter(long size, boolean acceptLarger) {

-        if (size < 0) {

-            throw new IllegalArgumentException("The size must be non-negative");

-        }

-        this.size = size;

-        this.acceptLarger = acceptLarger;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks to see if the size of the file is favorable.

-     * <p>

-     * If size equals threshold and smaller files are required,

-     * file <b>IS NOT</b> selected.

-     * If size equals threshold and larger files are required,

-     * file <b>IS</b> selected.

-     *

-     * @param file  the File to check

-     * @return true if the filename matches

-     */

-    public boolean accept(File file) {

-        boolean smaller = file.length() < size;

-        return acceptLarger ? !smaller : smaller;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    public String toString() {

-        String condition = acceptLarger ? ">=" : "<";

-        return super.toString() + "(" + condition + 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Filters files based on size, can filter either smaller files or
+ * files equal to or larger than a given threshold.
+ * <p>
+ * For example, to print all files and directories in the
+ * current directory whose size is greater than 1 MB:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( new SizeFileFilter(1024 * 1024) );
+ * for ( int i = 0; i &lt; files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @author Rahul Akolkar
+ * @version $Id: SizeFileFilter.java 591058 2007-11-01 15:47:05Z niallp $
+ * @since Commons IO 1.2
+ */
+public class SizeFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The size threshold. */
+    private final long size;
+    /** Whether the files accepted will be larger or smaller. */
+    private final boolean acceptLarger;
+
+    /**
+     * Constructs a new size file filter for files equal to or 
+     * larger than a certain size.
+     *
+     * @param size  the threshold size of the files
+     * @throws IllegalArgumentException if the size is negative
+     */
+    public SizeFileFilter(long size) {
+        this(size, true);
+    }
+
+    /**
+     * Constructs a new size file filter for files based on a certain size
+     * threshold.
+     *
+     * @param size  the threshold size of the files
+     * @param acceptLarger  if true, files equal to or larger are accepted,
+     * otherwise smaller ones (but not equal to)
+     * @throws IllegalArgumentException if the size is negative
+     */
+    public SizeFileFilter(long size, boolean acceptLarger) {
+        if (size < 0) {
+            throw new IllegalArgumentException("The size must be non-negative");
+        }
+        this.size = size;
+        this.acceptLarger = acceptLarger;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks to see if the size of the file is favorable.
+     * <p>
+     * If size equals threshold and smaller files are required,
+     * file <b>IS NOT</b> selected.
+     * If size equals threshold and larger files are required,
+     * file <b>IS</b> selected.
+     *
+     * @param file  the File to check
+     * @return true if the filename matches
+     */
+    public boolean accept(File file) {
+        boolean smaller = file.length() < size;
+        return acceptLarger ? !smaller : smaller;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    public String toString() {
+        String condition = acceptLarger ? ">=" : "<";
+        return super.toString() + "(" + condition + size + ")";
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/SuffixFileFilter.java b/src/org/apache/commons/io/filefilter/SuffixFileFilter.java
index 8bdf781..31080a5 100644
--- a/src/org/apache/commons/io/filefilter/SuffixFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/SuffixFileFilter.java
@@ -1,201 +1,201 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.List;

-

-import org.apache.commons.io.IOCase;

-

-/**

- * Filters files based on the suffix (what the filename ends with).

- * This is used in retrieving all the files of a particular type.

- * <p>

- * For example, to retrieve and print all <code>*.java</code> files

- * in the current directory:

- *

- * <pre>

- * File dir = new File(".");

- * String[] files = dir.list( new SuffixFileFilter(".java") );

- * for (int i = 0; i &lt; files.length; i++) {

- *     System.out.println(files[i]);

- * }

- * </pre>

- *

- * @since Commons IO 1.0

- * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- *

- * @author Stephen Colebourne

- * @author Federico Barbieri

- * @author Serge Knystautas

- * @author Peter Donald

- */

-public class SuffixFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The filename suffixes to search for */

-    private final String[] suffixes;

-

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Constructs a new Suffix file filter for a single extension.

-     *

-     * @param suffix  the suffix to allow, must not be null

-     * @throws IllegalArgumentException if the suffix is null

-     */

-    public SuffixFileFilter(String suffix) {

-        this(suffix, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Suffix file filter for a single extension

-     * specifying case-sensitivity.

-     *

-     * @param suffix  the suffix to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the suffix is null

-     * @since Commons IO 1.4

-     */

-    public SuffixFileFilter(String suffix, IOCase caseSensitivity) {

-        if (suffix == null) {

-            throw new IllegalArgumentException("The suffix must not be null");

-        }

-        this.suffixes = new String[] {suffix};

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new Suffix file filter for an array of suffixs.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param suffixes  the suffixes to allow, must not be null

-     * @throws IllegalArgumentException if the suffix array is null

-     */

-    public SuffixFileFilter(String[] suffixes) {

-        this(suffixes, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Suffix file filter for an array of suffixs

-     * specifying case-sensitivity.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param suffixes  the suffixes to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the suffix array is null

-     * @since Commons IO 1.4

-     */

-    public SuffixFileFilter(String[] suffixes, IOCase caseSensitivity) {

-        if (suffixes == null) {

-            throw new IllegalArgumentException("The array of suffixes must not be null");

-        }

-        this.suffixes = suffixes;

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Constructs a new Suffix file filter for a list of suffixes.

-     *

-     * @param suffixes  the suffixes to allow, must not be null

-     * @throws IllegalArgumentException if the suffix list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public SuffixFileFilter(List<String> suffixes) {

-        this(suffixes, IOCase.SENSITIVE);

-    }

-

-    /**

-     * Constructs a new Suffix file filter for a list of suffixes

-     * specifying case-sensitivity.

-     *

-     * @param suffixes  the suffixes to allow, must not be null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the suffix list is null

-     * @throws ClassCastException if the list does not contain Strings

-     * @since Commons IO 1.4

-     */

-    public SuffixFileFilter(List<String> suffixes, IOCase caseSensitivity) {

-        if (suffixes == null) {

-            throw new IllegalArgumentException("The list of suffixes must not be null");

-        }

-        this.suffixes = suffixes.toArray(new String[suffixes.size()]);

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Checks to see if the filename ends with the suffix.

-     *

-     * @param file  the File to check

-     * @return true if the filename ends with one of our suffixes

-     */

-    @Override

-    public boolean accept(File file) {

-        String name = file.getName();

-        for (int i = 0; i < this.suffixes.length; i++) {

-            if (caseSensitivity.checkEndsWith(name, suffixes[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Checks to see if the filename ends with the suffix.

-     *

-     * @param file  the File directory

-     * @param name  the filename

-     * @return true if the filename ends with one of our suffixes

-     */

-    @Override

-    public boolean accept(File file, String name) {

-        for (int i = 0; i < this.suffixes.length; i++) {

-            if (caseSensitivity.checkEndsWith(name, suffixes[i])) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (suffixes != null) {

-            for (int i = 0; i < suffixes.length; i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                buffer.append(suffixes[i]);

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.io.IOCase;
+
+/**
+ * Filters files based on the suffix (what the filename ends with).
+ * This is used in retrieving all the files of a particular type.
+ * <p>
+ * For example, to retrieve and print all <code>*.java</code> files
+ * in the current directory:
+ *
+ * <pre>
+ * File dir = new File(".");
+ * String[] files = dir.list( new SuffixFileFilter(".java") );
+ * for (int i = 0; i &lt; files.length; i++) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ *
+ * @author Stephen Colebourne
+ * @author Federico Barbieri
+ * @author Serge Knystautas
+ * @author Peter Donald
+ */
+public class SuffixFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The filename suffixes to search for */
+    private final String[] suffixes;
+
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Constructs a new Suffix file filter for a single extension.
+     *
+     * @param suffix  the suffix to allow, must not be null
+     * @throws IllegalArgumentException if the suffix is null
+     */
+    public SuffixFileFilter(String suffix) {
+        this(suffix, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Suffix file filter for a single extension
+     * specifying case-sensitivity.
+     *
+     * @param suffix  the suffix to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the suffix is null
+     * @since Commons IO 1.4
+     */
+    public SuffixFileFilter(String suffix, IOCase caseSensitivity) {
+        if (suffix == null) {
+            throw new IllegalArgumentException("The suffix must not be null");
+        }
+        this.suffixes = new String[] {suffix};
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new Suffix file filter for an array of suffixs.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param suffixes  the suffixes to allow, must not be null
+     * @throws IllegalArgumentException if the suffix array is null
+     */
+    public SuffixFileFilter(String[] suffixes) {
+        this(suffixes, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Suffix file filter for an array of suffixs
+     * specifying case-sensitivity.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param suffixes  the suffixes to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the suffix array is null
+     * @since Commons IO 1.4
+     */
+    public SuffixFileFilter(String[] suffixes, IOCase caseSensitivity) {
+        if (suffixes == null) {
+            throw new IllegalArgumentException("The array of suffixes must not be null");
+        }
+        this.suffixes = suffixes;
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Constructs a new Suffix file filter for a list of suffixes.
+     *
+     * @param suffixes  the suffixes to allow, must not be null
+     * @throws IllegalArgumentException if the suffix list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public SuffixFileFilter(List<String> suffixes) {
+        this(suffixes, IOCase.SENSITIVE);
+    }
+
+    /**
+     * Constructs a new Suffix file filter for a list of suffixes
+     * specifying case-sensitivity.
+     *
+     * @param suffixes  the suffixes to allow, must not be null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the suffix list is null
+     * @throws ClassCastException if the list does not contain Strings
+     * @since Commons IO 1.4
+     */
+    public SuffixFileFilter(List<String> suffixes, IOCase caseSensitivity) {
+        if (suffixes == null) {
+            throw new IllegalArgumentException("The list of suffixes must not be null");
+        }
+        this.suffixes = suffixes.toArray(new String[suffixes.size()]);
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Checks to see if the filename ends with the suffix.
+     *
+     * @param file  the File to check
+     * @return true if the filename ends with one of our suffixes
+     */
+    @Override
+    public boolean accept(File file) {
+        String name = file.getName();
+        for (int i = 0; i < this.suffixes.length; i++) {
+            if (caseSensitivity.checkEndsWith(name, suffixes[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if the filename ends with the suffix.
+     *
+     * @param file  the File directory
+     * @param name  the filename
+     * @return true if the filename ends with one of our suffixes
+     */
+    @Override
+    public boolean accept(File file, String name) {
+        for (int i = 0; i < this.suffixes.length; i++) {
+            if (caseSensitivity.checkEndsWith(name, suffixes[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (suffixes != null) {
+            for (int i = 0; i < suffixes.length; i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                buffer.append(suffixes[i]);
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/TrueFileFilter.java b/src/org/apache/commons/io/filefilter/TrueFileFilter.java
index be1b13a..9eb1e75 100644
--- a/src/org/apache/commons/io/filefilter/TrueFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/TrueFileFilter.java
@@ -1,72 +1,72 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-

-/**

- * A file filter that always returns true.

- *

- * @since Commons IO 1.0

- * @version $Revision: 587978 $ $Date: 2007-10-24 20:36:51 +0100 (Wed, 24 Oct 2007) $

- *

- * @author Stephen Colebourne

- */

-public class TrueFileFilter implements IOFileFilter, Serializable {

-

-    /**

-     * Singleton instance of true filter.

-     * @since Commons IO 1.3

-     */

-    public static final IOFileFilter TRUE = new TrueFileFilter();

-    /**

-     * Singleton instance of true filter.

-     * Please use the identical TrueFileFilter.TRUE constant.

-     * The new name is more JDK 1.5 friendly as it doesn't clash with other

-     * values when using static imports.

-     */

-    public static final IOFileFilter INSTANCE = TRUE;

-

-    /**

-     * Restrictive consructor.

-     */

-    protected TrueFileFilter() {

-    }

-

-    /**

-     * Returns true.

-     *

-     * @param file  the file to check

-     * @return true

-     */

-    public boolean accept(File file) {

-        return true;

-    }

-

-    /**

-     * Returns true.

-     *

-     * @param dir  the directory to check

-     * @param name  the filename

-     * @return true

-     */

-    public boolean accept(File dir, String name) {

-        return true;

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * A file filter that always returns true.
+ *
+ * @since Commons IO 1.0
+ * @version $Revision: 587978 $ $Date: 2007-10-24 20:36:51 +0100 (Wed, 24 Oct 2007) $
+ *
+ * @author Stephen Colebourne
+ */
+public class TrueFileFilter implements IOFileFilter, Serializable {
+
+    /**
+     * Singleton instance of true filter.
+     * @since Commons IO 1.3
+     */
+    public static final IOFileFilter TRUE = new TrueFileFilter();
+    /**
+     * Singleton instance of true filter.
+     * Please use the identical TrueFileFilter.TRUE constant.
+     * The new name is more JDK 1.5 friendly as it doesn't clash with other
+     * values when using static imports.
+     */
+    public static final IOFileFilter INSTANCE = TRUE;
+
+    /**
+     * Restrictive consructor.
+     */
+    protected TrueFileFilter() {
+    }
+
+    /**
+     * Returns true.
+     *
+     * @param file  the file to check
+     * @return true
+     */
+    public boolean accept(File file) {
+        return true;
+    }
+
+    /**
+     * Returns true.
+     *
+     * @param dir  the directory to check
+     * @param name  the filename
+     * @return true
+     */
+    public boolean accept(File dir, String name) {
+        return true;
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/WildcardFileFilter.java b/src/org/apache/commons/io/filefilter/WildcardFileFilter.java
index f3a1ee9..e9eddbb 100644
--- a/src/org/apache/commons/io/filefilter/WildcardFileFilter.java
+++ b/src/org/apache/commons/io/filefilter/WildcardFileFilter.java
@@ -1,199 +1,199 @@
-/*

- * 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.commons.io.filefilter;

-

-import java.io.File;

-import java.io.Serializable;

-import java.util.List;

-

-import org.apache.commons.io.FilenameUtils;

-import org.apache.commons.io.IOCase;

-

-/**

- * Filters files using the supplied wildcards.

- * <p>

- * This filter selects files and directories based on one or more wildcards.

- * Testing is case-sensitive by default, but this can be configured.

- * <p>

- * The wildcard matcher uses the characters '?' and '*' to represent a

- * single or multiple wildcard characters.

- * This is the same as often found on Dos/Unix command lines.

- * The extension check is case-sensitive by .

- * See {@link FilenameUtils#wildcardMatchOnSystem} for more information.

- * <p>

- * For example:

- * <pre>

- * File dir = new File(".");

- * FileFilter fileFilter = new WildcardFileFilter("*test*.java~*~");

- * File[] files = dir.listFiles(fileFilter);

- * for (int i = 0; i < files.length; i++) {

- *   System.out.println(files[i]);

- * }

- * </pre>

- *

- * @author Jason Anderson

- * @version $Revision: 155419 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $

- * @since Commons IO 1.3

- */

-public class WildcardFileFilter extends AbstractFileFilter implements Serializable {

-

-    /** The wildcards that will be used to match filenames. */

-    private final String[] wildcards;

-    /** Whether the comparison is case sensitive. */

-    private final IOCase caseSensitivity;

-

-    /**

-     * Construct a new case-sensitive wildcard filter for a single wildcard.

-     *

-     * @param wildcard  the wildcard to match

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public WildcardFileFilter(String wildcard) {

-        this(wildcard, null);

-    }

-

-    /**

-     * Construct a new wildcard filter for a single wildcard specifying case-sensitivity.

-     *

-     * @param wildcard  the wildcard to match, not null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the pattern is null

-     */

-    public WildcardFileFilter(String wildcard, IOCase caseSensitivity) {

-        if (wildcard == null) {

-            throw new IllegalArgumentException("The wildcard must not be null");

-        }

-        this.wildcards = new String[] { wildcard };

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Construct a new case-sensitive wildcard filter for an array of wildcards.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param wildcards  the array of wildcards to match

-     * @throws IllegalArgumentException if the pattern array is null

-     */

-    public WildcardFileFilter(String[] wildcards) {

-        this(wildcards, null);

-    }

-

-    /**

-     * Construct a new wildcard filter for an array of wildcards specifying case-sensitivity.

-     * <p>

-     * The array is not cloned, so could be changed after constructing the

-     * instance. This would be inadvisable however.

-     *

-     * @param wildcards  the array of wildcards to match, not null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the pattern array is null

-     */

-    public WildcardFileFilter(String[] wildcards, IOCase caseSensitivity) {

-        if (wildcards == null) {

-            throw new IllegalArgumentException("The wildcard array must not be null");

-        }

-        this.wildcards = wildcards;

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    /**

-     * Construct a new case-sensitive wildcard filter for a list of wildcards.

-     *

-     * @param wildcards  the list of wildcards to match, not null

-     * @throws IllegalArgumentException if the pattern list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public WildcardFileFilter(List<String> wildcards) {

-        this(wildcards, null);

-    }

-

-    /**

-     * Construct a new wildcard filter for a list of wildcards specifying case-sensitivity.

-     *

-     * @param wildcards  the list of wildcards to match, not null

-     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive

-     * @throws IllegalArgumentException if the pattern list is null

-     * @throws ClassCastException if the list does not contain Strings

-     */

-    public WildcardFileFilter(List<String> wildcards, IOCase caseSensitivity) {

-        if (wildcards == null) {

-            throw new IllegalArgumentException("The wildcard list must not be null");

-        }

-        this.wildcards = wildcards.toArray(new String[wildcards.size()]);

-        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Checks to see if the filename matches one of the wildcards.

-     *

-     * @param dir  the file directory

-     * @param name  the filename

-     * @return true if the filename matches one of the wildcards

-     */

-    @Override

-    public boolean accept(File dir, String name) {

-        for (int i = 0; i < wildcards.length; i++) {

-            if (FilenameUtils.wildcardMatch(name, wildcards[i], caseSensitivity)) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Checks to see if the filename matches one of the wildcards.

-     *

-     * @param file  the file to check

-     * @return true if the filename matches one of the wildcards

-     */

-    @Override

-    public boolean accept(File file) {

-        String name = file.getName();

-        for (int i = 0; i < wildcards.length; i++) {

-            if (FilenameUtils.wildcardMatch(name, wildcards[i], caseSensitivity)) {

-                return true;

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Provide a String representaion of this file filter.

-     *

-     * @return a String representaion

-     */

-    @Override

-    public String toString() {

-        StringBuffer buffer = new StringBuffer();

-        buffer.append(super.toString());

-        buffer.append("(");

-        if (wildcards != null) {

-            for (int i = 0; i < wildcards.length; i++) {

-                if (i > 0) {

-                    buffer.append(",");

-                }

-                buffer.append(wildcards[i]);

-            }

-        }

-        buffer.append(")");

-        return buffer.toString();

-    }

-

-}

+/*
+ * 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.commons.io.filefilter;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOCase;
+
+/**
+ * Filters files using the supplied wildcards.
+ * <p>
+ * This filter selects files and directories based on one or more wildcards.
+ * Testing is case-sensitive by default, but this can be configured.
+ * <p>
+ * The wildcard matcher uses the characters '?' and '*' to represent a
+ * single or multiple wildcard characters.
+ * This is the same as often found on Dos/Unix command lines.
+ * The extension check is case-sensitive by .
+ * See {@link FilenameUtils#wildcardMatchOnSystem} for more information.
+ * <p>
+ * For example:
+ * <pre>
+ * File dir = new File(".");
+ * FileFilter fileFilter = new WildcardFileFilter("*test*.java~*~");
+ * File[] files = dir.listFiles(fileFilter);
+ * for (int i = 0; i < files.length; i++) {
+ *   System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @author Jason Anderson
+ * @version $Revision: 155419 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $
+ * @since Commons IO 1.3
+ */
+public class WildcardFileFilter extends AbstractFileFilter implements Serializable {
+
+    /** The wildcards that will be used to match filenames. */
+    private final String[] wildcards;
+    /** Whether the comparison is case sensitive. */
+    private final IOCase caseSensitivity;
+
+    /**
+     * Construct a new case-sensitive wildcard filter for a single wildcard.
+     *
+     * @param wildcard  the wildcard to match
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public WildcardFileFilter(String wildcard) {
+        this(wildcard, null);
+    }
+
+    /**
+     * Construct a new wildcard filter for a single wildcard specifying case-sensitivity.
+     *
+     * @param wildcard  the wildcard to match, not null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the pattern is null
+     */
+    public WildcardFileFilter(String wildcard, IOCase caseSensitivity) {
+        if (wildcard == null) {
+            throw new IllegalArgumentException("The wildcard must not be null");
+        }
+        this.wildcards = new String[] { wildcard };
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Construct a new case-sensitive wildcard filter for an array of wildcards.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param wildcards  the array of wildcards to match
+     * @throws IllegalArgumentException if the pattern array is null
+     */
+    public WildcardFileFilter(String[] wildcards) {
+        this(wildcards, null);
+    }
+
+    /**
+     * Construct a new wildcard filter for an array of wildcards specifying case-sensitivity.
+     * <p>
+     * The array is not cloned, so could be changed after constructing the
+     * instance. This would be inadvisable however.
+     *
+     * @param wildcards  the array of wildcards to match, not null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the pattern array is null
+     */
+    public WildcardFileFilter(String[] wildcards, IOCase caseSensitivity) {
+        if (wildcards == null) {
+            throw new IllegalArgumentException("The wildcard array must not be null");
+        }
+        this.wildcards = wildcards;
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    /**
+     * Construct a new case-sensitive wildcard filter for a list of wildcards.
+     *
+     * @param wildcards  the list of wildcards to match, not null
+     * @throws IllegalArgumentException if the pattern list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public WildcardFileFilter(List<String> wildcards) {
+        this(wildcards, null);
+    }
+
+    /**
+     * Construct a new wildcard filter for a list of wildcards specifying case-sensitivity.
+     *
+     * @param wildcards  the list of wildcards to match, not null
+     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
+     * @throws IllegalArgumentException if the pattern list is null
+     * @throws ClassCastException if the list does not contain Strings
+     */
+    public WildcardFileFilter(List<String> wildcards, IOCase caseSensitivity) {
+        if (wildcards == null) {
+            throw new IllegalArgumentException("The wildcard list must not be null");
+        }
+        this.wildcards = wildcards.toArray(new String[wildcards.size()]);
+        this.caseSensitivity = (caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks to see if the filename matches one of the wildcards.
+     *
+     * @param dir  the file directory
+     * @param name  the filename
+     * @return true if the filename matches one of the wildcards
+     */
+    @Override
+    public boolean accept(File dir, String name) {
+        for (int i = 0; i < wildcards.length; i++) {
+            if (FilenameUtils.wildcardMatch(name, wildcards[i], caseSensitivity)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if the filename matches one of the wildcards.
+     *
+     * @param file  the file to check
+     * @return true if the filename matches one of the wildcards
+     */
+    @Override
+    public boolean accept(File file) {
+        String name = file.getName();
+        for (int i = 0; i < wildcards.length; i++) {
+            if (FilenameUtils.wildcardMatch(name, wildcards[i], caseSensitivity)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Provide a String representaion of this file filter.
+     *
+     * @return a String representaion
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(super.toString());
+        buffer.append("(");
+        if (wildcards != null) {
+            for (int i = 0; i < wildcards.length; i++) {
+                if (i > 0) {
+                    buffer.append(",");
+                }
+                buffer.append(wildcards[i]);
+            }
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/commons/io/filefilter/package.html b/src/org/apache/commons/io/filefilter/package.html
index 7a45f25..e495e95 100644
--- a/src/org/apache/commons/io/filefilter/package.html
+++ b/src/org/apache/commons/io/filefilter/package.html
@@ -1,143 +1,143 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body>

-<p>This package defines an interface (IOFileFilter) that combines both 

-{@link java.io.FileFilter} and {@link java.io.FilenameFilter}. Besides

-that the package offers a series of ready-to-use implementations of the

-IOFileFilter interface including implementation that allow you to combine

-other such filters.</p>

-<p>These filter can be used to list files or in {@link java.awt.FileDialog}, 

-for example.</p>

-     

-<p>There are a number of 'primitive' filters:</p>

-     

-<table>

-       <tbody>

-    <tr>

-      <td><a href="DirectoryFileFilter.html">DirectoryFilter</a></td>

-      <td>Only accept directories</td>

-    </tr>

-       <tr>

-      <td><a href="PrefixFileFilter.html">PrefixFileFilter</a></td>

-      <td>Filter based on a prefix</td>

-    </tr>

-       <tr>

-      <td><a href="SuffixFileFilter.html">SuffixFileFilter</a></td>

-      <td>Filter based on a suffix</td>

-    </tr>

-    <tr>

-      <td><a href="NameFileFilter.html">NameFileFilter</a></td>

-      <td>Filter based on a filename</td>

-    </tr>

-    <tr>

-      <td><a href="WildcardFileFilter.html">WildcardFileFilter</a></td>

-      <td>Filter based on wildcards</td>

-    </tr>

-    <tr>

-      <td><a href="AgeFileFilter.html">AgeFileFilter</a></td>

-      <td>Filter based on last modified time of file</td>

-    </tr>

-    <tr>

-      <td><a href="SizeFileFilter.html">SizeFileFilter</a></td>

-      <td>Filter based on file size</td>

-    </tr>

-  </tbody>

-</table>

-     

-<p>And there are five 'boolean' filters:</p>

-     

-<table>

-       <tbody>

-       <tr>

-      <td><a href="TrueFileFilter.html">TrueFileFilter</a></td>

-      <td>Accept all files</td>

-    </tr>

-       <tr>

-      <td><a href="FalseFileFilter.html">FalseFileFilter</a></td>

-      <td>Accept no files</td>

-    </tr>

-       <tr>

-      <td><a href="NotFileFilter.html">NotFileFilter</a></td>

-      <td>Applies a logical NOT to an existing filter</td>

-    </tr>

-    <tr>

-      <td><a href="AndFileFilter.html">AndFileFilter</a></td>

-      <td>Combines two filters using a logical AND</td>

-    </tr>

-       <tr>

-      <td><a href="OrFileFilter.html">OrFileFilter</a></td>

-      <td>Combines two filter using a logical OR</td>

-    </tr>

-     

-  </tbody>

-</table>

-      

-<p>These boolean FilenameFilters can be nested, to allow arbitrary expressions.

-For example, here is how one could print all non-directory files in the

-current directory, starting with "A", and ending in ".java" or ".class":</p>

-     

-<pre>

-  File dir = new File(".");

-  String[] files = dir.list( 

-    new AndFileFilter(

-      new AndFileFilter(

-        new PrefixFileFilter("A"),

-        new OrFileFilter(

-          new SuffixFileFilter(".class"),

-          new SuffixFileFilter(".java")

-        )

-      ),

-      new NotFileFilter(

-        new DirectoryFileFilter()

-      )

-    )

-  );

-  for ( int i=0; i&lt;files.length; i++ ) {

-    System.out.println(files[i]);

-  }

-</pre>

-

-<p>This package also contains a utility class: 

-<a href="FileFilterUtils.html">FileFilterUtils</a>. It allows you to use all 

-file filters without having to put them in the import section. Here's how the 

-above example will look using FileFilterUtils:</p>

-<pre>

-  File dir = new File(".");

-  String[] files = dir.list( 

-    FileFilterUtils.andFileFilter(

-      FileFilterUtils.andFileFilter(

-        FileFilterUtils.prefixFileFilter("A"),

-        FileFilterUtils.orFileFilter(

-          FileFilterUtils.suffixFileFilter(".class"),

-          FileFilterUtils.suffixFileFilter(".java")

-        )

-      ),

-      FileFilterUtils.notFileFilter(

-        FileFilterUtils.directoryFileFilter()

-      )

-    )

-  );

-  for ( int i=0; i&lt;files.length; i++ ) {

-    System.out.println(files[i]);

-  }

-</pre>

-<p>There are a few other goodies in that class so please have a look at the 

-documentation in detail.</p>

-    </body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body>
+<p>This package defines an interface (IOFileFilter) that combines both 
+{@link java.io.FileFilter} and {@link java.io.FilenameFilter}. Besides
+that the package offers a series of ready-to-use implementations of the
+IOFileFilter interface including implementation that allow you to combine
+other such filters.</p>
+<p>These filter can be used to list files or in {@link java.awt.FileDialog}, 
+for example.</p>
+     
+<p>There are a number of 'primitive' filters:</p>
+     
+<table>
+       <tbody>
+    <tr>
+      <td><a href="DirectoryFileFilter.html">DirectoryFilter</a></td>
+      <td>Only accept directories</td>
+    </tr>
+       <tr>
+      <td><a href="PrefixFileFilter.html">PrefixFileFilter</a></td>
+      <td>Filter based on a prefix</td>
+    </tr>
+       <tr>
+      <td><a href="SuffixFileFilter.html">SuffixFileFilter</a></td>
+      <td>Filter based on a suffix</td>
+    </tr>
+    <tr>
+      <td><a href="NameFileFilter.html">NameFileFilter</a></td>
+      <td>Filter based on a filename</td>
+    </tr>
+    <tr>
+      <td><a href="WildcardFileFilter.html">WildcardFileFilter</a></td>
+      <td>Filter based on wildcards</td>
+    </tr>
+    <tr>
+      <td><a href="AgeFileFilter.html">AgeFileFilter</a></td>
+      <td>Filter based on last modified time of file</td>
+    </tr>
+    <tr>
+      <td><a href="SizeFileFilter.html">SizeFileFilter</a></td>
+      <td>Filter based on file size</td>
+    </tr>
+  </tbody>
+</table>
+     
+<p>And there are five 'boolean' filters:</p>
+     
+<table>
+       <tbody>
+       <tr>
+      <td><a href="TrueFileFilter.html">TrueFileFilter</a></td>
+      <td>Accept all files</td>
+    </tr>
+       <tr>
+      <td><a href="FalseFileFilter.html">FalseFileFilter</a></td>
+      <td>Accept no files</td>
+    </tr>
+       <tr>
+      <td><a href="NotFileFilter.html">NotFileFilter</a></td>
+      <td>Applies a logical NOT to an existing filter</td>
+    </tr>
+    <tr>
+      <td><a href="AndFileFilter.html">AndFileFilter</a></td>
+      <td>Combines two filters using a logical AND</td>
+    </tr>
+       <tr>
+      <td><a href="OrFileFilter.html">OrFileFilter</a></td>
+      <td>Combines two filter using a logical OR</td>
+    </tr>
+     
+  </tbody>
+</table>
+      
+<p>These boolean FilenameFilters can be nested, to allow arbitrary expressions.
+For example, here is how one could print all non-directory files in the
+current directory, starting with "A", and ending in ".java" or ".class":</p>
+     
+<pre>
+  File dir = new File(".");
+  String[] files = dir.list( 
+    new AndFileFilter(
+      new AndFileFilter(
+        new PrefixFileFilter("A"),
+        new OrFileFilter(
+          new SuffixFileFilter(".class"),
+          new SuffixFileFilter(".java")
+        )
+      ),
+      new NotFileFilter(
+        new DirectoryFileFilter()
+      )
+    )
+  );
+  for ( int i=0; i&lt;files.length; i++ ) {
+    System.out.println(files[i]);
+  }
+</pre>
+
+<p>This package also contains a utility class: 
+<a href="FileFilterUtils.html">FileFilterUtils</a>. It allows you to use all 
+file filters without having to put them in the import section. Here's how the 
+above example will look using FileFilterUtils:</p>
+<pre>
+  File dir = new File(".");
+  String[] files = dir.list( 
+    FileFilterUtils.andFileFilter(
+      FileFilterUtils.andFileFilter(
+        FileFilterUtils.prefixFileFilter("A"),
+        FileFilterUtils.orFileFilter(
+          FileFilterUtils.suffixFileFilter(".class"),
+          FileFilterUtils.suffixFileFilter(".java")
+        )
+      ),
+      FileFilterUtils.notFileFilter(
+        FileFilterUtils.directoryFileFilter()
+      )
+    )
+  );
+  for ( int i=0; i&lt;files.length; i++ ) {
+    System.out.println(files[i]);
+  }
+</pre>
+<p>There are a few other goodies in that class so please have a look at the 
+documentation in detail.</p>
+    </body>
+</html>
diff --git a/src/org/apache/commons/io/input/AutoCloseInputStream.java b/src/org/apache/commons/io/input/AutoCloseInputStream.java
index bb62358..401dfd6 100644
--- a/src/org/apache/commons/io/input/AutoCloseInputStream.java
+++ b/src/org/apache/commons/io/input/AutoCloseInputStream.java
@@ -1,129 +1,129 @@
-/*

- * 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.commons.io.input;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * Proxy stream that closes and discards the underlying stream as soon as the

- * end of input has been reached or when the stream is explicitly closed.

- * Not even a reference to the underlying stream is kept after it has been

- * closed, so any allocated in-memory buffers can be freed even if the

- * client application still keeps a reference to the proxy stream.

- * <p>

- * This class is typically used to release any resources related to an open

- * stream as soon as possible even if the client application (by not explicitly

- * closing the stream when no longer needed) or the underlying stream (by not

- * releasing resources once the last byte has been read) do not do that.

- *

- * @version $Id: AutoCloseInputStream.java 610010 2008-01-08 14:50:59Z niallp $

- * @since Commons IO 1.4

- */

-public class AutoCloseInputStream extends ProxyInputStream {

-

-    /**

-     * Creates an automatically closing proxy for the given input stream.

-     *

-     * @param in underlying input stream

-     */

-    public AutoCloseInputStream(InputStream in) {

-        super(in);

-    }

-

-    /**

-     * Closes the underlying input stream and replaces the reference to it

-     * with a {@link ClosedInputStream} instance.

-     * <p>

-     * This method is automatically called by the read methods when the end

-     * of input has been reached.

-     * <p>

-     * Note that it is safe to call this method any number of times. The original

-     * underlying input stream is closed and discarded only once when this

-     * method is first called.

-     *

-     * @throws IOException if the underlying input stream can not be closed

-     */

-    public void close() throws IOException {

-        in.close();

-        in = new ClosedInputStream();

-    }

-

-    /**

-     * Reads and returns a single byte from the underlying input stream.

-     * If the underlying stream returns -1, the {@link #close()} method is

-     * called to automatically close and discard the stream.

-     *

-     * @return next byte in the stream, or -1 if no more bytes are available

-     * @throws IOException if the stream could not be read or closed

-     */

-    public int read() throws IOException {

-        int n = in.read();

-        if (n == -1) {

-            close();

-        }

-        return n;

-    }

-

-    /**

-     * Reads and returns bytes from the underlying input stream to the given

-     * buffer. If the underlying stream returns -1, the {@link #close()} method

-     * i called to automatically close and discard the stream.

-     *

-     * @param b buffer to which bytes from the stream are written

-     * @return number of bytes read, or -1 if no more bytes are available

-     * @throws IOException if the stream could not be read or closed

-     */

-    public int read(byte[] b) throws IOException {

-        int n = in.read(b);

-        if (n == -1) {

-            close();

-        }

-        return n;

-    }

-

-    /**

-     * Reads and returns bytes from the underlying input stream to the given

-     * buffer. If the underlying stream returns -1, the {@link #close()} method

-     * i called to automatically close and discard the stream.

-     *

-     * @param b buffer to which bytes from the stream are written

-     * @param off start offset within the buffer

-     * @param len maximum number of bytes to read

-     * @return number of bytes read, or -1 if no more bytes are available

-     * @throws IOException if the stream could not be read or closed

-     */

-    public int read(byte[] b, int off, int len) throws IOException {

-        int n = in.read(b, off, len);

-        if (n == -1) {

-            close();

-        }

-        return n;

-    }

-

-    /**

-     * Ensures that the stream is closed before it gets garbage-collected.

-     * As mentioned in {@link #close()}, this is a no-op if the stream has

-     * already been closed.

-     * @throws Throwable if an error occurs

-     */

-    protected void finalize() throws Throwable {

-        close();

-        super.finalize();

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Proxy stream that closes and discards the underlying stream as soon as the
+ * end of input has been reached or when the stream is explicitly closed.
+ * Not even a reference to the underlying stream is kept after it has been
+ * closed, so any allocated in-memory buffers can be freed even if the
+ * client application still keeps a reference to the proxy stream.
+ * <p>
+ * This class is typically used to release any resources related to an open
+ * stream as soon as possible even if the client application (by not explicitly
+ * closing the stream when no longer needed) or the underlying stream (by not
+ * releasing resources once the last byte has been read) do not do that.
+ *
+ * @version $Id: AutoCloseInputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ * @since Commons IO 1.4
+ */
+public class AutoCloseInputStream extends ProxyInputStream {
+
+    /**
+     * Creates an automatically closing proxy for the given input stream.
+     *
+     * @param in underlying input stream
+     */
+    public AutoCloseInputStream(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * Closes the underlying input stream and replaces the reference to it
+     * with a {@link ClosedInputStream} instance.
+     * <p>
+     * This method is automatically called by the read methods when the end
+     * of input has been reached.
+     * <p>
+     * Note that it is safe to call this method any number of times. The original
+     * underlying input stream is closed and discarded only once when this
+     * method is first called.
+     *
+     * @throws IOException if the underlying input stream can not be closed
+     */
+    public void close() throws IOException {
+        in.close();
+        in = new ClosedInputStream();
+    }
+
+    /**
+     * Reads and returns a single byte from the underlying input stream.
+     * If the underlying stream returns -1, the {@link #close()} method is
+     * called to automatically close and discard the stream.
+     *
+     * @return next byte in the stream, or -1 if no more bytes are available
+     * @throws IOException if the stream could not be read or closed
+     */
+    public int read() throws IOException {
+        int n = in.read();
+        if (n == -1) {
+            close();
+        }
+        return n;
+    }
+
+    /**
+     * Reads and returns bytes from the underlying input stream to the given
+     * buffer. If the underlying stream returns -1, the {@link #close()} method
+     * i called to automatically close and discard the stream.
+     *
+     * @param b buffer to which bytes from the stream are written
+     * @return number of bytes read, or -1 if no more bytes are available
+     * @throws IOException if the stream could not be read or closed
+     */
+    public int read(byte[] b) throws IOException {
+        int n = in.read(b);
+        if (n == -1) {
+            close();
+        }
+        return n;
+    }
+
+    /**
+     * Reads and returns bytes from the underlying input stream to the given
+     * buffer. If the underlying stream returns -1, the {@link #close()} method
+     * i called to automatically close and discard the stream.
+     *
+     * @param b buffer to which bytes from the stream are written
+     * @param off start offset within the buffer
+     * @param len maximum number of bytes to read
+     * @return number of bytes read, or -1 if no more bytes are available
+     * @throws IOException if the stream could not be read or closed
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        int n = in.read(b, off, len);
+        if (n == -1) {
+            close();
+        }
+        return n;
+    }
+
+    /**
+     * Ensures that the stream is closed before it gets garbage-collected.
+     * As mentioned in {@link #close()}, this is a no-op if the stream has
+     * already been closed.
+     * @throws Throwable if an error occurs
+     */
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/CharSequenceReader.java b/src/org/apache/commons/io/input/CharSequenceReader.java
index 6ee11d8..1be5282 100644
--- a/src/org/apache/commons/io/input/CharSequenceReader.java
+++ b/src/org/apache/commons/io/input/CharSequenceReader.java
@@ -1,155 +1,155 @@
-/*

- * 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.commons.io.input;

-

-import java.io.Reader;

-import java.io.Serializable;

-

-/**

- * {@link Reader} implementation that can read from String, StringBuffer,

- * StringBuilder or CharBuffer.

- * <p>

- * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.

- *

- * @version $Revision: 610516 $ $Date: 2008-01-09 19:05:05 +0000 (Wed, 09 Jan 2008) $

- * @since Commons IO 1.4

- */

-public class CharSequenceReader extends Reader implements Serializable {

-

-    private final CharSequence charSequence;

-    private int idx;

-    private int mark;

-

-    /**

-     * Construct a new instance with the specified character sequence.

-     *

-     * @param charSequence The character sequence, may be <code>null</code>

-     */

-    public CharSequenceReader(CharSequence charSequence) {

-        this.charSequence = (charSequence != null ? charSequence : "");

-    }

-

-    /**

-     * Close resets the file back to the start and removes any marked position.

-     */

-    public void close() {

-        idx = 0;

-        mark = 0;

-    }

-

-    /**

-     * Mark the current position.

-     *

-     * @param readAheadLimit ignored

-     */

-    public void mark(int readAheadLimit) {

-        mark = idx;

-    }

-

-    /**

-     * Mark is supported (returns true).

-     *

-     * @return <code>true</code>

-     */

-    public boolean markSupported() {

-        return true;

-    }

-

-    /**

-     * Read a single character.

-     *

-     * @return the next character from the character sequence

-     * or -1 if the end has been reached.

-     */

-    public int read() {

-        if (idx >= charSequence.length()) {

-            return -1;

-        } else {

-            return charSequence.charAt(idx++);

-        }

-    }

-

-    /**

-     * Read the sepcified number of characters into the array.

-     *

-     * @param array The array to store the characters in

-     * @param offset The starting position in the array to store

-     * @param length The maximum number of characters to read

-     * @return The number of characters read or -1 if there are

-     * no more

-     */

-    public int read(char[] array, int offset, int length) {

-        if (idx >= charSequence.length()) {

-            return -1;

-        }

-        if (array == null) {

-            throw new NullPointerException("Character array is missing");

-        }

-        if (length < 0 || (offset + length) > array.length) {

-            throw new IndexOutOfBoundsException("Array Size=" + array.length +

-                    ", offset=" + offset + ", length=" + length);

-        }

-        int count = 0;

-        for (int i = 0; i < length; i++) {

-            int c = read();

-            if (c == -1) {

-                return count;

-            }

-            array[offset + i] = (char)c;

-            count++;

-        }

-        return count;

-    }

-

-    /**

-     * Reset the reader to the last marked position (or the beginning if

-     * mark has not been called).

-     */

-    public void reset() {

-        idx = mark;

-    }

-

-    /**

-     * Skip the specified number of characters.

-     *

-     * @param n The number of characters to skip

-     * @return The actual number of characters skipped

-     */

-    public long skip(long n) {

-        if (n < 0) {

-            throw new IllegalArgumentException(

-                    "Number of characters to skip is less than zero: " + n);

-        }

-        if (idx >= charSequence.length()) {

-            return -1;

-        }

-        int dest = (int)Math.min(charSequence.length(), (idx + n));

-        int count = dest - idx;

-        idx = dest;

-        return count;

-    }

-

-    /**

-     * Return a String representation of the underlying

-     * character sequence.

-     *

-     * @return The contents of the character sequence

-     */

-    public String toString() {

-        return charSequence.toString();

-    }

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.Reader;
+import java.io.Serializable;
+
+/**
+ * {@link Reader} implementation that can read from String, StringBuffer,
+ * StringBuilder or CharBuffer.
+ * <p>
+ * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.
+ *
+ * @version $Revision: 610516 $ $Date: 2008-01-09 19:05:05 +0000 (Wed, 09 Jan 2008) $
+ * @since Commons IO 1.4
+ */
+public class CharSequenceReader extends Reader implements Serializable {
+
+    private final CharSequence charSequence;
+    private int idx;
+    private int mark;
+
+    /**
+     * Construct a new instance with the specified character sequence.
+     *
+     * @param charSequence The character sequence, may be <code>null</code>
+     */
+    public CharSequenceReader(CharSequence charSequence) {
+        this.charSequence = (charSequence != null ? charSequence : "");
+    }
+
+    /**
+     * Close resets the file back to the start and removes any marked position.
+     */
+    public void close() {
+        idx = 0;
+        mark = 0;
+    }
+
+    /**
+     * Mark the current position.
+     *
+     * @param readAheadLimit ignored
+     */
+    public void mark(int readAheadLimit) {
+        mark = idx;
+    }
+
+    /**
+     * Mark is supported (returns true).
+     *
+     * @return <code>true</code>
+     */
+    public boolean markSupported() {
+        return true;
+    }
+
+    /**
+     * Read a single character.
+     *
+     * @return the next character from the character sequence
+     * or -1 if the end has been reached.
+     */
+    public int read() {
+        if (idx >= charSequence.length()) {
+            return -1;
+        } else {
+            return charSequence.charAt(idx++);
+        }
+    }
+
+    /**
+     * Read the sepcified number of characters into the array.
+     *
+     * @param array The array to store the characters in
+     * @param offset The starting position in the array to store
+     * @param length The maximum number of characters to read
+     * @return The number of characters read or -1 if there are
+     * no more
+     */
+    public int read(char[] array, int offset, int length) {
+        if (idx >= charSequence.length()) {
+            return -1;
+        }
+        if (array == null) {
+            throw new NullPointerException("Character array is missing");
+        }
+        if (length < 0 || (offset + length) > array.length) {
+            throw new IndexOutOfBoundsException("Array Size=" + array.length +
+                    ", offset=" + offset + ", length=" + length);
+        }
+        int count = 0;
+        for (int i = 0; i < length; i++) {
+            int c = read();
+            if (c == -1) {
+                return count;
+            }
+            array[offset + i] = (char)c;
+            count++;
+        }
+        return count;
+    }
+
+    /**
+     * Reset the reader to the last marked position (or the beginning if
+     * mark has not been called).
+     */
+    public void reset() {
+        idx = mark;
+    }
+
+    /**
+     * Skip the specified number of characters.
+     *
+     * @param n The number of characters to skip
+     * @return The actual number of characters skipped
+     */
+    public long skip(long n) {
+        if (n < 0) {
+            throw new IllegalArgumentException(
+                    "Number of characters to skip is less than zero: " + n);
+        }
+        if (idx >= charSequence.length()) {
+            return -1;
+        }
+        int dest = (int)Math.min(charSequence.length(), (idx + n));
+        int count = dest - idx;
+        idx = dest;
+        return count;
+    }
+
+    /**
+     * Return a String representation of the underlying
+     * character sequence.
+     *
+     * @return The contents of the character sequence
+     */
+    public String toString() {
+        return charSequence.toString();
+    }
+}
diff --git a/src/org/apache/commons/io/input/ClassLoaderObjectInputStream.java b/src/org/apache/commons/io/input/ClassLoaderObjectInputStream.java
index 13d0489..334ff89 100644
--- a/src/org/apache/commons/io/input/ClassLoaderObjectInputStream.java
+++ b/src/org/apache/commons/io/input/ClassLoaderObjectInputStream.java
@@ -1,77 +1,77 @@
-/*

- * 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.commons.io.input;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.ObjectInputStream;

-import java.io.ObjectStreamClass;

-import java.io.StreamCorruptedException;

-

-/**

- * A special ObjectInputStream that loads a class based on a specified

- * <code>ClassLoader</code> rather than the system default.

- * <p>

- * This is useful in dynamic container environments.

- *

- * @author Paul Hammant

- * @version $Id: ClassLoaderObjectInputStream.java 437567 2006-08-28 06:39:07Z bayard $

- * @since Commons IO 1.1

- */

-public class ClassLoaderObjectInputStream extends ObjectInputStream {

-

-    /** The class loader to use. */

-    private ClassLoader classLoader;

-

-    /**

-     * Constructs a new ClassLoaderObjectInputStream.

-     *

-     * @param classLoader  the ClassLoader from which classes should be loaded

-     * @param inputStream  the InputStream to work on

-     * @throws IOException in case of an I/O error

-     * @throws StreamCorruptedException if the stream is corrupted

-     */

-    public ClassLoaderObjectInputStream(

-            ClassLoader classLoader, InputStream inputStream)

-            throws IOException, StreamCorruptedException {

-        super(inputStream);

-        this.classLoader = classLoader;

-    }

-

-    /**

-     * Resolve a class specified by the descriptor using the

-     * specified ClassLoader or the super ClassLoader.

-     *

-     * @param objectStreamClass  descriptor of the class

-     * @return the Class object described by the ObjectStreamClass

-     * @throws IOException in case of an I/O error

-     * @throws ClassNotFoundException if the Class cannot be found

-     */

-    protected Class resolveClass(ObjectStreamClass objectStreamClass)

-            throws IOException, ClassNotFoundException {

-        

-        Class clazz = Class.forName(objectStreamClass.getName(), false, classLoader);

-

-        if (clazz != null) {

-            // the classloader knows of the class

-            return clazz;

-        } else {

-            // classloader knows not of class, let the super classloader do it

-            return super.resolveClass(objectStreamClass);

-        }

-    }

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
+/**
+ * A special ObjectInputStream that loads a class based on a specified
+ * <code>ClassLoader</code> rather than the system default.
+ * <p>
+ * This is useful in dynamic container environments.
+ *
+ * @author Paul Hammant
+ * @version $Id: ClassLoaderObjectInputStream.java 437567 2006-08-28 06:39:07Z bayard $
+ * @since Commons IO 1.1
+ */
+public class ClassLoaderObjectInputStream extends ObjectInputStream {
+
+    /** The class loader to use. */
+    private ClassLoader classLoader;
+
+    /**
+     * Constructs a new ClassLoaderObjectInputStream.
+     *
+     * @param classLoader  the ClassLoader from which classes should be loaded
+     * @param inputStream  the InputStream to work on
+     * @throws IOException in case of an I/O error
+     * @throws StreamCorruptedException if the stream is corrupted
+     */
+    public ClassLoaderObjectInputStream(
+            ClassLoader classLoader, InputStream inputStream)
+            throws IOException, StreamCorruptedException {
+        super(inputStream);
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Resolve a class specified by the descriptor using the
+     * specified ClassLoader or the super ClassLoader.
+     *
+     * @param objectStreamClass  descriptor of the class
+     * @return the Class object described by the ObjectStreamClass
+     * @throws IOException in case of an I/O error
+     * @throws ClassNotFoundException if the Class cannot be found
+     */
+    protected Class resolveClass(ObjectStreamClass objectStreamClass)
+            throws IOException, ClassNotFoundException {
+        
+        Class clazz = Class.forName(objectStreamClass.getName(), false, classLoader);
+
+        if (clazz != null) {
+            // the classloader knows of the class
+            return clazz;
+        } else {
+            // classloader knows not of class, let the super classloader do it
+            return super.resolveClass(objectStreamClass);
+        }
+    }
+}
diff --git a/src/org/apache/commons/io/input/CloseShieldInputStream.java b/src/org/apache/commons/io/input/CloseShieldInputStream.java
index 2058bee..5964104 100644
--- a/src/org/apache/commons/io/input/CloseShieldInputStream.java
+++ b/src/org/apache/commons/io/input/CloseShieldInputStream.java
@@ -1,52 +1,52 @@
-/*

- * 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.commons.io.input;

-

-import java.io.InputStream;

-

-/**

- * Proxy stream that prevents the underlying input stream from being closed.

- * <p>

- * This class is typically used in cases where an input stream needs to be

- * passed to a component that wants to explicitly close the stream even if

- * more input would still be available to other components.

- *

- * @version $Id: CloseShieldInputStream.java 587913 2007-10-24 15:47:30Z niallp $

- * @since Commons IO 1.4

- */

-public class CloseShieldInputStream extends ProxyInputStream {

-

-    /**

-     * Creates a proxy that shields the given input stream from being

-     * closed.

-     *

-     * @param in underlying input stream

-     */

-    public CloseShieldInputStream(InputStream in) {

-        super(in);

-    }

-

-    /**

-     * Replaces the underlying input stream with a {@link ClosedInputStream}

-     * sentinel. The original input stream will remain open, but this proxy

-     * will appear closed.

-     */

-    public void close() {

-        in = new ClosedInputStream();

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.InputStream;
+
+/**
+ * Proxy stream that prevents the underlying input stream from being closed.
+ * <p>
+ * This class is typically used in cases where an input stream needs to be
+ * passed to a component that wants to explicitly close the stream even if
+ * more input would still be available to other components.
+ *
+ * @version $Id: CloseShieldInputStream.java 587913 2007-10-24 15:47:30Z niallp $
+ * @since Commons IO 1.4
+ */
+public class CloseShieldInputStream extends ProxyInputStream {
+
+    /**
+     * Creates a proxy that shields the given input stream from being
+     * closed.
+     *
+     * @param in underlying input stream
+     */
+    public CloseShieldInputStream(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * Replaces the underlying input stream with a {@link ClosedInputStream}
+     * sentinel. The original input stream will remain open, but this proxy
+     * will appear closed.
+     */
+    public void close() {
+        in = new ClosedInputStream();
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/ClosedInputStream.java b/src/org/apache/commons/io/input/ClosedInputStream.java
index 86c83c9..b0a7ccd 100644
--- a/src/org/apache/commons/io/input/ClosedInputStream.java
+++ b/src/org/apache/commons/io/input/ClosedInputStream.java
@@ -1,48 +1,48 @@
-/*

- * 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.commons.io.input;

-

-import java.io.InputStream;

-

-/**

- * Closed input stream. This stream returns -1 to all attempts to read

- * something from the stream.

- * <p>

- * Typically uses of this class include testing for corner cases in methods

- * that accept input streams and acting as a sentinel value instead of a

- * <code>null</code> input stream.

- *

- * @version $Id: ClosedInputStream.java 601751 2007-12-06 14:55:45Z niallp $

- * @since Commons IO 1.4

- */

-public class ClosedInputStream extends InputStream {

-    

-    /**

-     * A singleton.

-     */

-    public static final ClosedInputStream CLOSED_INPUT_STREAM = new ClosedInputStream();

-

-    /**

-     * Returns -1 to indicate that the stream is closed.

-     *

-     * @return always -1

-     */

-    public int read() {

-        return -1;

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.InputStream;
+
+/**
+ * Closed input stream. This stream returns -1 to all attempts to read
+ * something from the stream.
+ * <p>
+ * Typically uses of this class include testing for corner cases in methods
+ * that accept input streams and acting as a sentinel value instead of a
+ * <code>null</code> input stream.
+ *
+ * @version $Id: ClosedInputStream.java 601751 2007-12-06 14:55:45Z niallp $
+ * @since Commons IO 1.4
+ */
+public class ClosedInputStream extends InputStream {
+    
+    /**
+     * A singleton.
+     */
+    public static final ClosedInputStream CLOSED_INPUT_STREAM = new ClosedInputStream();
+
+    /**
+     * Returns -1 to indicate that the stream is closed.
+     *
+     * @return always -1
+     */
+    public int read() {
+        return -1;
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/CountingInputStream.java b/src/org/apache/commons/io/input/CountingInputStream.java
index 2782276..2e5da27 100644
--- a/src/org/apache/commons/io/input/CountingInputStream.java
+++ b/src/org/apache/commons/io/input/CountingInputStream.java
@@ -1,175 +1,175 @@
-/*

- * 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.commons.io.input;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * A decorating input stream that counts the number of bytes that have passed

- * through the stream so far.

- * <p>

- * A typical use case would be during debugging, to ensure that data is being

- * read as expected.

- *

- * @author Marcelo Liberato

- * @version $Id: CountingInputStream.java 471628 2006-11-06 04:06:45Z bayard $

- */

-public class CountingInputStream extends ProxyInputStream {

-

-    /** The count of bytes that have passed. */

-    private long count;

-

-    /**

-     * Constructs a new CountingInputStream.

-     *

-     * @param in  the InputStream to delegate to

-     */

-    public CountingInputStream(InputStream in) {

-        super(in);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Reads a number of bytes into the byte array, keeping count of the

-     * number read.

-     *

-     * @param b  the buffer into which the data is read, not null

-     * @return the total number of bytes read into the buffer, -1 if end of stream

-     * @throws IOException if an I/O error occurs

-     * @see java.io.InputStream#read(byte[]) 

-     */

-    public int read(byte[] b) throws IOException {

-        int found = super.read(b);

-        this.count += (found >= 0) ? found : 0;

-        return found;

-    }

-

-    /**

-     * Reads a number of bytes into the byte array at a specific offset,

-     * keeping count of the number read.

-     *

-     * @param b  the buffer into which the data is read, not null

-     * @param off  the start offset in the buffer

-     * @param len  the maximum number of bytes to read

-     * @return the total number of bytes read into the buffer, -1 if end of stream

-     * @throws IOException if an I/O error occurs

-     * @see java.io.InputStream#read(byte[], int, int)

-     */

-    public int read(byte[] b, int off, int len) throws IOException {

-        int found = super.read(b, off, len);

-        this.count += (found >= 0) ? found : 0;

-        return found;

-    }

-

-    /**

-     * Reads the next byte of data adding to the count of bytes received

-     * if a byte is successfully read. 

-     *

-     * @return the byte read, -1 if end of stream

-     * @throws IOException if an I/O error occurs

-     * @see java.io.InputStream#read()

-     */

-    public int read() throws IOException {

-        int found = super.read();

-        this.count += (found >= 0) ? 1 : 0;

-        return found;

-    }

-

-    /**

-     * Skips the stream over the specified number of bytes, adding the skipped

-     * amount to the count.

-     *

-     * @param length  the number of bytes to skip

-     * @return the actual number of bytes skipped

-     * @throws IOException if an I/O error occurs

-     * @see java.io.InputStream#skip(long)

-     */

-    public long skip(final long length) throws IOException {

-        final long skip = super.skip(length);

-        this.count += skip;

-        return skip;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * The number of bytes that have passed through this stream.

-     * <p>

-     * NOTE: From v1.3 this method throws an ArithmeticException if the

-     * count is greater than can be expressed by an <code>int</code>.

-     * See {@link #getByteCount()} for a method using a <code>long</code>.

-     *

-     * @return the number of bytes accumulated

-     * @throws ArithmeticException if the byte count is too large

-     */

-    public synchronized int getCount() {

-        long result = getByteCount();

-        if (result > Integer.MAX_VALUE) {

-            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");

-        }

-        return (int) result;

-    }

-

-    /** 

-     * Set the byte count back to 0. 

-     * <p>

-     * NOTE: From v1.3 this method throws an ArithmeticException if the

-     * count is greater than can be expressed by an <code>int</code>.

-     * See {@link #resetByteCount()} for a method using a <code>long</code>.

-     *

-     * @return the count previous to resetting

-     * @throws ArithmeticException if the byte count is too large

-     */

-    public synchronized int resetCount() {

-        long result = resetByteCount();

-        if (result > Integer.MAX_VALUE) {

-            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");

-        }

-        return (int) result;

-    }

-

-    /**

-     * The number of bytes that have passed through this stream.

-     * <p>

-     * NOTE: This method is an alternative for <code>getCount()</code>

-     * and was added because that method returns an integer which will

-     * result in incorrect count for files over 2GB.

-     *

-     * @return the number of bytes accumulated

-     * @since Commons IO 1.3

-     */

-    public synchronized long getByteCount() {

-        return this.count;

-    }

-

-    /** 

-     * Set the byte count back to 0. 

-     * <p>

-     * NOTE: This method is an alternative for <code>resetCount()</code>

-     * and was added because that method returns an integer which will

-     * result in incorrect count for files over 2GB.

-     *

-     * @return the count previous to resetting

-     * @since Commons IO 1.3

-     */

-    public synchronized long resetByteCount() {

-        long tmp = this.count;

-        this.count = 0;

-        return tmp;

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A decorating input stream that counts the number of bytes that have passed
+ * through the stream so far.
+ * <p>
+ * A typical use case would be during debugging, to ensure that data is being
+ * read as expected.
+ *
+ * @author Marcelo Liberato
+ * @version $Id: CountingInputStream.java 471628 2006-11-06 04:06:45Z bayard $
+ */
+public class CountingInputStream extends ProxyInputStream {
+
+    /** The count of bytes that have passed. */
+    private long count;
+
+    /**
+     * Constructs a new CountingInputStream.
+     *
+     * @param in  the InputStream to delegate to
+     */
+    public CountingInputStream(InputStream in) {
+        super(in);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Reads a number of bytes into the byte array, keeping count of the
+     * number read.
+     *
+     * @param b  the buffer into which the data is read, not null
+     * @return the total number of bytes read into the buffer, -1 if end of stream
+     * @throws IOException if an I/O error occurs
+     * @see java.io.InputStream#read(byte[]) 
+     */
+    public int read(byte[] b) throws IOException {
+        int found = super.read(b);
+        this.count += (found >= 0) ? found : 0;
+        return found;
+    }
+
+    /**
+     * Reads a number of bytes into the byte array at a specific offset,
+     * keeping count of the number read.
+     *
+     * @param b  the buffer into which the data is read, not null
+     * @param off  the start offset in the buffer
+     * @param len  the maximum number of bytes to read
+     * @return the total number of bytes read into the buffer, -1 if end of stream
+     * @throws IOException if an I/O error occurs
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        int found = super.read(b, off, len);
+        this.count += (found >= 0) ? found : 0;
+        return found;
+    }
+
+    /**
+     * Reads the next byte of data adding to the count of bytes received
+     * if a byte is successfully read. 
+     *
+     * @return the byte read, -1 if end of stream
+     * @throws IOException if an I/O error occurs
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        int found = super.read();
+        this.count += (found >= 0) ? 1 : 0;
+        return found;
+    }
+
+    /**
+     * Skips the stream over the specified number of bytes, adding the skipped
+     * amount to the count.
+     *
+     * @param length  the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws IOException if an I/O error occurs
+     * @see java.io.InputStream#skip(long)
+     */
+    public long skip(final long length) throws IOException {
+        final long skip = super.skip(length);
+        this.count += skip;
+        return skip;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
+     * NOTE: From v1.3 this method throws an ArithmeticException if the
+     * count is greater than can be expressed by an <code>int</code>.
+     * See {@link #getByteCount()} for a method using a <code>long</code>.
+     *
+     * @return the number of bytes accumulated
+     * @throws ArithmeticException if the byte count is too large
+     */
+    public synchronized int getCount() {
+        long result = getByteCount();
+        if (result > Integer.MAX_VALUE) {
+            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
+        }
+        return (int) result;
+    }
+
+    /** 
+     * Set the byte count back to 0. 
+     * <p>
+     * NOTE: From v1.3 this method throws an ArithmeticException if the
+     * count is greater than can be expressed by an <code>int</code>.
+     * See {@link #resetByteCount()} for a method using a <code>long</code>.
+     *
+     * @return the count previous to resetting
+     * @throws ArithmeticException if the byte count is too large
+     */
+    public synchronized int resetCount() {
+        long result = resetByteCount();
+        if (result > Integer.MAX_VALUE) {
+            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
+        }
+        return (int) result;
+    }
+
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
+     * NOTE: This method is an alternative for <code>getCount()</code>
+     * and was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the number of bytes accumulated
+     * @since Commons IO 1.3
+     */
+    public synchronized long getByteCount() {
+        return this.count;
+    }
+
+    /** 
+     * Set the byte count back to 0. 
+     * <p>
+     * NOTE: This method is an alternative for <code>resetCount()</code>
+     * and was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the count previous to resetting
+     * @since Commons IO 1.3
+     */
+    public synchronized long resetByteCount() {
+        long tmp = this.count;
+        this.count = 0;
+        return tmp;
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/DemuxInputStream.java b/src/org/apache/commons/io/input/DemuxInputStream.java
index 64b41c4..69cfcf9 100644
--- a/src/org/apache/commons/io/input/DemuxInputStream.java
+++ b/src/org/apache/commons/io/input/DemuxInputStream.java
@@ -1,93 +1,93 @@
-/*

- * 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.commons.io.input;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * Data written to this stream is forwarded to a stream that has been associated

- * with this thread.

- *

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $

- */

-public class DemuxInputStream

-    extends InputStream

-{

-    private InheritableThreadLocal<InputStream> m_streams = new InheritableThreadLocal<InputStream>();

-

-    /**

-     * Bind the specified stream to the current thread.

-     *

-     * @param input the stream to bind

-     * @return the InputStream that was previously active

-     */

-    public InputStream bindStream( InputStream input )

-    {

-        InputStream oldValue = getStream();

-        m_streams.set( input );

-        return oldValue;

-    }

-

-    /**

-     * Closes stream associated with current thread.

-     *

-     * @throws IOException if an error occurs

-     */

-    @Override

-    public void close()

-        throws IOException

-    {

-        InputStream input = getStream();

-        if( null != input )

-        {

-            input.close();

-        }

-    }

-

-    /**

-     * Read byte from stream associated with current thread.

-     *

-     * @return the byte read from stream

-     * @throws IOException if an error occurs

-     */

-    @Override

-    public int read()

-        throws IOException

-    {

-        InputStream input = getStream();

-        if( null != input )

-        {

-            return input.read();

-        }

-        else

-        {

-            return -1;

-        }

-    }

-

-    /**

-     * Utility method to retrieve stream bound to current thread (if any).

-     *

-     * @return the input stream

-     */

-    private InputStream getStream()

-    {

-        return m_streams.get();

-    }

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Data written to this stream is forwarded to a stream that has been associated
+ * with this thread.
+ *
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $
+ */
+public class DemuxInputStream
+    extends InputStream
+{
+    private InheritableThreadLocal<InputStream> m_streams = new InheritableThreadLocal<InputStream>();
+
+    /**
+     * Bind the specified stream to the current thread.
+     *
+     * @param input the stream to bind
+     * @return the InputStream that was previously active
+     */
+    public InputStream bindStream( InputStream input )
+    {
+        InputStream oldValue = getStream();
+        m_streams.set( input );
+        return oldValue;
+    }
+
+    /**
+     * Closes stream associated with current thread.
+     *
+     * @throws IOException if an error occurs
+     */
+    @Override
+    public void close()
+        throws IOException
+    {
+        InputStream input = getStream();
+        if( null != input )
+        {
+            input.close();
+        }
+    }
+
+    /**
+     * Read byte from stream associated with current thread.
+     *
+     * @return the byte read from stream
+     * @throws IOException if an error occurs
+     */
+    @Override
+    public int read()
+        throws IOException
+    {
+        InputStream input = getStream();
+        if( null != input )
+        {
+            return input.read();
+        }
+        else
+        {
+            return -1;
+        }
+    }
+
+    /**
+     * Utility method to retrieve stream bound to current thread (if any).
+     *
+     * @return the input stream
+     */
+    private InputStream getStream()
+    {
+        return m_streams.get();
+    }
+}
diff --git a/src/org/apache/commons/io/input/NullInputStream.java b/src/org/apache/commons/io/input/NullInputStream.java
index 7cee2c6..96cc6c4 100644
--- a/src/org/apache/commons/io/input/NullInputStream.java
+++ b/src/org/apache/commons/io/input/NullInputStream.java
@@ -1,329 +1,329 @@
-/*

- * 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.commons.io.input;

-

-import java.io.EOFException;

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * A functional, light weight {@link InputStream} that emulates

- * a stream of a specified size.

- * <p>

- * This implementation provides a light weight

- * object for testing with an {@link InputStream}

- * where the contents don't matter.

- * <p>

- * One use case would be for testing the handling of

- * large {@link InputStream} as it can emulate that

- * scenario without the overhead of actually processing

- * large numbers of bytes - significantly speeding up

- * test execution times.

- * <p>

- * This implementation returns zero from the method that

- * reads a byte and leaves the array unchanged in the read

- * methods that are passed a byte array.

- * If alternative data is required the <code>processByte()</code> and

- * <code>processBytes()</code> methods can be implemented to generate

- * data, for example:

- *

- * <pre>

- *  public class TestInputStream extends NullInputStream {

- *      public TestInputStream(int size) {

- *          super(size);

- *      }

- *      protected int processByte() {

- *          return ... // return required value here

- *      }

- *      protected void processBytes(byte[] bytes, int offset, int length) {

- *          for (int i = offset; i < length; i++) {

- *              bytes[i] = ... // set array value here

- *          }

- *      }

- *  }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 463529 $

- */

-public class NullInputStream extends InputStream {

-

-    private long size;

-    private long position;

-    private long mark = -1;

-    private long readlimit;

-    private boolean eof;

-    private boolean throwEofException;

-    private boolean markSupported;

-

-    /**

-     * Create an {@link InputStream} that emulates a specified size

-     * which supports marking and does not throw EOFException.

-     *

-     * @param size The size of the input stream to emulate.

-     */

-    public NullInputStream(long size) {

-       this(size, true, false);

-    }

-

-    /**

-     * Create an {@link InputStream} that emulates a specified

-     * size with option settings.

-     *

-     * @param size The size of the input stream to emulate.

-     * @param markSupported Whether this instance will support

-     * the <code>mark()</code> functionality.

-     * @param throwEofException Whether this implementation

-     * will throw an {@link EOFException} or return -1 when the

-     * end of file is reached.

-     */

-    public NullInputStream(long size, boolean markSupported, boolean throwEofException) {

-       this.size = size;

-       this.markSupported = markSupported;

-       this.throwEofException = throwEofException;

-    }

-

-    /**

-     * Return the current position.

-     *

-     * @return the current position.

-     */

-    public long getPosition() {

-        return position;

-    }

-

-    /**

-     * Return the size this {@link InputStream} emulates.

-     *

-     * @return The size of the input stream to emulate.

-     */

-    public long getSize() {

-        return size;

-    }

-

-    /**

-     * Return the number of bytes that can be read.

-     *

-     * @return The number of bytes that can be read.

-     */

-    public int available() {

-        long avail = size - position;

-        if (avail <= 0) {

-            return 0;

-        } else if (avail > Integer.MAX_VALUE) {

-            return Integer.MAX_VALUE;

-        } else {

-            return (int)avail;

-        }

-    }

-

-    /**

-     * Close this input stream - resets the internal state to

-     * the initial values.

-     *

-     * @throws IOException If an error occurs.

-     */

-    public void close() throws IOException {

-        eof = false;

-        position = 0;

-        mark = -1;

-    }

-

-    /**

-     * Mark the current position.

-     *

-     * @param readlimit The number of bytes before this marked position

-     * is invalid.

-     * @throws UnsupportedOperationException if mark is not supported.

-     */

-    public synchronized void mark(int readlimit) {

-        if (!markSupported) {

-            throw new UnsupportedOperationException("Mark not supported");

-        }

-        mark = position;

-        this.readlimit = readlimit;

-    }

-

-    /**

-     * Indicates whether <i>mark</i> is supported.

-     *

-     * @return Whether <i>mark</i> is supported or not.

-     */

-    public boolean markSupported() {

-        return markSupported;

-    }

-

-    /**

-     * Read a byte.

-     *

-     * @return Either The byte value returned by <code>processByte()</code>

-     * or <code>-1</code> if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read() throws IOException {

-        if (eof) {

-            throw new IOException("Read after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position++;

-        return processByte();

-    }

-

-    /**

-     * Read some bytes into the specified array.

-     *

-     * @param bytes The byte array to read into

-     * @return The number of bytes read or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read(byte[] bytes) throws IOException {

-        return read(bytes, 0, bytes.length);

-    }

-

-    /**

-     * Read the specified number bytes into an array.

-     *

-     * @param bytes The byte array to read into.

-     * @param offset The offset to start reading bytes into.

-     * @param length The number of bytes to read.

-     * @return The number of bytes read or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read(byte[] bytes, int offset, int length) throws IOException {

-        if (eof) {

-            throw new IOException("Read after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position += length;

-        int returnLength = length;

-        if (position > size) {

-            returnLength = length - (int)(position - size);

-            position = size;

-        }

-        processBytes(bytes, offset, returnLength);

-        return returnLength;

-    }

-

-    /**

-     * Reset the stream to the point when mark was last called.

-     *

-     * @throws UnsupportedOperationException if mark is not supported.

-     * @throws IOException If no position has been marked

-     * or the read limit has been exceed since the last position was

-     * marked.

-     */

-    public synchronized void reset() throws IOException {

-        if (!markSupported) {

-            throw new UnsupportedOperationException("Mark not supported");

-        }

-        if (mark < 0) {

-            throw new IOException("No position has been marked");

-        }

-        if (position > (mark + readlimit)) {

-            throw new IOException("Marked position [" + mark +

-                    "] is no longer valid - passed the read limit [" +

-                    readlimit + "]");

-        }

-        position = mark;

-        eof = false;

-    }

-

-    /**

-     * Skip a specified number of bytes.

-     *

-     * @param numberOfBytes The number of bytes to skip.

-     * @return The number of bytes skipped or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public long skip(long numberOfBytes) throws IOException {

-        if (eof) {

-            throw new IOException("Skip after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position += numberOfBytes;

-        long returnLength = numberOfBytes;

-        if (position > size) {

-            returnLength = numberOfBytes - (position - size);

-            position = size;

-        }

-        return returnLength;

-    }

-

-    /**

-     * Return a byte value for the  <code>read()</code> method.

-     * <p>

-     * This implementation returns zero.

-     *

-     * @return This implementation always returns zero.

-     */

-    protected int processByte() {

-        // do nothing - overridable by subclass

-        return 0;

-    }

-

-    /**

-     * Process the bytes for the <code>read(byte[], offset, length)</code>

-     * method.

-     * <p>

-     * This implementation leaves the byte array unchanged.

-     *

-     * @param bytes The byte array

-     * @param offset The offset to start at.

-     * @param length The number of bytes.

-     */

-    protected void processBytes(byte[] bytes, int offset, int length) {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Handle End of File.

-     *

-     * @return <code>-1</code> if <code>throwEofException</code> is

-     * set to <code>false</code>

-     * @throws EOFException if <code>throwEofException</code> is set

-     * to <code>true</code>.

-     */

-    private int doEndOfFile() throws EOFException {

-        eof = true;

-        if (throwEofException) {

-            throw new EOFException();

-        }

-        return -1;

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A functional, light weight {@link InputStream} that emulates
+ * a stream of a specified size.
+ * <p>
+ * This implementation provides a light weight
+ * object for testing with an {@link InputStream}
+ * where the contents don't matter.
+ * <p>
+ * One use case would be for testing the handling of
+ * large {@link InputStream} as it can emulate that
+ * scenario without the overhead of actually processing
+ * large numbers of bytes - significantly speeding up
+ * test execution times.
+ * <p>
+ * This implementation returns zero from the method that
+ * reads a byte and leaves the array unchanged in the read
+ * methods that are passed a byte array.
+ * If alternative data is required the <code>processByte()</code> and
+ * <code>processBytes()</code> methods can be implemented to generate
+ * data, for example:
+ *
+ * <pre>
+ *  public class TestInputStream extends NullInputStream {
+ *      public TestInputStream(int size) {
+ *          super(size);
+ *      }
+ *      protected int processByte() {
+ *          return ... // return required value here
+ *      }
+ *      protected void processBytes(byte[] bytes, int offset, int length) {
+ *          for (int i = offset; i < length; i++) {
+ *              bytes[i] = ... // set array value here
+ *          }
+ *      }
+ *  }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 463529 $
+ */
+public class NullInputStream extends InputStream {
+
+    private long size;
+    private long position;
+    private long mark = -1;
+    private long readlimit;
+    private boolean eof;
+    private boolean throwEofException;
+    private boolean markSupported;
+
+    /**
+     * Create an {@link InputStream} that emulates a specified size
+     * which supports marking and does not throw EOFException.
+     *
+     * @param size The size of the input stream to emulate.
+     */
+    public NullInputStream(long size) {
+       this(size, true, false);
+    }
+
+    /**
+     * Create an {@link InputStream} that emulates a specified
+     * size with option settings.
+     *
+     * @param size The size of the input stream to emulate.
+     * @param markSupported Whether this instance will support
+     * the <code>mark()</code> functionality.
+     * @param throwEofException Whether this implementation
+     * will throw an {@link EOFException} or return -1 when the
+     * end of file is reached.
+     */
+    public NullInputStream(long size, boolean markSupported, boolean throwEofException) {
+       this.size = size;
+       this.markSupported = markSupported;
+       this.throwEofException = throwEofException;
+    }
+
+    /**
+     * Return the current position.
+     *
+     * @return the current position.
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    /**
+     * Return the size this {@link InputStream} emulates.
+     *
+     * @return The size of the input stream to emulate.
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * Return the number of bytes that can be read.
+     *
+     * @return The number of bytes that can be read.
+     */
+    public int available() {
+        long avail = size - position;
+        if (avail <= 0) {
+            return 0;
+        } else if (avail > Integer.MAX_VALUE) {
+            return Integer.MAX_VALUE;
+        } else {
+            return (int)avail;
+        }
+    }
+
+    /**
+     * Close this input stream - resets the internal state to
+     * the initial values.
+     *
+     * @throws IOException If an error occurs.
+     */
+    public void close() throws IOException {
+        eof = false;
+        position = 0;
+        mark = -1;
+    }
+
+    /**
+     * Mark the current position.
+     *
+     * @param readlimit The number of bytes before this marked position
+     * is invalid.
+     * @throws UnsupportedOperationException if mark is not supported.
+     */
+    public synchronized void mark(int readlimit) {
+        if (!markSupported) {
+            throw new UnsupportedOperationException("Mark not supported");
+        }
+        mark = position;
+        this.readlimit = readlimit;
+    }
+
+    /**
+     * Indicates whether <i>mark</i> is supported.
+     *
+     * @return Whether <i>mark</i> is supported or not.
+     */
+    public boolean markSupported() {
+        return markSupported;
+    }
+
+    /**
+     * Read a byte.
+     *
+     * @return Either The byte value returned by <code>processByte()</code>
+     * or <code>-1</code> if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read() throws IOException {
+        if (eof) {
+            throw new IOException("Read after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position++;
+        return processByte();
+    }
+
+    /**
+     * Read some bytes into the specified array.
+     *
+     * @param bytes The byte array to read into
+     * @return The number of bytes read or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read(byte[] bytes) throws IOException {
+        return read(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Read the specified number bytes into an array.
+     *
+     * @param bytes The byte array to read into.
+     * @param offset The offset to start reading bytes into.
+     * @param length The number of bytes to read.
+     * @return The number of bytes read or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read(byte[] bytes, int offset, int length) throws IOException {
+        if (eof) {
+            throw new IOException("Read after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position += length;
+        int returnLength = length;
+        if (position > size) {
+            returnLength = length - (int)(position - size);
+            position = size;
+        }
+        processBytes(bytes, offset, returnLength);
+        return returnLength;
+    }
+
+    /**
+     * Reset the stream to the point when mark was last called.
+     *
+     * @throws UnsupportedOperationException if mark is not supported.
+     * @throws IOException If no position has been marked
+     * or the read limit has been exceed since the last position was
+     * marked.
+     */
+    public synchronized void reset() throws IOException {
+        if (!markSupported) {
+            throw new UnsupportedOperationException("Mark not supported");
+        }
+        if (mark < 0) {
+            throw new IOException("No position has been marked");
+        }
+        if (position > (mark + readlimit)) {
+            throw new IOException("Marked position [" + mark +
+                    "] is no longer valid - passed the read limit [" +
+                    readlimit + "]");
+        }
+        position = mark;
+        eof = false;
+    }
+
+    /**
+     * Skip a specified number of bytes.
+     *
+     * @param numberOfBytes The number of bytes to skip.
+     * @return The number of bytes skipped or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public long skip(long numberOfBytes) throws IOException {
+        if (eof) {
+            throw new IOException("Skip after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position += numberOfBytes;
+        long returnLength = numberOfBytes;
+        if (position > size) {
+            returnLength = numberOfBytes - (position - size);
+            position = size;
+        }
+        return returnLength;
+    }
+
+    /**
+     * Return a byte value for the  <code>read()</code> method.
+     * <p>
+     * This implementation returns zero.
+     *
+     * @return This implementation always returns zero.
+     */
+    protected int processByte() {
+        // do nothing - overridable by subclass
+        return 0;
+    }
+
+    /**
+     * Process the bytes for the <code>read(byte[], offset, length)</code>
+     * method.
+     * <p>
+     * This implementation leaves the byte array unchanged.
+     *
+     * @param bytes The byte array
+     * @param offset The offset to start at.
+     * @param length The number of bytes.
+     */
+    protected void processBytes(byte[] bytes, int offset, int length) {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Handle End of File.
+     *
+     * @return <code>-1</code> if <code>throwEofException</code> is
+     * set to <code>false</code>
+     * @throws EOFException if <code>throwEofException</code> is set
+     * to <code>true</code>.
+     */
+    private int doEndOfFile() throws EOFException {
+        eof = true;
+        if (throwEofException) {
+            throw new EOFException();
+        }
+        return -1;
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/NullReader.java b/src/org/apache/commons/io/input/NullReader.java
index 159e390..95f7400 100644
--- a/src/org/apache/commons/io/input/NullReader.java
+++ b/src/org/apache/commons/io/input/NullReader.java
@@ -1,313 +1,313 @@
-/*

- * 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.commons.io.input;

-

-import java.io.EOFException;

-import java.io.IOException;

-import java.io.Reader;

-

-/**

- * A functional, light weight {@link Reader} that emulates

- * a reader of a specified size.

- * <p>

- * This implementation provides a light weight

- * object for testing with an {@link Reader}

- * where the contents don't matter.

- * <p>

- * One use case would be for testing the handling of

- * large {@link Reader} as it can emulate that

- * scenario without the overhead of actually processing

- * large numbers of characters - significantly speeding up

- * test execution times.

- * <p>

- * This implementation returns a space from the method that

- * reads a character and leaves the array unchanged in the read

- * methods that are passed a character array.

- * If alternative data is required the <code>processChar()</code> and

- * <code>processChars()</code> methods can be implemented to generate

- * data, for example:

- *

- * <pre>

- *  public class TestReader extends NullReader {

- *      public TestReader(int size) {

- *          super(size);

- *      }

- *      protected char processChar() {

- *          return ... // return required value here

- *      }

- *      protected void processChars(char[] chars, int offset, int length) {

- *          for (int i = offset; i < length; i++) {

- *              chars[i] = ... // set array value here

- *          }

- *      }

- *  }

- * </pre>

- *

- * @since Commons IO 1.3

- * @version $Revision: 463529 $

- */

-public class NullReader extends Reader {

-

-    private long size;

-    private long position;

-    private long mark = -1;

-    private long readlimit;

-    private boolean eof;

-    private boolean throwEofException;

-    private boolean markSupported;

-

-    /**

-     * Create a {@link Reader} that emulates a specified size

-     * which supports marking and does not throw EOFException.

-     *

-     * @param size The size of the reader to emulate.

-     */

-    public NullReader(long size) {

-       this(size, true, false);

-    }

-

-    /**

-     * Create a {@link Reader} that emulates a specified

-     * size with option settings.

-     *

-     * @param size The size of the reader to emulate.

-     * @param markSupported Whether this instance will support

-     * the <code>mark()</code> functionality.

-     * @param throwEofException Whether this implementation

-     * will throw an {@link EOFException} or return -1 when the

-     * end of file is reached.

-     */

-    public NullReader(long size, boolean markSupported, boolean throwEofException) {

-       this.size = size;

-       this.markSupported = markSupported;

-       this.throwEofException = throwEofException;

-    }

-

-    /**

-     * Return the current position.

-     *

-     * @return the current position.

-     */

-    public long getPosition() {

-        return position;

-    }

-

-    /**

-     * Return the size this {@link Reader} emulates.

-     *

-     * @return The size of the reader to emulate.

-     */

-    public long getSize() {

-        return size;

-    }

-

-    /**

-     * Close this Reader - resets the internal state to

-     * the initial values.

-     *

-     * @throws IOException If an error occurs.

-     */

-    public void close() throws IOException {

-        eof = false;

-        position = 0;

-        mark = -1;

-    }

-

-    /**

-     * Mark the current position.

-     *

-     * @param readlimit The number of characters before this marked position

-     * is invalid.

-     * @throws UnsupportedOperationException if mark is not supported.

-     */

-    public synchronized void mark(int readlimit) {

-        if (!markSupported) {

-            throw new UnsupportedOperationException("Mark not supported");

-        }

-        mark = position;

-        this.readlimit = readlimit;

-    }

-

-    /**

-     * Indicates whether <i>mark</i> is supported.

-     *

-     * @return Whether <i>mark</i> is supported or not.

-     */

-    public boolean markSupported() {

-        return markSupported;

-    }

-

-    /**

-     * Read a character.

-     *

-     * @return Either The character value returned by <code>processChar()</code>

-     * or <code>-1</code> if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read() throws IOException {

-        if (eof) {

-            throw new IOException("Read after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position++;

-        return processChar();

-    }

-

-    /**

-     * Read some characters into the specified array.

-     *

-     * @param chars The character array to read into

-     * @return The number of characters read or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read(char[] chars) throws IOException {

-        return read(chars, 0, chars.length);

-    }

-

-    /**

-     * Read the specified number characters into an array.

-     *

-     * @param chars The character array to read into.

-     * @param offset The offset to start reading characters into.

-     * @param length The number of characters to read.

-     * @return The number of characters read or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public int read(char[] chars, int offset, int length) throws IOException {

-        if (eof) {

-            throw new IOException("Read after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position += length;

-        int returnLength = length;

-        if (position > size) {

-            returnLength = length - (int)(position - size);

-            position = size;

-        }

-        processChars(chars, offset, returnLength);

-        return returnLength;

-    }

-

-    /**

-     * Reset the stream to the point when mark was last called.

-     *

-     * @throws UnsupportedOperationException if mark is not supported.

-     * @throws IOException If no position has been marked

-     * or the read limit has been exceed since the last position was

-     * marked.

-     */

-    public synchronized void reset() throws IOException {

-        if (!markSupported) {

-            throw new UnsupportedOperationException("Mark not supported");

-        }

-        if (mark < 0) {

-            throw new IOException("No position has been marked");

-        }

-        if (position > (mark + readlimit)) {

-            throw new IOException("Marked position [" + mark +

-                    "] is no longer valid - passed the read limit [" +

-                    readlimit + "]");

-        }

-        position = mark;

-        eof = false;

-    }

-

-    /**

-     * Skip a specified number of characters.

-     *

-     * @param numberOfChars The number of characters to skip.

-     * @return The number of characters skipped or <code>-1</code>

-     * if the end of file has been reached and

-     * <code>throwEofException</code> is set to <code>false</code>.

-     * @throws EOFException if the end of file is reached and

-     * <code>throwEofException</code> is set to <code>true</code>.

-     * @throws IOException if trying to read past the end of file.

-     */

-    public long skip(long numberOfChars) throws IOException {

-        if (eof) {

-            throw new IOException("Skip after end of file");

-        }

-        if (position == size) {

-            return doEndOfFile();

-        }

-        position += numberOfChars;

-        long returnLength = numberOfChars;

-        if (position > size) {

-            returnLength = numberOfChars - (position - size);

-            position = size;

-        }

-        return returnLength;

-    }

-

-    /**

-     * Return a character value for the  <code>read()</code> method.

-     * <p>

-     * This implementation returns zero.

-     *

-     * @return This implementation always returns zero.

-     */

-    protected int processChar() {

-        // do nothing - overridable by subclass

-        return 0;

-    }

-

-    /**

-     * Process the characters for the <code>read(char[], offset, length)</code>

-     * method.

-     * <p>

-     * This implementation leaves the character array unchanged.

-     *

-     * @param chars The character array

-     * @param offset The offset to start at.

-     * @param length The number of characters.

-     */

-    protected void processChars(char[] chars, int offset, int length) {

-        // do nothing - overridable by subclass

-    }

-

-    /**

-     * Handle End of File.

-     *

-     * @return <code>-1</code> if <code>throwEofException</code> is

-     * set to <code>false</code>

-     * @throws EOFException if <code>throwEofException</code> is set

-     * to <code>true</code>.

-     */

-    private int doEndOfFile() throws EOFException {

-        eof = true;

-        if (throwEofException) {

-            throw new EOFException();

-        }

-        return -1;

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A functional, light weight {@link Reader} that emulates
+ * a reader of a specified size.
+ * <p>
+ * This implementation provides a light weight
+ * object for testing with an {@link Reader}
+ * where the contents don't matter.
+ * <p>
+ * One use case would be for testing the handling of
+ * large {@link Reader} as it can emulate that
+ * scenario without the overhead of actually processing
+ * large numbers of characters - significantly speeding up
+ * test execution times.
+ * <p>
+ * This implementation returns a space from the method that
+ * reads a character and leaves the array unchanged in the read
+ * methods that are passed a character array.
+ * If alternative data is required the <code>processChar()</code> and
+ * <code>processChars()</code> methods can be implemented to generate
+ * data, for example:
+ *
+ * <pre>
+ *  public class TestReader extends NullReader {
+ *      public TestReader(int size) {
+ *          super(size);
+ *      }
+ *      protected char processChar() {
+ *          return ... // return required value here
+ *      }
+ *      protected void processChars(char[] chars, int offset, int length) {
+ *          for (int i = offset; i < length; i++) {
+ *              chars[i] = ... // set array value here
+ *          }
+ *      }
+ *  }
+ * </pre>
+ *
+ * @since Commons IO 1.3
+ * @version $Revision: 463529 $
+ */
+public class NullReader extends Reader {
+
+    private long size;
+    private long position;
+    private long mark = -1;
+    private long readlimit;
+    private boolean eof;
+    private boolean throwEofException;
+    private boolean markSupported;
+
+    /**
+     * Create a {@link Reader} that emulates a specified size
+     * which supports marking and does not throw EOFException.
+     *
+     * @param size The size of the reader to emulate.
+     */
+    public NullReader(long size) {
+       this(size, true, false);
+    }
+
+    /**
+     * Create a {@link Reader} that emulates a specified
+     * size with option settings.
+     *
+     * @param size The size of the reader to emulate.
+     * @param markSupported Whether this instance will support
+     * the <code>mark()</code> functionality.
+     * @param throwEofException Whether this implementation
+     * will throw an {@link EOFException} or return -1 when the
+     * end of file is reached.
+     */
+    public NullReader(long size, boolean markSupported, boolean throwEofException) {
+       this.size = size;
+       this.markSupported = markSupported;
+       this.throwEofException = throwEofException;
+    }
+
+    /**
+     * Return the current position.
+     *
+     * @return the current position.
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    /**
+     * Return the size this {@link Reader} emulates.
+     *
+     * @return The size of the reader to emulate.
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * Close this Reader - resets the internal state to
+     * the initial values.
+     *
+     * @throws IOException If an error occurs.
+     */
+    public void close() throws IOException {
+        eof = false;
+        position = 0;
+        mark = -1;
+    }
+
+    /**
+     * Mark the current position.
+     *
+     * @param readlimit The number of characters before this marked position
+     * is invalid.
+     * @throws UnsupportedOperationException if mark is not supported.
+     */
+    public synchronized void mark(int readlimit) {
+        if (!markSupported) {
+            throw new UnsupportedOperationException("Mark not supported");
+        }
+        mark = position;
+        this.readlimit = readlimit;
+    }
+
+    /**
+     * Indicates whether <i>mark</i> is supported.
+     *
+     * @return Whether <i>mark</i> is supported or not.
+     */
+    public boolean markSupported() {
+        return markSupported;
+    }
+
+    /**
+     * Read a character.
+     *
+     * @return Either The character value returned by <code>processChar()</code>
+     * or <code>-1</code> if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read() throws IOException {
+        if (eof) {
+            throw new IOException("Read after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position++;
+        return processChar();
+    }
+
+    /**
+     * Read some characters into the specified array.
+     *
+     * @param chars The character array to read into
+     * @return The number of characters read or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read(char[] chars) throws IOException {
+        return read(chars, 0, chars.length);
+    }
+
+    /**
+     * Read the specified number characters into an array.
+     *
+     * @param chars The character array to read into.
+     * @param offset The offset to start reading characters into.
+     * @param length The number of characters to read.
+     * @return The number of characters read or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public int read(char[] chars, int offset, int length) throws IOException {
+        if (eof) {
+            throw new IOException("Read after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position += length;
+        int returnLength = length;
+        if (position > size) {
+            returnLength = length - (int)(position - size);
+            position = size;
+        }
+        processChars(chars, offset, returnLength);
+        return returnLength;
+    }
+
+    /**
+     * Reset the stream to the point when mark was last called.
+     *
+     * @throws UnsupportedOperationException if mark is not supported.
+     * @throws IOException If no position has been marked
+     * or the read limit has been exceed since the last position was
+     * marked.
+     */
+    public synchronized void reset() throws IOException {
+        if (!markSupported) {
+            throw new UnsupportedOperationException("Mark not supported");
+        }
+        if (mark < 0) {
+            throw new IOException("No position has been marked");
+        }
+        if (position > (mark + readlimit)) {
+            throw new IOException("Marked position [" + mark +
+                    "] is no longer valid - passed the read limit [" +
+                    readlimit + "]");
+        }
+        position = mark;
+        eof = false;
+    }
+
+    /**
+     * Skip a specified number of characters.
+     *
+     * @param numberOfChars The number of characters to skip.
+     * @return The number of characters skipped or <code>-1</code>
+     * if the end of file has been reached and
+     * <code>throwEofException</code> is set to <code>false</code>.
+     * @throws EOFException if the end of file is reached and
+     * <code>throwEofException</code> is set to <code>true</code>.
+     * @throws IOException if trying to read past the end of file.
+     */
+    public long skip(long numberOfChars) throws IOException {
+        if (eof) {
+            throw new IOException("Skip after end of file");
+        }
+        if (position == size) {
+            return doEndOfFile();
+        }
+        position += numberOfChars;
+        long returnLength = numberOfChars;
+        if (position > size) {
+            returnLength = numberOfChars - (position - size);
+            position = size;
+        }
+        return returnLength;
+    }
+
+    /**
+     * Return a character value for the  <code>read()</code> method.
+     * <p>
+     * This implementation returns zero.
+     *
+     * @return This implementation always returns zero.
+     */
+    protected int processChar() {
+        // do nothing - overridable by subclass
+        return 0;
+    }
+
+    /**
+     * Process the characters for the <code>read(char[], offset, length)</code>
+     * method.
+     * <p>
+     * This implementation leaves the character array unchanged.
+     *
+     * @param chars The character array
+     * @param offset The offset to start at.
+     * @param length The number of characters.
+     */
+    protected void processChars(char[] chars, int offset, int length) {
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Handle End of File.
+     *
+     * @return <code>-1</code> if <code>throwEofException</code> is
+     * set to <code>false</code>
+     * @throws EOFException if <code>throwEofException</code> is set
+     * to <code>true</code>.
+     */
+    private int doEndOfFile() throws EOFException {
+        eof = true;
+        if (throwEofException) {
+            throw new EOFException();
+        }
+        return -1;
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/ProxyInputStream.java b/src/org/apache/commons/io/input/ProxyInputStream.java
index a08ad92..5b2edd5 100644
--- a/src/org/apache/commons/io/input/ProxyInputStream.java
+++ b/src/org/apache/commons/io/input/ProxyInputStream.java
@@ -1,129 +1,129 @@
-/*

- * 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.commons.io.input;

-

-import java.io.FilterInputStream;

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * A Proxy stream which acts as expected, that is it passes the method 

- * calls on to the proxied stream and doesn't change which methods are 

- * being called. 

- * <p>

- * It is an alternative base class to FilterInputStream

- * to increase reusability, because FilterInputStream changes the 

- * methods being called, such as read(byte[]) to read(byte[], int, int).

- * 

- * @author Stephen Colebourne

- * @version $Id: ProxyInputStream.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public abstract class ProxyInputStream extends FilterInputStream {

-

-    /**

-     * Constructs a new ProxyInputStream.

-     * 

-     * @param proxy  the InputStream to delegate to

-     */

-    public ProxyInputStream(InputStream proxy) {

-        super(proxy);

-        // the proxy is stored in a protected superclass variable named 'in'

-    }

-

-    /**

-     * Invokes the delegate's <code>read()</code> method.

-     * @return the byte read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read() throws IOException {

-        return in.read();

-    }

-

-    /**

-     * Invokes the delegate's <code>read(byte[])</code> method.

-     * @param bts the buffer to read the bytes into

-     * @return the number of bytes read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read(byte[] bts) throws IOException {

-        return in.read(bts);

-    }

-

-    /**

-     * Invokes the delegate's <code>read(byte[], int, int)</code> method.

-     * @param bts the buffer to read the bytes into

-     * @param st The start offset

-     * @param end The number of bytes to read

-     * @return the number of bytes read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read(byte[] bts, int st, int end) throws IOException {

-        return in.read(bts, st, end);

-    }

-

-    /**

-     * Invokes the delegate's <code>skip(long)</code> method.

-     * @param ln the number of bytes to skip

-     * @return the number of bytes to skipped or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public long skip(long ln) throws IOException {

-        return in.skip(ln);

-    }

-

-    /**

-     * Invokes the delegate's <code>available()</code> method.

-     * @return the number of available bytes

-     * @throws IOException if an I/O error occurs

-     */

-    public int available() throws IOException {

-        return in.available();

-    }

-

-    /**

-     * Invokes the delegate's <code>close()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        in.close();

-    }

-

-    /**

-     * Invokes the delegate's <code>mark(int)</code> method.

-     * @param idx read ahead limit

-     */

-    public synchronized void mark(int idx) {

-        in.mark(idx);

-    }

-

-    /**

-     * Invokes the delegate's <code>reset()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void reset() throws IOException {

-        in.reset();

-    }

-

-    /**

-     * Invokes the delegate's <code>markSupported()</code> method.

-     * @return true if mark is supported, otherwise false

-     */

-    public boolean markSupported() {

-        return in.markSupported();

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A Proxy stream which acts as expected, that is it passes the method 
+ * calls on to the proxied stream and doesn't change which methods are 
+ * being called. 
+ * <p>
+ * It is an alternative base class to FilterInputStream
+ * to increase reusability, because FilterInputStream changes the 
+ * methods being called, such as read(byte[]) to read(byte[], int, int).
+ * 
+ * @author Stephen Colebourne
+ * @version $Id: ProxyInputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public abstract class ProxyInputStream extends FilterInputStream {
+
+    /**
+     * Constructs a new ProxyInputStream.
+     * 
+     * @param proxy  the InputStream to delegate to
+     */
+    public ProxyInputStream(InputStream proxy) {
+        super(proxy);
+        // the proxy is stored in a protected superclass variable named 'in'
+    }
+
+    /**
+     * Invokes the delegate's <code>read()</code> method.
+     * @return the byte read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read() throws IOException {
+        return in.read();
+    }
+
+    /**
+     * Invokes the delegate's <code>read(byte[])</code> method.
+     * @param bts the buffer to read the bytes into
+     * @return the number of bytes read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read(byte[] bts) throws IOException {
+        return in.read(bts);
+    }
+
+    /**
+     * Invokes the delegate's <code>read(byte[], int, int)</code> method.
+     * @param bts the buffer to read the bytes into
+     * @param st The start offset
+     * @param end The number of bytes to read
+     * @return the number of bytes read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read(byte[] bts, int st, int end) throws IOException {
+        return in.read(bts, st, end);
+    }
+
+    /**
+     * Invokes the delegate's <code>skip(long)</code> method.
+     * @param ln the number of bytes to skip
+     * @return the number of bytes to skipped or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public long skip(long ln) throws IOException {
+        return in.skip(ln);
+    }
+
+    /**
+     * Invokes the delegate's <code>available()</code> method.
+     * @return the number of available bytes
+     * @throws IOException if an I/O error occurs
+     */
+    public int available() throws IOException {
+        return in.available();
+    }
+
+    /**
+     * Invokes the delegate's <code>close()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /**
+     * Invokes the delegate's <code>mark(int)</code> method.
+     * @param idx read ahead limit
+     */
+    public synchronized void mark(int idx) {
+        in.mark(idx);
+    }
+
+    /**
+     * Invokes the delegate's <code>reset()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void reset() throws IOException {
+        in.reset();
+    }
+
+    /**
+     * Invokes the delegate's <code>markSupported()</code> method.
+     * @return true if mark is supported, otherwise false
+     */
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/ProxyReader.java b/src/org/apache/commons/io/input/ProxyReader.java
index d55290f..81e113c 100644
--- a/src/org/apache/commons/io/input/ProxyReader.java
+++ b/src/org/apache/commons/io/input/ProxyReader.java
@@ -1,130 +1,130 @@
-/*

- * 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.commons.io.input;

-

-import java.io.FilterReader;

-import java.io.IOException;

-import java.io.Reader;

-

-/**

- * A Proxy stream which acts as expected, that is it passes the method 

- * calls on to the proxied stream and doesn't change which methods are 

- * being called. 

- * <p>

- * It is an alternative base class to FilterReader

- * to increase reusability, because FilterReader changes the 

- * methods being called, such as read(char[]) to read(char[], int, int).

- * 

- * @author Stephen Colebourne

- * @version $Id: ProxyReader.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public abstract class ProxyReader extends FilterReader {

-

-    /**

-     * Constructs a new ProxyReader.

-     * 

-     * @param proxy  the Reader to delegate to

-     */

-    public ProxyReader(Reader proxy) {

-        super(proxy);

-        // the proxy is stored in a protected superclass variable named 'in'

-    }

-

-    /**

-     * Invokes the delegate's <code>read()</code> method.

-     * @return the character read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read() throws IOException {

-        return in.read();

-    }

-

-    /**

-     * Invokes the delegate's <code>read(char[])</code> method.

-     * @param chr the buffer to read the characters into

-     * @return the number of characters read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read(char[] chr) throws IOException {

-        return in.read(chr);

-    }

-

-    /**

-     * Invokes the delegate's <code>read(char[], int, int)</code> method.

-     * @param chr the buffer to read the characters into

-     * @param st The start offset

-     * @param end The number of bytes to read

-     * @return the number of characters read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public int read(char[] chr, int st, int end) throws IOException {

-        return in.read(chr, st, end);

-    }

-

-    /**

-     * Invokes the delegate's <code>skip(long)</code> method.

-     * @param ln the number of bytes to skip

-     * @return the number of bytes to skipped or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     */

-    public long skip(long ln) throws IOException {

-        return in.skip(ln);

-    }

-

-    /**

-     * Invokes the delegate's <code>ready()</code> method.

-     * @return true if the stream is ready to be read

-     * @throws IOException if an I/O error occurs

-     */

-    public boolean ready() throws IOException {

-        return in.ready();

-    }

-

-    /**

-     * Invokes the delegate's <code>close()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        in.close();

-    }

-

-    /**

-     * Invokes the delegate's <code>mark(int)</code> method.

-     * @param idx read ahead limit

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void mark(int idx) throws IOException {

-        in.mark(idx);

-    }

-

-    /**

-     * Invokes the delegate's <code>reset()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void reset() throws IOException {

-        in.reset();

-    }

-

-    /**

-     * Invokes the delegate's <code>markSupported()</code> method.

-     * @return true if mark is supported, otherwise false

-     */

-    public boolean markSupported() {

-        return in.markSupported();

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A Proxy stream which acts as expected, that is it passes the method 
+ * calls on to the proxied stream and doesn't change which methods are 
+ * being called. 
+ * <p>
+ * It is an alternative base class to FilterReader
+ * to increase reusability, because FilterReader changes the 
+ * methods being called, such as read(char[]) to read(char[], int, int).
+ * 
+ * @author Stephen Colebourne
+ * @version $Id: ProxyReader.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public abstract class ProxyReader extends FilterReader {
+
+    /**
+     * Constructs a new ProxyReader.
+     * 
+     * @param proxy  the Reader to delegate to
+     */
+    public ProxyReader(Reader proxy) {
+        super(proxy);
+        // the proxy is stored in a protected superclass variable named 'in'
+    }
+
+    /**
+     * Invokes the delegate's <code>read()</code> method.
+     * @return the character read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read() throws IOException {
+        return in.read();
+    }
+
+    /**
+     * Invokes the delegate's <code>read(char[])</code> method.
+     * @param chr the buffer to read the characters into
+     * @return the number of characters read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read(char[] chr) throws IOException {
+        return in.read(chr);
+    }
+
+    /**
+     * Invokes the delegate's <code>read(char[], int, int)</code> method.
+     * @param chr the buffer to read the characters into
+     * @param st The start offset
+     * @param end The number of bytes to read
+     * @return the number of characters read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public int read(char[] chr, int st, int end) throws IOException {
+        return in.read(chr, st, end);
+    }
+
+    /**
+     * Invokes the delegate's <code>skip(long)</code> method.
+     * @param ln the number of bytes to skip
+     * @return the number of bytes to skipped or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     */
+    public long skip(long ln) throws IOException {
+        return in.skip(ln);
+    }
+
+    /**
+     * Invokes the delegate's <code>ready()</code> method.
+     * @return true if the stream is ready to be read
+     * @throws IOException if an I/O error occurs
+     */
+    public boolean ready() throws IOException {
+        return in.ready();
+    }
+
+    /**
+     * Invokes the delegate's <code>close()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /**
+     * Invokes the delegate's <code>mark(int)</code> method.
+     * @param idx read ahead limit
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void mark(int idx) throws IOException {
+        in.mark(idx);
+    }
+
+    /**
+     * Invokes the delegate's <code>reset()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void reset() throws IOException {
+        in.reset();
+    }
+
+    /**
+     * Invokes the delegate's <code>markSupported()</code> method.
+     * @return true if mark is supported, otherwise false
+     */
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/SwappedDataInputStream.java b/src/org/apache/commons/io/input/SwappedDataInputStream.java
index 5b65b1e..fe2f97e 100644
--- a/src/org/apache/commons/io/input/SwappedDataInputStream.java
+++ b/src/org/apache/commons/io/input/SwappedDataInputStream.java
@@ -1,251 +1,251 @@
-/*

- * 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.commons.io.input;

-

-import java.io.DataInput;

-import java.io.EOFException;

-import java.io.IOException;

-import java.io.InputStream;

-

-import org.apache.commons.io.EndianUtils;

-

-/**

- * DataInput for systems relying on little endian data formats.

- * When read, values will be changed from little endian to big 

- * endian formats for internal usage. 

- * <p>

- * <b>Origin of code: </b>Avalon Excalibur (IO)

- *

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @version CVS $Revision: 610010 $ $Date: 2008-01-08 14:50:59 +0000 (Tue, 08 Jan 2008) $

- */

-public class SwappedDataInputStream extends ProxyInputStream

-    implements DataInput

-{

-

-    /**

-     * Constructs a SwappedDataInputStream.

-     *

-     * @param input InputStream to read from

-     */

-    public SwappedDataInputStream( InputStream input )

-    {

-        super( input );

-    }

-

-    /**

-     * Return <code>{@link #readByte()} == 0</code>

-     * @return the true if the byte read is zero, otherwise false

-     * @throws IOException if an I/O error occurs

-     * @throws EOFException if an end of file is reached unexpectedly

-     */

-    public boolean readBoolean()

-        throws IOException, EOFException

-    {

-        return ( 0 == readByte() );

-    }

-

-    /**

-     * Invokes the delegate's <code>read()</code> method.

-     * @return the byte read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     * @throws EOFException if an end of file is reached unexpectedly

-     */

-    public byte readByte()

-        throws IOException, EOFException

-    {

-        return (byte)in.read();

-    }

-

-    /**

-     * Reads a character delegating to {@link #readShort()}.

-     * @return the byte read or -1 if the end of stream

-     * @throws IOException if an I/O error occurs

-     * @throws EOFException if an end of file is reached unexpectedly

-     */

-    public char readChar()

-        throws IOException, EOFException

-    {

-        return (char)readShort();

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedDouble(InputStream)}. 

-     * @return the read long

-     * @throws IOException if an I/O error occurs

-     * @throws EOFException if an end of file is reached unexpectedly

-     */

-    public double readDouble()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedDouble( in );

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedFloat(InputStream)}. 

-     * @return the read long

-     * @throws IOException if an I/O error occurs

-     * @throws EOFException if an end of file is reached unexpectedly

-     */

-    public float readFloat()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedFloat( in );

-    }

-

-    /**

-     * Invokes the delegate's <code>read(byte[] data, int, int)</code> method.

-     * 

-     * @param data the buffer to read the bytes into

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public void readFully( byte[] data )

-        throws IOException, EOFException

-    {

-        readFully( data, 0, data.length );

-    }

-

-

-    /**

-     * Invokes the delegate's <code>read(byte[] data, int, int)</code> method.

-     * 

-     * @param data the buffer to read the bytes into

-     * @param offset The start offset

-     * @param length The number of bytes to read

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public void readFully( byte[] data, int offset, int length )

-        throws IOException, EOFException

-    {

-        int remaining = length;

-

-        while( remaining > 0 )

-        {

-            int location = offset + ( length - remaining );

-            int count = read( data, location, remaining );

-

-            if( -1 == count )

-            {

-                throw new EOFException();

-            }

-

-            remaining -= count;

-        }

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedInteger(InputStream)}. 

-     * @return the read long

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public int readInt()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedInteger( in );

-    }

-

-    /**

-     * Not currently supported - throws {@link UnsupportedOperationException}.

-     * @return the line read

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public String readLine()

-        throws IOException, EOFException

-    {

-        throw new UnsupportedOperationException( 

-                "Operation not supported: readLine()" );

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedLong(InputStream)}. 

-     * @return the read long

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public long readLong()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedLong( in );

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedShort(InputStream)}. 

-     * @return the read long

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public short readShort()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedShort( in );

-    }

-

-    /**

-     * Invokes the delegate's <code>read()</code> method.

-     * @return the byte read or -1 if the end of stream

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public int readUnsignedByte()

-        throws IOException, EOFException

-    {

-        return in.read();

-    }

-

-    /**

-     * Delegates to {@link EndianUtils#readSwappedUnsignedShort(InputStream)}. 

-     * @return the read long

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public int readUnsignedShort()

-        throws IOException, EOFException

-    {

-        return EndianUtils.readSwappedUnsignedShort( in );

-    }

-

-    /**

-     * Not currently supported - throws {@link UnsupportedOperationException}.

-     * @return UTF String read

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public String readUTF()

-        throws IOException, EOFException

-    {

-        throw new UnsupportedOperationException( 

-                "Operation not supported: readUTF()" );

-    }

-

-    /**

-     * Invokes the delegate's <code>skip(int)</code> method.

-     * @param count the number of bytes to skip

-     * @return the number of bytes to skipped or -1 if the end of stream

-     * @throws EOFException if an end of file is reached unexpectedly

-     * @throws IOException if an I/O error occurs

-     */

-    public int skipBytes( int count )

-        throws IOException, EOFException

-    {

-        return (int)in.skip( count );

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.DataInput;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.EndianUtils;
+
+/**
+ * DataInput for systems relying on little endian data formats.
+ * When read, values will be changed from little endian to big 
+ * endian formats for internal usage. 
+ * <p>
+ * <b>Origin of code: </b>Avalon Excalibur (IO)
+ *
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version CVS $Revision: 610010 $ $Date: 2008-01-08 14:50:59 +0000 (Tue, 08 Jan 2008) $
+ */
+public class SwappedDataInputStream extends ProxyInputStream
+    implements DataInput
+{
+
+    /**
+     * Constructs a SwappedDataInputStream.
+     *
+     * @param input InputStream to read from
+     */
+    public SwappedDataInputStream( InputStream input )
+    {
+        super( input );
+    }
+
+    /**
+     * Return <code>{@link #readByte()} == 0</code>
+     * @return the true if the byte read is zero, otherwise false
+     * @throws IOException if an I/O error occurs
+     * @throws EOFException if an end of file is reached unexpectedly
+     */
+    public boolean readBoolean()
+        throws IOException, EOFException
+    {
+        return ( 0 == readByte() );
+    }
+
+    /**
+     * Invokes the delegate's <code>read()</code> method.
+     * @return the byte read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     * @throws EOFException if an end of file is reached unexpectedly
+     */
+    public byte readByte()
+        throws IOException, EOFException
+    {
+        return (byte)in.read();
+    }
+
+    /**
+     * Reads a character delegating to {@link #readShort()}.
+     * @return the byte read or -1 if the end of stream
+     * @throws IOException if an I/O error occurs
+     * @throws EOFException if an end of file is reached unexpectedly
+     */
+    public char readChar()
+        throws IOException, EOFException
+    {
+        return (char)readShort();
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedDouble(InputStream)}. 
+     * @return the read long
+     * @throws IOException if an I/O error occurs
+     * @throws EOFException if an end of file is reached unexpectedly
+     */
+    public double readDouble()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedDouble( in );
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedFloat(InputStream)}. 
+     * @return the read long
+     * @throws IOException if an I/O error occurs
+     * @throws EOFException if an end of file is reached unexpectedly
+     */
+    public float readFloat()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedFloat( in );
+    }
+
+    /**
+     * Invokes the delegate's <code>read(byte[] data, int, int)</code> method.
+     * 
+     * @param data the buffer to read the bytes into
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public void readFully( byte[] data )
+        throws IOException, EOFException
+    {
+        readFully( data, 0, data.length );
+    }
+
+
+    /**
+     * Invokes the delegate's <code>read(byte[] data, int, int)</code> method.
+     * 
+     * @param data the buffer to read the bytes into
+     * @param offset The start offset
+     * @param length The number of bytes to read
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public void readFully( byte[] data, int offset, int length )
+        throws IOException, EOFException
+    {
+        int remaining = length;
+
+        while( remaining > 0 )
+        {
+            int location = offset + ( length - remaining );
+            int count = read( data, location, remaining );
+
+            if( -1 == count )
+            {
+                throw new EOFException();
+            }
+
+            remaining -= count;
+        }
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedInteger(InputStream)}. 
+     * @return the read long
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public int readInt()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedInteger( in );
+    }
+
+    /**
+     * Not currently supported - throws {@link UnsupportedOperationException}.
+     * @return the line read
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public String readLine()
+        throws IOException, EOFException
+    {
+        throw new UnsupportedOperationException( 
+                "Operation not supported: readLine()" );
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedLong(InputStream)}. 
+     * @return the read long
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public long readLong()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedLong( in );
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedShort(InputStream)}. 
+     * @return the read long
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public short readShort()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedShort( in );
+    }
+
+    /**
+     * Invokes the delegate's <code>read()</code> method.
+     * @return the byte read or -1 if the end of stream
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public int readUnsignedByte()
+        throws IOException, EOFException
+    {
+        return in.read();
+    }
+
+    /**
+     * Delegates to {@link EndianUtils#readSwappedUnsignedShort(InputStream)}. 
+     * @return the read long
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public int readUnsignedShort()
+        throws IOException, EOFException
+    {
+        return EndianUtils.readSwappedUnsignedShort( in );
+    }
+
+    /**
+     * Not currently supported - throws {@link UnsupportedOperationException}.
+     * @return UTF String read
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public String readUTF()
+        throws IOException, EOFException
+    {
+        throw new UnsupportedOperationException( 
+                "Operation not supported: readUTF()" );
+    }
+
+    /**
+     * Invokes the delegate's <code>skip(int)</code> method.
+     * @param count the number of bytes to skip
+     * @return the number of bytes to skipped or -1 if the end of stream
+     * @throws EOFException if an end of file is reached unexpectedly
+     * @throws IOException if an I/O error occurs
+     */
+    public int skipBytes( int count )
+        throws IOException, EOFException
+    {
+        return (int)in.skip( count );
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/TeeInputStream.java b/src/org/apache/commons/io/input/TeeInputStream.java
index fed000e..acc4649 100644
--- a/src/org/apache/commons/io/input/TeeInputStream.java
+++ b/src/org/apache/commons/io/input/TeeInputStream.java
@@ -1,147 +1,147 @@
-/*

- * 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.commons.io.input;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-/**

- * InputStream proxy that transparently writes a copy of all bytes read

- * from the proxied stream to a given OutputStream. Using {@link #skip(long)}

- * or {@link #mark(int)}/{@link #reset()} on the stream will result on some

- * bytes from the input stream being skipped or duplicated in the output

- * stream.

- * <p>

- * The proxied input stream is closed when the {@link #close()} method is

- * called on this proxy. It is configurable whether the associated output

- * stream will also closed.

- *

- * @version $Id: TeeInputStream.java 587913 2007-10-24 15:47:30Z niallp $

- * @since Commons IO 1.4

- */

-public class TeeInputStream extends ProxyInputStream {

-

-    /**

-     * The output stream that will receive a copy of all bytes read from the

-     * proxied input stream.

-     */

-    private final OutputStream branch;

-

-    /**

-     * Flag for closing also the associated output stream when this

-     * stream is closed.

-     */

-    private final boolean closeBranch;

-

-    /**

-     * Creates a TeeInputStream that proxies the given {@link InputStream}

-     * and copies all read bytes to the given {@link OutputStream}. The given

-     * output stream will not be closed when this stream gets closed.

-     *

-     * @param input input stream to be proxied

-     * @param branch output stream that will receive a copy of all bytes read

-     */

-    public TeeInputStream(InputStream input, OutputStream branch) {

-        this(input, branch, false);

-    }

-

-    /**

-     * Creates a TeeInputStream that proxies the given {@link InputStream}

-     * and copies all read bytes to the given {@link OutputStream}. The given

-     * output stream will be closed when this stream gets closed if the

-     * closeBranch parameter is <code>true</code>.

-     *

-     * @param input input stream to be proxied

-     * @param branch output stream that will receive a copy of all bytes read

-     * @param closeBranch flag for closing also the output stream when this

-     *                    stream is closed

-     */

-    public TeeInputStream(

-            InputStream input, OutputStream branch, boolean closeBranch) {

-        super(input);

-        this.branch = branch;

-        this.closeBranch = closeBranch;

-    }

-

-    /**

-     * Closes the proxied input stream and, if so configured, the associated

-     * output stream. An exception thrown from one stream will not prevent

-     * closing of the other stream.

-     *

-     * @throws IOException if either of the streams could not be closed

-     */

-    public void close() throws IOException {

-        try {

-            super.close();

-        } finally {

-            if (closeBranch) {

-                branch.close();

-            }

-        }

-    }

-

-    /**

-     * Reads a single byte from the proxied input stream and writes it to

-     * the associated output stream.

-     *

-     * @return next byte from the stream, or -1 if the stream has ended

-     * @throws IOException if the stream could not be read (or written) 

-     */

-    public int read() throws IOException {

-        int ch = super.read();

-        if (ch != -1) {

-            branch.write(ch);

-        }

-        return ch;

-    }

-

-    /**

-     * Reads bytes from the proxied input stream and writes the read bytes

-     * to the associated output stream.

-     *

-     * @param bts byte buffer

-     * @param st start offset within the buffer

-     * @param end maximum number of bytes to read

-     * @return number of bytes read, or -1 if the stream has ended

-     * @throws IOException if the stream could not be read (or written) 

-     */

-    public int read(byte[] bts, int st, int end) throws IOException {

-        int n = super.read(bts, st, end);

-        if (n != -1) {

-            branch.write(bts, st, n);

-        }

-        return n;

-    }

-

-    /**

-     * Reads bytes from the proxied input stream and writes the read bytes

-     * to the associated output stream.

-     *

-     * @param bts byte buffer

-     * @return number of bytes read, or -1 if the stream has ended

-     * @throws IOException if the stream could not be read (or written) 

-     */

-    public int read(byte[] bts) throws IOException {

-        int n = super.read(bts);

-        if (n != -1) {

-            branch.write(bts, 0, n);

-        }

-        return n;

-    }

-

-}

+/*
+ * 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.commons.io.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * InputStream proxy that transparently writes a copy of all bytes read
+ * from the proxied stream to a given OutputStream. Using {@link #skip(long)}
+ * or {@link #mark(int)}/{@link #reset()} on the stream will result on some
+ * bytes from the input stream being skipped or duplicated in the output
+ * stream.
+ * <p>
+ * The proxied input stream is closed when the {@link #close()} method is
+ * called on this proxy. It is configurable whether the associated output
+ * stream will also closed.
+ *
+ * @version $Id: TeeInputStream.java 587913 2007-10-24 15:47:30Z niallp $
+ * @since Commons IO 1.4
+ */
+public class TeeInputStream extends ProxyInputStream {
+
+    /**
+     * The output stream that will receive a copy of all bytes read from the
+     * proxied input stream.
+     */
+    private final OutputStream branch;
+
+    /**
+     * Flag for closing also the associated output stream when this
+     * stream is closed.
+     */
+    private final boolean closeBranch;
+
+    /**
+     * Creates a TeeInputStream that proxies the given {@link InputStream}
+     * and copies all read bytes to the given {@link OutputStream}. The given
+     * output stream will not be closed when this stream gets closed.
+     *
+     * @param input input stream to be proxied
+     * @param branch output stream that will receive a copy of all bytes read
+     */
+    public TeeInputStream(InputStream input, OutputStream branch) {
+        this(input, branch, false);
+    }
+
+    /**
+     * Creates a TeeInputStream that proxies the given {@link InputStream}
+     * and copies all read bytes to the given {@link OutputStream}. The given
+     * output stream will be closed when this stream gets closed if the
+     * closeBranch parameter is <code>true</code>.
+     *
+     * @param input input stream to be proxied
+     * @param branch output stream that will receive a copy of all bytes read
+     * @param closeBranch flag for closing also the output stream when this
+     *                    stream is closed
+     */
+    public TeeInputStream(
+            InputStream input, OutputStream branch, boolean closeBranch) {
+        super(input);
+        this.branch = branch;
+        this.closeBranch = closeBranch;
+    }
+
+    /**
+     * Closes the proxied input stream and, if so configured, the associated
+     * output stream. An exception thrown from one stream will not prevent
+     * closing of the other stream.
+     *
+     * @throws IOException if either of the streams could not be closed
+     */
+    public void close() throws IOException {
+        try {
+            super.close();
+        } finally {
+            if (closeBranch) {
+                branch.close();
+            }
+        }
+    }
+
+    /**
+     * Reads a single byte from the proxied input stream and writes it to
+     * the associated output stream.
+     *
+     * @return next byte from the stream, or -1 if the stream has ended
+     * @throws IOException if the stream could not be read (or written) 
+     */
+    public int read() throws IOException {
+        int ch = super.read();
+        if (ch != -1) {
+            branch.write(ch);
+        }
+        return ch;
+    }
+
+    /**
+     * Reads bytes from the proxied input stream and writes the read bytes
+     * to the associated output stream.
+     *
+     * @param bts byte buffer
+     * @param st start offset within the buffer
+     * @param end maximum number of bytes to read
+     * @return number of bytes read, or -1 if the stream has ended
+     * @throws IOException if the stream could not be read (or written) 
+     */
+    public int read(byte[] bts, int st, int end) throws IOException {
+        int n = super.read(bts, st, end);
+        if (n != -1) {
+            branch.write(bts, st, n);
+        }
+        return n;
+    }
+
+    /**
+     * Reads bytes from the proxied input stream and writes the read bytes
+     * to the associated output stream.
+     *
+     * @param bts byte buffer
+     * @return number of bytes read, or -1 if the stream has ended
+     * @throws IOException if the stream could not be read (or written) 
+     */
+    public int read(byte[] bts) throws IOException {
+        int n = super.read(bts);
+        if (n != -1) {
+            branch.write(bts, 0, n);
+        }
+        return n;
+    }
+
+}
diff --git a/src/org/apache/commons/io/input/package.html b/src/org/apache/commons/io/input/package.html
index 9aa8b15..440fa0f 100644
--- a/src/org/apache/commons/io/input/package.html
+++ b/src/org/apache/commons/io/input/package.html
@@ -1,25 +1,25 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body>

-<p>

-This package provides implementations of input classes, such as

-<code>InputStream</code> and <code>Reader</code>.

-</p>

-</body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body>
+<p>
+This package provides implementations of input classes, such as
+<code>InputStream</code> and <code>Reader</code>.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/commons/io/output/ByteArrayOutputStream.java b/src/org/apache/commons/io/output/ByteArrayOutputStream.java
index 906a41d..034ddc8 100644
--- a/src/org/apache/commons/io/output/ByteArrayOutputStream.java
+++ b/src/org/apache/commons/io/output/ByteArrayOutputStream.java
@@ -1,312 +1,312 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.io.UnsupportedEncodingException;

-import java.util.ArrayList;

-import java.util.List;

-

-/**

- * This class implements an output stream in which the data is

- * written into a byte array. The buffer automatically grows as data

- * is written to it.

- * <p>

- * The data can be retrieved using <code>toByteArray()</code> and

- * <code>toString()</code>.

- * <p>

- * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in

- * this class can be called after the stream has been closed without

- * generating an <tt>IOException</tt>.

- * <p>

- * This is an alternative implementation of the java.io.ByteArrayOutputStream

- * class. The original implementation only allocates 32 bytes at the beginning.

- * As this class is designed for heavy duty it starts at 1024 bytes. In contrast

- * to the original it doesn't reallocate the whole memory block but allocates

- * additional buffers. This way no buffers need to be garbage collected and

- * the contents don't have to be copied to the new buffer. This class is

- * designed to behave exactly like the original. The only exception is the

- * deprecated toString(int) method that has been ignored.

- *

- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>

- * @author Holger Hoffstatte

- * @version $Id: ByteArrayOutputStream.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class ByteArrayOutputStream extends OutputStream {

-

-    /** A singleton empty byte array. */

-    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

-

-    /** The list of buffers, which grows and never reduces. */

-    private List<byte[]> buffers = new ArrayList<byte[]>();

-    /** The index of the current buffer. */

-    private int currentBufferIndex;

-    /** The total count of bytes in all the filled buffers. */

-    private int filledBufferSum;

-    /** The current buffer. */

-    private byte[] currentBuffer;

-    /** The total count of bytes written. */

-    private int count;

-

-    /**

-     * Creates a new byte array output stream. The buffer capacity is

-     * initially 1024 bytes, though its size increases if necessary.

-     */

-    public ByteArrayOutputStream() {

-        this(1024);

-    }

-

-    /**

-     * Creates a new byte array output stream, with a buffer capacity of

-     * the specified size, in bytes.

-     *

-     * @param size  the initial size

-     * @throws IllegalArgumentException if size is negative

-     */

-    public ByteArrayOutputStream(int size) {

-        if (size < 0) {

-            throw new IllegalArgumentException(

-                "Negative initial size: " + size);

-        }

-        needNewBuffer(size);

-    }

-

-    /**

-     * Return the appropriate <code>byte[]</code> buffer

-     * specified by index.

-     *

-     * @param index  the index of the buffer required

-     * @return the buffer

-     */

-    private byte[] getBuffer(int index) {

-        return buffers.get(index);

-    }

-

-    /**

-     * Makes a new buffer available either by allocating

-     * a new one or re-cycling an existing one.

-     *

-     * @param newcount  the size of the buffer if one is created

-     */

-    private void needNewBuffer(int newcount) {

-        if (currentBufferIndex < buffers.size() - 1) {

-            //Recycling old buffer

-            filledBufferSum += currentBuffer.length;

-

-            currentBufferIndex++;

-            currentBuffer = getBuffer(currentBufferIndex);

-        } else {

-            //Creating new buffer

-            int newBufferSize;

-            if (currentBuffer == null) {

-                newBufferSize = newcount;

-                filledBufferSum = 0;

-            } else {

-                newBufferSize = Math.max(

-                    currentBuffer.length << 1,

-                    newcount - filledBufferSum);

-                filledBufferSum += currentBuffer.length;

-            }

-

-            currentBufferIndex++;

-            currentBuffer = new byte[newBufferSize];

-            buffers.add(currentBuffer);

-        }

-    }

-

-    /**

-     * Write the bytes to byte array.

-     * @param b the bytes to write

-     * @param off The start offset

-     * @param len The number of bytes to write

-     */

-    @Override

-    public void write(byte[] b, int off, int len) {

-        if ((off < 0)

-                || (off > b.length)

-                || (len < 0)

-                || ((off + len) > b.length)

-                || ((off + len) < 0)) {

-            throw new IndexOutOfBoundsException();

-        } else if (len == 0) {

-            return;

-        }

-        synchronized (this) {

-            int newcount = count + len;

-            int remaining = len;

-            int inBufferPos = count - filledBufferSum;

-            while (remaining > 0) {

-                int part = Math.min(remaining, currentBuffer.length - inBufferPos);

-                System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);

-                remaining -= part;

-                if (remaining > 0) {

-                    needNewBuffer(newcount);

-                    inBufferPos = 0;

-                }

-            }

-            count = newcount;

-        }

-    }

-

-    /**

-     * Write a byte to byte array.

-     * @param b the byte to write

-     */

-    @Override

-    public synchronized void write(int b) {

-        int inBufferPos = count - filledBufferSum;

-        if (inBufferPos == currentBuffer.length) {

-            needNewBuffer(count + 1);

-            inBufferPos = 0;

-        }

-        currentBuffer[inBufferPos] = (byte) b;

-        count++;

-    }

-

-    /**

-     * Writes the entire contents of the specified input stream to this

-     * byte stream. Bytes from the input stream are read directly into the

-     * internal buffers of this streams.

-     *

-     * @param in the input stream to read from

-     * @return total number of bytes read from the input stream

-     *         (and written to this stream)

-     * @throws IOException if an I/O error occurs while reading the input stream

-     * @since Commons IO 1.4

-     */

-    public synchronized int write(InputStream in) throws IOException {

-        int readCount = 0;

-        int inBufferPos = count - filledBufferSum;

-        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);

-        while (n != -1) {

-            readCount += n;

-            inBufferPos += n;

-            count += n;

-            if (inBufferPos == currentBuffer.length) {

-                needNewBuffer(currentBuffer.length);

-                inBufferPos = 0;

-            }

-            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);

-        }

-        return readCount;

-    }

-

-    /**

-     * Return the current size of the byte array.

-     * @return the current size of the byte array

-     */

-    public synchronized int size() {

-        return count;

-    }

-

-    /**

-     * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in

-     * this class can be called after the stream has been closed without

-     * generating an <tt>IOException</tt>.

-     *

-     * @throws IOException never (this method should not declare this exception

-     * but it has to now due to backwards compatability)

-     */

-    @Override

-    public void close() throws IOException {

-        //nop

-    }

-

-    /**

-     * @see java.io.ByteArrayOutputStream#reset()

-     */

-    public synchronized void reset() {

-        count = 0;

-        filledBufferSum = 0;

-        currentBufferIndex = 0;

-        currentBuffer = getBuffer(currentBufferIndex);

-    }

-

-    /**

-     * Writes the entire contents of this byte stream to the

-     * specified output stream.

-     *

-     * @param out  the output stream to write to

-     * @throws IOException if an I/O error occurs, such as if the stream is closed

-     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)

-     */

-    public synchronized void writeTo(OutputStream out) throws IOException {

-        int remaining = count;

-        for (int i = 0; i < buffers.size(); i++) {

-            byte[] buf = getBuffer(i);

-            int c = Math.min(buf.length, remaining);

-            out.write(buf, 0, c);

-            remaining -= c;

-            if (remaining == 0) {

-                break;

-            }

-        }

-    }

-

-    /**

-     * Gets the curent contents of this byte stream as a byte array.

-     * The result is independent of this stream.

-     *

-     * @return the current contents of this output stream, as a byte array

-     * @see java.io.ByteArrayOutputStream#toByteArray()

-     */

-    public synchronized byte[] toByteArray() {

-        int remaining = count;

-        if (remaining == 0) {

-            return EMPTY_BYTE_ARRAY;

-        }

-        byte newbuf[] = new byte[remaining];

-        int pos = 0;

-        for (int i = 0; i < buffers.size(); i++) {

-            byte[] buf = getBuffer(i);

-            int c = Math.min(buf.length, remaining);

-            System.arraycopy(buf, 0, newbuf, pos, c);

-            pos += c;

-            remaining -= c;

-            if (remaining == 0) {

-                break;

-            }

-        }

-        return newbuf;

-    }

-

-    /**

-     * Gets the curent contents of this byte stream as a string.

-     * @return the contents of the byte array as a String

-     * @see java.io.ByteArrayOutputStream#toString()

-     */

-    @Override

-    public String toString() {

-        return new String(toByteArray());

-    }

-

-    /**

-     * Gets the curent contents of this byte stream as a string

-     * using the specified encoding.

-     *

-     * @param enc  the name of the character encoding

-     * @return the string converted from the byte array

-     * @throws UnsupportedEncodingException if the encoding is not supported

-     * @see java.io.ByteArrayOutputStream#toString(String)

-     */

-    public String toString(String enc) throws UnsupportedEncodingException {

-        return new String(toByteArray(), enc);

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class implements an output stream in which the data is
+ * written into a byte array. The buffer automatically grows as data
+ * is written to it.
+ * <p>
+ * The data can be retrieved using <code>toByteArray()</code> and
+ * <code>toString()</code>.
+ * <p>
+ * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+ * this class can be called after the stream has been closed without
+ * generating an <tt>IOException</tt>.
+ * <p>
+ * This is an alternative implementation of the java.io.ByteArrayOutputStream
+ * class. The original implementation only allocates 32 bytes at the beginning.
+ * As this class is designed for heavy duty it starts at 1024 bytes. In contrast
+ * to the original it doesn't reallocate the whole memory block but allocates
+ * additional buffers. This way no buffers need to be garbage collected and
+ * the contents don't have to be copied to the new buffer. This class is
+ * designed to behave exactly like the original. The only exception is the
+ * deprecated toString(int) method that has been ignored.
+ *
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @author Holger Hoffstatte
+ * @version $Id: ByteArrayOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class ByteArrayOutputStream extends OutputStream {
+
+    /** A singleton empty byte array. */
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    /** The list of buffers, which grows and never reduces. */
+    private List<byte[]> buffers = new ArrayList<byte[]>();
+    /** The index of the current buffer. */
+    private int currentBufferIndex;
+    /** The total count of bytes in all the filled buffers. */
+    private int filledBufferSum;
+    /** The current buffer. */
+    private byte[] currentBuffer;
+    /** The total count of bytes written. */
+    private int count;
+
+    /**
+     * Creates a new byte array output stream. The buffer capacity is
+     * initially 1024 bytes, though its size increases if necessary.
+     */
+    public ByteArrayOutputStream() {
+        this(1024);
+    }
+
+    /**
+     * Creates a new byte array output stream, with a buffer capacity of
+     * the specified size, in bytes.
+     *
+     * @param size  the initial size
+     * @throws IllegalArgumentException if size is negative
+     */
+    public ByteArrayOutputStream(int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException(
+                "Negative initial size: " + size);
+        }
+        needNewBuffer(size);
+    }
+
+    /**
+     * Return the appropriate <code>byte[]</code> buffer
+     * specified by index.
+     *
+     * @param index  the index of the buffer required
+     * @return the buffer
+     */
+    private byte[] getBuffer(int index) {
+        return buffers.get(index);
+    }
+
+    /**
+     * Makes a new buffer available either by allocating
+     * a new one or re-cycling an existing one.
+     *
+     * @param newcount  the size of the buffer if one is created
+     */
+    private void needNewBuffer(int newcount) {
+        if (currentBufferIndex < buffers.size() - 1) {
+            //Recycling old buffer
+            filledBufferSum += currentBuffer.length;
+
+            currentBufferIndex++;
+            currentBuffer = getBuffer(currentBufferIndex);
+        } else {
+            //Creating new buffer
+            int newBufferSize;
+            if (currentBuffer == null) {
+                newBufferSize = newcount;
+                filledBufferSum = 0;
+            } else {
+                newBufferSize = Math.max(
+                    currentBuffer.length << 1,
+                    newcount - filledBufferSum);
+                filledBufferSum += currentBuffer.length;
+            }
+
+            currentBufferIndex++;
+            currentBuffer = new byte[newBufferSize];
+            buffers.add(currentBuffer);
+        }
+    }
+
+    /**
+     * Write the bytes to byte array.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     */
+    @Override
+    public void write(byte[] b, int off, int len) {
+        if ((off < 0)
+                || (off > b.length)
+                || (len < 0)
+                || ((off + len) > b.length)
+                || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        }
+        synchronized (this) {
+            int newcount = count + len;
+            int remaining = len;
+            int inBufferPos = count - filledBufferSum;
+            while (remaining > 0) {
+                int part = Math.min(remaining, currentBuffer.length - inBufferPos);
+                System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
+                remaining -= part;
+                if (remaining > 0) {
+                    needNewBuffer(newcount);
+                    inBufferPos = 0;
+                }
+            }
+            count = newcount;
+        }
+    }
+
+    /**
+     * Write a byte to byte array.
+     * @param b the byte to write
+     */
+    @Override
+    public synchronized void write(int b) {
+        int inBufferPos = count - filledBufferSum;
+        if (inBufferPos == currentBuffer.length) {
+            needNewBuffer(count + 1);
+            inBufferPos = 0;
+        }
+        currentBuffer[inBufferPos] = (byte) b;
+        count++;
+    }
+
+    /**
+     * Writes the entire contents of the specified input stream to this
+     * byte stream. Bytes from the input stream are read directly into the
+     * internal buffers of this streams.
+     *
+     * @param in the input stream to read from
+     * @return total number of bytes read from the input stream
+     *         (and written to this stream)
+     * @throws IOException if an I/O error occurs while reading the input stream
+     * @since Commons IO 1.4
+     */
+    public synchronized int write(InputStream in) throws IOException {
+        int readCount = 0;
+        int inBufferPos = count - filledBufferSum;
+        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        while (n != -1) {
+            readCount += n;
+            inBufferPos += n;
+            count += n;
+            if (inBufferPos == currentBuffer.length) {
+                needNewBuffer(currentBuffer.length);
+                inBufferPos = 0;
+            }
+            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        }
+        return readCount;
+    }
+
+    /**
+     * Return the current size of the byte array.
+     * @return the current size of the byte array
+     */
+    public synchronized int size() {
+        return count;
+    }
+
+    /**
+     * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+     * this class can be called after the stream has been closed without
+     * generating an <tt>IOException</tt>.
+     *
+     * @throws IOException never (this method should not declare this exception
+     * but it has to now due to backwards compatability)
+     */
+    @Override
+    public void close() throws IOException {
+        //nop
+    }
+
+    /**
+     * @see java.io.ByteArrayOutputStream#reset()
+     */
+    public synchronized void reset() {
+        count = 0;
+        filledBufferSum = 0;
+        currentBufferIndex = 0;
+        currentBuffer = getBuffer(currentBufferIndex);
+    }
+
+    /**
+     * Writes the entire contents of this byte stream to the
+     * specified output stream.
+     *
+     * @param out  the output stream to write to
+     * @throws IOException if an I/O error occurs, such as if the stream is closed
+     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     */
+    public synchronized void writeTo(OutputStream out) throws IOException {
+        int remaining = count;
+        for (int i = 0; i < buffers.size(); i++) {
+            byte[] buf = getBuffer(i);
+            int c = Math.min(buf.length, remaining);
+            out.write(buf, 0, c);
+            remaining -= c;
+            if (remaining == 0) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a byte array.
+     * The result is independent of this stream.
+     *
+     * @return the current contents of this output stream, as a byte array
+     * @see java.io.ByteArrayOutputStream#toByteArray()
+     */
+    public synchronized byte[] toByteArray() {
+        int remaining = count;
+        if (remaining == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        byte newbuf[] = new byte[remaining];
+        int pos = 0;
+        for (int i = 0; i < buffers.size(); i++) {
+            byte[] buf = getBuffer(i);
+            int c = Math.min(buf.length, remaining);
+            System.arraycopy(buf, 0, newbuf, pos, c);
+            pos += c;
+            remaining -= c;
+            if (remaining == 0) {
+                break;
+            }
+        }
+        return newbuf;
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a string.
+     * @return the contents of the byte array as a String
+     * @see java.io.ByteArrayOutputStream#toString()
+     */
+    @Override
+    public String toString() {
+        return new String(toByteArray());
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a string
+     * using the specified encoding.
+     *
+     * @param enc  the name of the character encoding
+     * @return the string converted from the byte array
+     * @throws UnsupportedEncodingException if the encoding is not supported
+     * @see java.io.ByteArrayOutputStream#toString(String)
+     */
+    public String toString(String enc) throws UnsupportedEncodingException {
+        return new String(toByteArray(), enc);
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/CloseShieldOutputStream.java b/src/org/apache/commons/io/output/CloseShieldOutputStream.java
index 63f44be..58a9946 100644
--- a/src/org/apache/commons/io/output/CloseShieldOutputStream.java
+++ b/src/org/apache/commons/io/output/CloseShieldOutputStream.java
@@ -1,52 +1,52 @@
-/*

- * 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.commons.io.output;

-

-import java.io.OutputStream;

-

-/**

- * Proxy stream that prevents the underlying output stream from being closed.

- * <p>

- * This class is typically used in cases where an output stream needs to be

- * passed to a component that wants to explicitly close the stream even if

- * other components would still use the stream for output.

- *

- * @version $Id: CloseShieldOutputStream.java 587913 2007-10-24 15:47:30Z niallp $

- * @since Commons IO 1.4

- */

-public class CloseShieldOutputStream extends ProxyOutputStream {

-

-    /**

-     * Creates a proxy that shields the given output stream from being

-     * closed.

-     *

-     * @param out underlying output stream

-     */

-    public CloseShieldOutputStream(OutputStream out) {

-        super(out);

-    }

-

-    /**

-     * Replaces the underlying output stream with a {@link ClosedOutputStream}

-     * sentinel. The original output stream will remain open, but this proxy

-     * will appear closed.

-     */

-    public void close() {

-        out = new ClosedOutputStream();

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.OutputStream;
+
+/**
+ * Proxy stream that prevents the underlying output stream from being closed.
+ * <p>
+ * This class is typically used in cases where an output stream needs to be
+ * passed to a component that wants to explicitly close the stream even if
+ * other components would still use the stream for output.
+ *
+ * @version $Id: CloseShieldOutputStream.java 587913 2007-10-24 15:47:30Z niallp $
+ * @since Commons IO 1.4
+ */
+public class CloseShieldOutputStream extends ProxyOutputStream {
+
+    /**
+     * Creates a proxy that shields the given output stream from being
+     * closed.
+     *
+     * @param out underlying output stream
+     */
+    public CloseShieldOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /**
+     * Replaces the underlying output stream with a {@link ClosedOutputStream}
+     * sentinel. The original output stream will remain open, but this proxy
+     * will appear closed.
+     */
+    public void close() {
+        out = new ClosedOutputStream();
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/ClosedOutputStream.java b/src/org/apache/commons/io/output/ClosedOutputStream.java
index b585c0c..8c881c6 100644
--- a/src/org/apache/commons/io/output/ClosedOutputStream.java
+++ b/src/org/apache/commons/io/output/ClosedOutputStream.java
@@ -1,50 +1,50 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * Closed output stream. This stream throws an exception on all attempts to

- * write something to the stream.

- * <p>

- * Typically uses of this class include testing for corner cases in methods

- * that accept an output stream and acting as a sentinel value instead of

- * a <code>null</code> output stream.

- *

- * @version $Id: ClosedOutputStream.java 601751 2007-12-06 14:55:45Z niallp $

- * @since Commons IO 1.4

- */

-public class ClosedOutputStream extends OutputStream {

-    

-    /**

-     * A singleton.

-     */

-    public static final ClosedOutputStream CLOSED_OUTPUT_STREAM = new ClosedOutputStream();

-

-    /**

-     * Throws an {@link IOException} to indicate that the stream is closed.

-     *

-     * @param b ignored

-     * @throws IOException always thrown

-     */

-    public void write(int b) throws IOException {

-        throw new IOException("write(" + b + ") failed: stream is closed");

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Closed output stream. This stream throws an exception on all attempts to
+ * write something to the stream.
+ * <p>
+ * Typically uses of this class include testing for corner cases in methods
+ * that accept an output stream and acting as a sentinel value instead of
+ * a <code>null</code> output stream.
+ *
+ * @version $Id: ClosedOutputStream.java 601751 2007-12-06 14:55:45Z niallp $
+ * @since Commons IO 1.4
+ */
+public class ClosedOutputStream extends OutputStream {
+    
+    /**
+     * A singleton.
+     */
+    public static final ClosedOutputStream CLOSED_OUTPUT_STREAM = new ClosedOutputStream();
+
+    /**
+     * Throws an {@link IOException} to indicate that the stream is closed.
+     *
+     * @param b ignored
+     * @throws IOException always thrown
+     */
+    public void write(int b) throws IOException {
+        throw new IOException("write(" + b + ") failed: stream is closed");
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/CountingOutputStream.java b/src/org/apache/commons/io/output/CountingOutputStream.java
index 6728828..392276a 100644
--- a/src/org/apache/commons/io/output/CountingOutputStream.java
+++ b/src/org/apache/commons/io/output/CountingOutputStream.java
@@ -1,154 +1,154 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * A decorating output stream that counts the number of bytes that have passed

- * through the stream so far.

- * <p>

- * A typical use case would be during debugging, to ensure that data is being

- * written as expected.

- *

- * @version $Id: CountingOutputStream.java 471628 2006-11-06 04:06:45Z bayard $

- */

-public class CountingOutputStream extends ProxyOutputStream {

-

-    /** The count of bytes that have passed. */

-    private long count;

-

-    /**

-     * Constructs a new CountingOutputStream.

-     * 

-     * @param out  the OutputStream to write to

-     */

-    public CountingOutputStream( OutputStream out ) {

-        super(out);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Writes the contents of the specified byte array to this output stream

-     * keeping count of the number of bytes written.

-     *

-     * @param b  the bytes to write, not null

-     * @throws IOException if an I/O error occurs

-     * @see java.io.OutputStream#write(byte[])

-     */

-    public void write(byte[] b) throws IOException {

-        count += b.length;

-        super.write(b);

-    }

-

-    /**

-     * Writes a portion of the specified byte array to this output stream

-     * keeping count of the number of bytes written.

-     *

-     * @param b  the bytes to write, not null

-     * @param off  the start offset in the buffer

-     * @param len  the maximum number of bytes to write

-     * @throws IOException if an I/O error occurs

-     * @see java.io.OutputStream#write(byte[], int, int)

-     */

-    public void write(byte[] b, int off, int len) throws IOException {

-        count += len;

-        super.write(b, off, len);

-    }

-

-    /**

-     * Writes a single byte to the output stream adding to the count of the

-     * number of bytes written.

-     *

-     * @param b  the byte to write

-     * @throws IOException if an I/O error occurs

-     * @see java.io.OutputStream#write(int)

-     */

-    public void write(int b) throws IOException {

-        count++;

-        super.write(b);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * The number of bytes that have passed through this stream.

-     * <p>

-     * NOTE: From v1.3 this method throws an ArithmeticException if the

-     * count is greater than can be expressed by an <code>int</code>.

-     * See {@link #getByteCount()} for a method using a <code>long</code>.

-     *

-     * @return the number of bytes accumulated

-     * @throws ArithmeticException if the byte count is too large

-     */

-    public synchronized int getCount() {

-        long result = getByteCount();

-        if (result > Integer.MAX_VALUE) {

-            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");

-        }

-        return (int) result;

-    }

-

-    /** 

-     * Set the byte count back to 0. 

-     * <p>

-     * NOTE: From v1.3 this method throws an ArithmeticException if the

-     * count is greater than can be expressed by an <code>int</code>.

-     * See {@link #resetByteCount()} for a method using a <code>long</code>.

-     *

-     * @return the count previous to resetting

-     * @throws ArithmeticException if the byte count is too large

-     */

-    public synchronized int resetCount() {

-        long result = resetByteCount();

-        if (result > Integer.MAX_VALUE) {

-            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");

-        }

-        return (int) result;

-    }

-

-    /**

-     * The number of bytes that have passed through this stream.

-     * <p>

-     * NOTE: This method is an alternative for <code>getCount()</code>.

-     * It was added because that method returns an integer which will

-     * result in incorrect count for files over 2GB.

-     *

-     * @return the number of bytes accumulated

-     * @since Commons IO 1.3

-     */

-    public synchronized long getByteCount() {

-        return this.count;

-    }

-

-    /** 

-     * Set the byte count back to 0. 

-     * <p>

-     * NOTE: This method is an alternative for <code>resetCount()</code>.

-     * It was added because that method returns an integer which will

-     * result in incorrect count for files over 2GB.

-     *

-     * @return the count previous to resetting

-     * @since Commons IO 1.3

-     */

-    public synchronized long resetByteCount() {

-        long tmp = this.count;

-        this.count = 0;

-        return tmp;

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A decorating output stream that counts the number of bytes that have passed
+ * through the stream so far.
+ * <p>
+ * A typical use case would be during debugging, to ensure that data is being
+ * written as expected.
+ *
+ * @version $Id: CountingOutputStream.java 471628 2006-11-06 04:06:45Z bayard $
+ */
+public class CountingOutputStream extends ProxyOutputStream {
+
+    /** The count of bytes that have passed. */
+    private long count;
+
+    /**
+     * Constructs a new CountingOutputStream.
+     * 
+     * @param out  the OutputStream to write to
+     */
+    public CountingOutputStream( OutputStream out ) {
+        super(out);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Writes the contents of the specified byte array to this output stream
+     * keeping count of the number of bytes written.
+     *
+     * @param b  the bytes to write, not null
+     * @throws IOException if an I/O error occurs
+     * @see java.io.OutputStream#write(byte[])
+     */
+    public void write(byte[] b) throws IOException {
+        count += b.length;
+        super.write(b);
+    }
+
+    /**
+     * Writes a portion of the specified byte array to this output stream
+     * keeping count of the number of bytes written.
+     *
+     * @param b  the bytes to write, not null
+     * @param off  the start offset in the buffer
+     * @param len  the maximum number of bytes to write
+     * @throws IOException if an I/O error occurs
+     * @see java.io.OutputStream#write(byte[], int, int)
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        count += len;
+        super.write(b, off, len);
+    }
+
+    /**
+     * Writes a single byte to the output stream adding to the count of the
+     * number of bytes written.
+     *
+     * @param b  the byte to write
+     * @throws IOException if an I/O error occurs
+     * @see java.io.OutputStream#write(int)
+     */
+    public void write(int b) throws IOException {
+        count++;
+        super.write(b);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
+     * NOTE: From v1.3 this method throws an ArithmeticException if the
+     * count is greater than can be expressed by an <code>int</code>.
+     * See {@link #getByteCount()} for a method using a <code>long</code>.
+     *
+     * @return the number of bytes accumulated
+     * @throws ArithmeticException if the byte count is too large
+     */
+    public synchronized int getCount() {
+        long result = getByteCount();
+        if (result > Integer.MAX_VALUE) {
+            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
+        }
+        return (int) result;
+    }
+
+    /** 
+     * Set the byte count back to 0. 
+     * <p>
+     * NOTE: From v1.3 this method throws an ArithmeticException if the
+     * count is greater than can be expressed by an <code>int</code>.
+     * See {@link #resetByteCount()} for a method using a <code>long</code>.
+     *
+     * @return the count previous to resetting
+     * @throws ArithmeticException if the byte count is too large
+     */
+    public synchronized int resetCount() {
+        long result = resetByteCount();
+        if (result > Integer.MAX_VALUE) {
+            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
+        }
+        return (int) result;
+    }
+
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
+     * NOTE: This method is an alternative for <code>getCount()</code>.
+     * It was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the number of bytes accumulated
+     * @since Commons IO 1.3
+     */
+    public synchronized long getByteCount() {
+        return this.count;
+    }
+
+    /** 
+     * Set the byte count back to 0. 
+     * <p>
+     * NOTE: This method is an alternative for <code>resetCount()</code>.
+     * It was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the count previous to resetting
+     * @since Commons IO 1.3
+     */
+    public synchronized long resetByteCount() {
+        long tmp = this.count;
+        this.count = 0;
+        return tmp;
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/DeferredFileOutputStream.java b/src/org/apache/commons/io/output/DeferredFileOutputStream.java
index b8a9e96..c0f2233 100644
--- a/src/org/apache/commons/io/output/DeferredFileOutputStream.java
+++ b/src/org/apache/commons/io/output/DeferredFileOutputStream.java
@@ -1,269 +1,269 @@
-/*

- * 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.commons.io.output;

-

-import java.io.File;

-import java.io.FileInputStream;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.OutputStream;

-

-import org.apache.commons.io.IOUtils;

-

-

-/**

- * An output stream which will retain data in memory until a specified

- * threshold is reached, and only then commit it to disk. If the stream is

- * closed before the threshold is reached, the data will not be written to

- * disk at all.

- * <p>

- * This class originated in FileUpload processing. In this use case, you do

- * not know in advance the size of the file being uploaded. If the file is small

- * you want to store it in memory (for speed), but if the file is large you want

- * to store it to file (to avoid memory issues).

- *

- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>

- * @author gaxzerow

- *

- * @version $Id: DeferredFileOutputStream.java 606381 2007-12-22 02:03:16Z ggregory $

- */

-public class DeferredFileOutputStream

-    extends ThresholdingOutputStream

-{

-

-    // ----------------------------------------------------------- Data members

-

-

-    /**

-     * The output stream to which data will be written prior to the theshold

-     * being reached.

-     */

-    private ByteArrayOutputStream memoryOutputStream;

-

-

-    /**

-     * The output stream to which data will be written at any given time. This

-     * will always be one of <code>memoryOutputStream</code> or

-     * <code>diskOutputStream</code>.

-     */

-    private OutputStream currentOutputStream;

-

-

-    /**

-     * The file to which output will be directed if the threshold is exceeded.

-     */

-    private File outputFile;

-

-    /**

-     * The temporary file prefix.

-     */

-    private String prefix;

-

-    /**

-     * The temporary file suffix.

-     */

-    private String suffix;

-

-    /**

-     * The directory to use for temporary files.

-     */

-    private File directory;

-

-    

-    /**

-     * True when close() has been called successfully.

-     */

-    private boolean closed = false;

-

-    // ----------------------------------------------------------- Constructors

-

-

-    /**

-     * Constructs an instance of this class which will trigger an event at the

-     * specified threshold, and save data to a file beyond that point.

-     *

-     * @param threshold  The number of bytes at which to trigger an event.

-     * @param outputFile The file to which data is saved beyond the threshold.

-     */

-    public DeferredFileOutputStream(int threshold, File outputFile)

-    {

-        super(threshold);

-        this.outputFile = outputFile;

-

-        memoryOutputStream = new ByteArrayOutputStream();

-        currentOutputStream = memoryOutputStream;

-    }

-

-

-    /**

-     * Constructs an instance of this class which will trigger an event at the

-     * specified threshold, and save data to a temporary file beyond that point.

-     *

-     * @param threshold  The number of bytes at which to trigger an event.

-     * @param prefix Prefix to use for the temporary file.

-     * @param suffix Suffix to use for the temporary file.

-     * @param directory Temporary file directory.

-     *

-     * @since Commons IO 1.4

-     */

-    public DeferredFileOutputStream(int threshold, String prefix, String suffix, File directory)

-    {

-        this(threshold, (File)null);

-        if (prefix == null) {

-            throw new IllegalArgumentException("Temporary file prefix is missing");

-        }

-        this.prefix = prefix;

-        this.suffix = suffix;

-        this.directory = directory;

-    }

-

-

-    // --------------------------------------- ThresholdingOutputStream methods

-

-

-    /**

-     * Returns the current output stream. This may be memory based or disk

-     * based, depending on the current state with respect to the threshold.

-     *

-     * @return The underlying output stream.

-     *

-     * @exception IOException if an error occurs.

-     */

-    protected OutputStream getStream() throws IOException

-    {

-        return currentOutputStream;

-    }

-

-

-    /**

-     * Switches the underlying output stream from a memory based stream to one

-     * that is backed by disk. This is the point at which we realise that too

-     * much data is being written to keep in memory, so we elect to switch to

-     * disk-based storage.

-     *

-     * @exception IOException if an error occurs.

-     */

-    protected void thresholdReached() throws IOException

-    {

-        if (prefix != null) {

-            outputFile = File.createTempFile(prefix, suffix, directory);

-        }

-        FileOutputStream fos = new FileOutputStream(outputFile);

-        memoryOutputStream.writeTo(fos);

-        currentOutputStream = fos;

-        memoryOutputStream = null;

-    }

-

-

-    // --------------------------------------------------------- Public methods

-

-

-    /**

-     * Determines whether or not the data for this output stream has been

-     * retained in memory.

-     *

-     * @return <code>true</code> if the data is available in memory;

-     *         <code>false</code> otherwise.

-     */

-    public boolean isInMemory()

-    {

-        return (!isThresholdExceeded());

-    }

-

-

-    /**

-     * Returns the data for this output stream as an array of bytes, assuming

-     * that the data has been retained in memory. If the data was written to

-     * disk, this method returns <code>null</code>.

-     *

-     * @return The data for this output stream, or <code>null</code> if no such

-     *         data is available.

-     */

-    public byte[] getData()

-    {

-        if (memoryOutputStream != null)

-        {

-            return memoryOutputStream.toByteArray();

-        }

-        return null;

-    }

-

-

-    /**

-     * Returns either the output file specified in the constructor or

-     * the temporary file created or null.

-     * <p>

-     * If the constructor specifying the file is used then it returns that

-     * same output file, even when threashold has not been reached.

-     * <p>

-     * If constructor specifying a temporary file prefix/suffix is used

-     * then the temporary file created once the threashold is reached is returned

-     * If the threshold was not reached then <code>null</code> is returned.

-     *

-     * @return The file for this output stream, or <code>null</code> if no such

-     *         file exists.

-     */

-    public File getFile()

-    {

-        return outputFile;

-    }

-    

-        

-    /**

-     * Closes underlying output stream, and mark this as closed

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void close() throws IOException

-    {

-        super.close();

-        closed = true;

-    }

-    

-    

-    /**

-     * Writes the data from this output stream to the specified output stream,

-     * after it has been closed.

-     *

-     * @param out output stream to write to.

-     * @exception IOException if this stream is not yet closed or an error occurs.

-     */

-    public void writeTo(OutputStream out) throws IOException 

-    {

-        // we may only need to check if this is closed if we are working with a file

-        // but we should force the habit of closing wether we are working with

-        // a file or memory.

-        if (!closed)

-        {

-            throw new IOException("Stream not closed");

-        }

-        

-        if(isInMemory())

-        {

-            memoryOutputStream.writeTo(out);

-        }

-        else

-        {

-            FileInputStream fis = new FileInputStream(outputFile);

-            try {

-                IOUtils.copy(fis, out);

-            } finally {

-                IOUtils.closeQuietly(fis);

-            }

-        }

-    }

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+
+
+/**
+ * An output stream which will retain data in memory until a specified
+ * threshold is reached, and only then commit it to disk. If the stream is
+ * closed before the threshold is reached, the data will not be written to
+ * disk at all.
+ * <p>
+ * This class originated in FileUpload processing. In this use case, you do
+ * not know in advance the size of the file being uploaded. If the file is small
+ * you want to store it in memory (for speed), but if the file is large you want
+ * to store it to file (to avoid memory issues).
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author gaxzerow
+ *
+ * @version $Id: DeferredFileOutputStream.java 606381 2007-12-22 02:03:16Z ggregory $
+ */
+public class DeferredFileOutputStream
+    extends ThresholdingOutputStream
+{
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The output stream to which data will be written prior to the theshold
+     * being reached.
+     */
+    private ByteArrayOutputStream memoryOutputStream;
+
+
+    /**
+     * The output stream to which data will be written at any given time. This
+     * will always be one of <code>memoryOutputStream</code> or
+     * <code>diskOutputStream</code>.
+     */
+    private OutputStream currentOutputStream;
+
+
+    /**
+     * The file to which output will be directed if the threshold is exceeded.
+     */
+    private File outputFile;
+
+    /**
+     * The temporary file prefix.
+     */
+    private String prefix;
+
+    /**
+     * The temporary file suffix.
+     */
+    private String suffix;
+
+    /**
+     * The directory to use for temporary files.
+     */
+    private File directory;
+
+    
+    /**
+     * True when close() has been called successfully.
+     */
+    private boolean closed = false;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold, and save data to a file beyond that point.
+     *
+     * @param threshold  The number of bytes at which to trigger an event.
+     * @param outputFile The file to which data is saved beyond the threshold.
+     */
+    public DeferredFileOutputStream(int threshold, File outputFile)
+    {
+        super(threshold);
+        this.outputFile = outputFile;
+
+        memoryOutputStream = new ByteArrayOutputStream();
+        currentOutputStream = memoryOutputStream;
+    }
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold, and save data to a temporary file beyond that point.
+     *
+     * @param threshold  The number of bytes at which to trigger an event.
+     * @param prefix Prefix to use for the temporary file.
+     * @param suffix Suffix to use for the temporary file.
+     * @param directory Temporary file directory.
+     *
+     * @since Commons IO 1.4
+     */
+    public DeferredFileOutputStream(int threshold, String prefix, String suffix, File directory)
+    {
+        this(threshold, (File)null);
+        if (prefix == null) {
+            throw new IllegalArgumentException("Temporary file prefix is missing");
+        }
+        this.prefix = prefix;
+        this.suffix = suffix;
+        this.directory = directory;
+    }
+
+
+    // --------------------------------------- ThresholdingOutputStream methods
+
+
+    /**
+     * Returns the current output stream. This may be memory based or disk
+     * based, depending on the current state with respect to the threshold.
+     *
+     * @return The underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected OutputStream getStream() throws IOException
+    {
+        return currentOutputStream;
+    }
+
+
+    /**
+     * Switches the underlying output stream from a memory based stream to one
+     * that is backed by disk. This is the point at which we realise that too
+     * much data is being written to keep in memory, so we elect to switch to
+     * disk-based storage.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected void thresholdReached() throws IOException
+    {
+        if (prefix != null) {
+            outputFile = File.createTempFile(prefix, suffix, directory);
+        }
+        FileOutputStream fos = new FileOutputStream(outputFile);
+        memoryOutputStream.writeTo(fos);
+        currentOutputStream = fos;
+        memoryOutputStream = null;
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Determines whether or not the data for this output stream has been
+     * retained in memory.
+     *
+     * @return <code>true</code> if the data is available in memory;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isInMemory()
+    {
+        return (!isThresholdExceeded());
+    }
+
+
+    /**
+     * Returns the data for this output stream as an array of bytes, assuming
+     * that the data has been retained in memory. If the data was written to
+     * disk, this method returns <code>null</code>.
+     *
+     * @return The data for this output stream, or <code>null</code> if no such
+     *         data is available.
+     */
+    public byte[] getData()
+    {
+        if (memoryOutputStream != null)
+        {
+            return memoryOutputStream.toByteArray();
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns either the output file specified in the constructor or
+     * the temporary file created or null.
+     * <p>
+     * If the constructor specifying the file is used then it returns that
+     * same output file, even when threashold has not been reached.
+     * <p>
+     * If constructor specifying a temporary file prefix/suffix is used
+     * then the temporary file created once the threashold is reached is returned
+     * If the threshold was not reached then <code>null</code> is returned.
+     *
+     * @return The file for this output stream, or <code>null</code> if no such
+     *         file exists.
+     */
+    public File getFile()
+    {
+        return outputFile;
+    }
+    
+        
+    /**
+     * Closes underlying output stream, and mark this as closed
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void close() throws IOException
+    {
+        super.close();
+        closed = true;
+    }
+    
+    
+    /**
+     * Writes the data from this output stream to the specified output stream,
+     * after it has been closed.
+     *
+     * @param out output stream to write to.
+     * @exception IOException if this stream is not yet closed or an error occurs.
+     */
+    public void writeTo(OutputStream out) throws IOException 
+    {
+        // we may only need to check if this is closed if we are working with a file
+        // but we should force the habit of closing wether we are working with
+        // a file or memory.
+        if (!closed)
+        {
+            throw new IOException("Stream not closed");
+        }
+        
+        if(isInMemory())
+        {
+            memoryOutputStream.writeTo(out);
+        }
+        else
+        {
+            FileInputStream fis = new FileInputStream(outputFile);
+            try {
+                IOUtils.copy(fis, out);
+            } finally {
+                IOUtils.closeQuietly(fis);
+            }
+        }
+    }
+}
diff --git a/src/org/apache/commons/io/output/DemuxOutputStream.java b/src/org/apache/commons/io/output/DemuxOutputStream.java
index 280df14..5e501a3 100644
--- a/src/org/apache/commons/io/output/DemuxOutputStream.java
+++ b/src/org/apache/commons/io/output/DemuxOutputStream.java
@@ -1,105 +1,105 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * Data written to this stream is forwarded to a stream that has been associated

- * with this thread.

- *

- * @author <a href="mailto:peter@apache.org">Peter Donald</a>

- * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $

- */

-public class DemuxOutputStream

-    extends OutputStream

-{

-    private InheritableThreadLocal<OutputStream> m_streams = new InheritableThreadLocal<OutputStream>();

-

-    /**

-     * Bind the specified stream to the current thread.

-     *

-     * @param output the stream to bind

-     * @return the OutputStream that was previously active

-     */

-    public OutputStream bindStream( OutputStream output )

-    {

-        OutputStream stream = getStream();

-        m_streams.set( output );

-        return stream;

-    }

-

-    /**

-     * Closes stream associated with current thread.

-     *

-     * @throws IOException if an error occurs

-     */

-    @Override

-    public void close()

-        throws IOException

-    {

-        OutputStream output = getStream();

-        if( null != output )

-        {

-            output.close();

-        }

-    }

-

-    /**

-     * Flushes stream associated with current thread.

-     *

-     * @throws IOException if an error occurs

-     */

-    @Override

-    public void flush()

-        throws IOException

-    {

-        OutputStream output = getStream();

-        if( null != output )

-        {

-            output.flush();

-        }

-    }

-

-    /**

-     * Writes byte to stream associated with current thread.

-     *

-     * @param ch the byte to write to stream

-     * @throws IOException if an error occurs

-     */

-    @Override

-    public void write( int ch )

-        throws IOException

-    {

-        OutputStream output = getStream();

-        if( null != output )

-        {

-            output.write( ch );

-        }

-    }

-

-    /**

-     * Utility method to retrieve stream bound to current thread (if any).

-     *

-     * @return the output stream

-     */

-    private OutputStream getStream()

-    {

-        return m_streams.get();

-    }

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Data written to this stream is forwarded to a stream that has been associated
+ * with this thread.
+ *
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $
+ */
+public class DemuxOutputStream
+    extends OutputStream
+{
+    private InheritableThreadLocal<OutputStream> m_streams = new InheritableThreadLocal<OutputStream>();
+
+    /**
+     * Bind the specified stream to the current thread.
+     *
+     * @param output the stream to bind
+     * @return the OutputStream that was previously active
+     */
+    public OutputStream bindStream( OutputStream output )
+    {
+        OutputStream stream = getStream();
+        m_streams.set( output );
+        return stream;
+    }
+
+    /**
+     * Closes stream associated with current thread.
+     *
+     * @throws IOException if an error occurs
+     */
+    @Override
+    public void close()
+        throws IOException
+    {
+        OutputStream output = getStream();
+        if( null != output )
+        {
+            output.close();
+        }
+    }
+
+    /**
+     * Flushes stream associated with current thread.
+     *
+     * @throws IOException if an error occurs
+     */
+    @Override
+    public void flush()
+        throws IOException
+    {
+        OutputStream output = getStream();
+        if( null != output )
+        {
+            output.flush();
+        }
+    }
+
+    /**
+     * Writes byte to stream associated with current thread.
+     *
+     * @param ch the byte to write to stream
+     * @throws IOException if an error occurs
+     */
+    @Override
+    public void write( int ch )
+        throws IOException
+    {
+        OutputStream output = getStream();
+        if( null != output )
+        {
+            output.write( ch );
+        }
+    }
+
+    /**
+     * Utility method to retrieve stream bound to current thread (if any).
+     *
+     * @return the output stream
+     */
+    private OutputStream getStream()
+    {
+        return m_streams.get();
+    }
+}
diff --git a/src/org/apache/commons/io/output/FileWriterWithEncoding.java b/src/org/apache/commons/io/output/FileWriterWithEncoding.java
index a8f8933..d5708ad 100644
--- a/src/org/apache/commons/io/output/FileWriterWithEncoding.java
+++ b/src/org/apache/commons/io/output/FileWriterWithEncoding.java
@@ -1,324 +1,324 @@
-/*

- * 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.commons.io.output;

-

-import java.io.File;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.io.Writer;

-import java.nio.charset.Charset;

-import java.nio.charset.CharsetEncoder;

-

-import org.apache.commons.io.FileUtils;

-import org.apache.commons.io.IOUtils;

-

-/**

- * Writer of files that allows the encoding to be set.

- * <p>

- * This class provides a simple alternative to <code>FileWriter</code>

- * that allows an encoding to be set. Unfortunately, it cannot subclass

- * <code>FileWriter</code>.

- * <p>

- * By default, the file will be overwritten, but this may be changed to append.

- * <p>

- * The encoding must be specified using either the name of the {@link Charset},

- * the {@link Charset}, or a {@link CharsetEncoder}. If the default encoding

- * is required then use the {@link java.io.FileWriter} directly, rather than

- * this implementation.

- * <p>

- * 

- *

- * @since Commons IO 1.4

- * @version $Id: FileWriterWithEncoding.java 611634 2008-01-13 20:35:00Z niallp $

- */

-public class FileWriterWithEncoding extends Writer {

-    // Cannot extend ProxyWriter, as requires writer to be

-    // known when super() is called

-

-    /** The writer to decorate. */

-    private final Writer out;

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, String encoding) throws IOException {

-        this(new File(filename), encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, String encoding, boolean append) throws IOException {

-        this(new File(filename), encoding, append);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, Charset encoding) throws IOException {

-        this(new File(filename), encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, Charset encoding, boolean append) throws IOException {

-        this(new File(filename), encoding, append);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, CharsetEncoder encoding) throws IOException {

-        this(new File(filename), encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param filename  the name of the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file name or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(String filename, CharsetEncoder encoding, boolean append) throws IOException {

-        this(new File(filename), encoding, append);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, String encoding) throws IOException {

-        this(file, encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, String encoding, boolean append) throws IOException {

-        super();

-        this.out = initWriter(file, encoding, append);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, Charset encoding) throws IOException {

-        this(file, encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, Charset encoding, boolean append) throws IOException {

-        super();

-        this.out = initWriter(file, encoding, append);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, CharsetEncoder encoding) throws IOException {

-        this(file, encoding, false);

-    }

-

-    /**

-     * Constructs a FileWriterWithEncoding with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException in case of an I/O error

-     */

-    public FileWriterWithEncoding(File file, CharsetEncoder encoding, boolean append) throws IOException {

-        super();

-        this.out = initWriter(file, encoding, append);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Initialise the wrapped file writer.

-     * Ensure that a cleanup occurs if the writer creation fails.

-     *

-     * @param file  the file to be accessed

-     * @param encoding  the encoding to use - may be Charset, CharsetEncoder or String

-     * @param append  true to append

-     * @return the initialised writer

-     * @throws NullPointerException if the file or encoding is null

-     * @throws IOException if an error occurs

-     */

-     private static Writer initWriter(File file, Object encoding, boolean append) throws IOException {

-        if (file == null) {

-            throw new NullPointerException("File is missing");

-        }

-        if (encoding == null) {

-            throw new NullPointerException("Encoding is missing");

-        }

-        boolean fileExistedAlready = file.exists();

-        OutputStream stream = null;

-        Writer writer = null;

-        try {

-            stream = new FileOutputStream(file, append);

-            if (encoding instanceof Charset) {

-                writer = new OutputStreamWriter(stream, (Charset)encoding);

-            } else if (encoding instanceof CharsetEncoder) {

-                writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);

-            } else {

-                writer = new OutputStreamWriter(stream, (String)encoding);

-            }

-        } catch (IOException ex) {

-            IOUtils.closeQuietly(writer);

-            IOUtils.closeQuietly(stream);

-            if (fileExistedAlready == false) {

-                FileUtils.deleteQuietly(file);

-            }

-            throw ex;

-        } catch (RuntimeException ex) {

-            IOUtils.closeQuietly(writer);

-            IOUtils.closeQuietly(stream);

-            if (fileExistedAlready == false) {

-                FileUtils.deleteQuietly(file);

-            }

-            throw ex;

-        }

-        return writer;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Write a character.

-     * @param idx the character to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(int idx) throws IOException {

-        out.write(idx);

-    }

-

-    /**

-     * Write the characters from an array.

-     * @param chr the characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr) throws IOException {

-        out.write(chr);

-    }

-

-    /**

-     * Write the specified characters from an array.

-     * @param chr the characters to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr, int st, int end) throws IOException {

-        out.write(chr, st, end);

-    }

-

-    /**

-     * Write the characters from a string.

-     * @param str the string to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str) throws IOException {

-        out.write(str);

-    }

-

-    /**

-     * Write the specified characters from a string.

-     * @param str the string to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str, int st, int end) throws IOException {

-        out.write(str, st, end);

-    }

-

-    /**

-     * Flush the stream.

-     * @throws IOException if an I/O error occurs

-     */

-    public void flush() throws IOException {

-        out.flush();

-    }

-

-    /**

-     * Close the stream.

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        out.close();

-    }

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Writer of files that allows the encoding to be set.
+ * <p>
+ * This class provides a simple alternative to <code>FileWriter</code>
+ * that allows an encoding to be set. Unfortunately, it cannot subclass
+ * <code>FileWriter</code>.
+ * <p>
+ * By default, the file will be overwritten, but this may be changed to append.
+ * <p>
+ * The encoding must be specified using either the name of the {@link Charset},
+ * the {@link Charset}, or a {@link CharsetEncoder}. If the default encoding
+ * is required then use the {@link java.io.FileWriter} directly, rather than
+ * this implementation.
+ * <p>
+ * 
+ *
+ * @since Commons IO 1.4
+ * @version $Id: FileWriterWithEncoding.java 611634 2008-01-13 20:35:00Z niallp $
+ */
+public class FileWriterWithEncoding extends Writer {
+    // Cannot extend ProxyWriter, as requires writer to be
+    // known when super() is called
+
+    /** The writer to decorate. */
+    private final Writer out;
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, String encoding) throws IOException {
+        this(new File(filename), encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, String encoding, boolean append) throws IOException {
+        this(new File(filename), encoding, append);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, Charset encoding) throws IOException {
+        this(new File(filename), encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, Charset encoding, boolean append) throws IOException {
+        this(new File(filename), encoding, append);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, CharsetEncoder encoding) throws IOException {
+        this(new File(filename), encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param filename  the name of the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file name or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(String filename, CharsetEncoder encoding, boolean append) throws IOException {
+        this(new File(filename), encoding, append);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, String encoding) throws IOException {
+        this(file, encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, String encoding, boolean append) throws IOException {
+        super();
+        this.out = initWriter(file, encoding, append);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, Charset encoding) throws IOException {
+        this(file, encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, Charset encoding, boolean append) throws IOException {
+        super();
+        this.out = initWriter(file, encoding, append);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, CharsetEncoder encoding) throws IOException {
+        this(file, encoding, false);
+    }
+
+    /**
+     * Constructs a FileWriterWithEncoding with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException in case of an I/O error
+     */
+    public FileWriterWithEncoding(File file, CharsetEncoder encoding, boolean append) throws IOException {
+        super();
+        this.out = initWriter(file, encoding, append);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Initialise the wrapped file writer.
+     * Ensure that a cleanup occurs if the writer creation fails.
+     *
+     * @param file  the file to be accessed
+     * @param encoding  the encoding to use - may be Charset, CharsetEncoder or String
+     * @param append  true to append
+     * @return the initialised writer
+     * @throws NullPointerException if the file or encoding is null
+     * @throws IOException if an error occurs
+     */
+     private static Writer initWriter(File file, Object encoding, boolean append) throws IOException {
+        if (file == null) {
+            throw new NullPointerException("File is missing");
+        }
+        if (encoding == null) {
+            throw new NullPointerException("Encoding is missing");
+        }
+        boolean fileExistedAlready = file.exists();
+        OutputStream stream = null;
+        Writer writer = null;
+        try {
+            stream = new FileOutputStream(file, append);
+            if (encoding instanceof Charset) {
+                writer = new OutputStreamWriter(stream, (Charset)encoding);
+            } else if (encoding instanceof CharsetEncoder) {
+                writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);
+            } else {
+                writer = new OutputStreamWriter(stream, (String)encoding);
+            }
+        } catch (IOException ex) {
+            IOUtils.closeQuietly(writer);
+            IOUtils.closeQuietly(stream);
+            if (fileExistedAlready == false) {
+                FileUtils.deleteQuietly(file);
+            }
+            throw ex;
+        } catch (RuntimeException ex) {
+            IOUtils.closeQuietly(writer);
+            IOUtils.closeQuietly(stream);
+            if (fileExistedAlready == false) {
+                FileUtils.deleteQuietly(file);
+            }
+            throw ex;
+        }
+        return writer;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Write a character.
+     * @param idx the character to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(int idx) throws IOException {
+        out.write(idx);
+    }
+
+    /**
+     * Write the characters from an array.
+     * @param chr the characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr) throws IOException {
+        out.write(chr);
+    }
+
+    /**
+     * Write the specified characters from an array.
+     * @param chr the characters to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr, int st, int end) throws IOException {
+        out.write(chr, st, end);
+    }
+
+    /**
+     * Write the characters from a string.
+     * @param str the string to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str) throws IOException {
+        out.write(str);
+    }
+
+    /**
+     * Write the specified characters from a string.
+     * @param str the string to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str, int st, int end) throws IOException {
+        out.write(str, st, end);
+    }
+
+    /**
+     * Flush the stream.
+     * @throws IOException if an I/O error occurs
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Close the stream.
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+}
diff --git a/src/org/apache/commons/io/output/LockableFileWriter.java b/src/org/apache/commons/io/output/LockableFileWriter.java
index 6b10bd2..1092926 100644
--- a/src/org/apache/commons/io/output/LockableFileWriter.java
+++ b/src/org/apache/commons/io/output/LockableFileWriter.java
@@ -1,333 +1,333 @@
-/*

- * 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.commons.io.output;

-

-import java.io.File;

-import java.io.FileOutputStream;

-import java.io.FileWriter;

-import java.io.IOException;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.io.Writer;

-

-import org.apache.commons.io.FileUtils;

-import org.apache.commons.io.IOUtils;

-

-/**

- * FileWriter that will create and honor lock files to allow simple

- * cross thread file lock handling.

- * <p>

- * This class provides a simple alternative to <code>FileWriter</code>

- * that will use a lock file to prevent duplicate writes.

- * <p>

- * By default, the file will be overwritten, but this may be changed to append.

- * The lock directory may be specified, but defaults to the system property

- * <code>java.io.tmpdir</code>.

- * The encoding may also be specified, and defaults to the platform default.

- *

- * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>

- * @author <a href="mailto:ms@collab.net">Michael Salmon</a>

- * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>

- * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>

- * @author Stephen Colebourne

- * @author Andy Lehane

- * @version $Id: LockableFileWriter.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class LockableFileWriter extends Writer {

-    // Cannot extend ProxyWriter, as requires writer to be

-    // known when super() is called

-

-    /** The extension for the lock file. */

-    private static final String LCK = ".lck";

-

-    /** The writer to decorate. */

-    private final Writer out;

-    /** The lock file. */

-    private final File lockFile;

-

-    /**

-     * Constructs a LockableFileWriter.

-     * If the file exists, it is overwritten.

-     *

-     * @param fileName  the file to write to, not null

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(String fileName) throws IOException {

-        this(fileName, false, null);

-    }

-

-    /**

-     * Constructs a LockableFileWriter.

-     *

-     * @param fileName  file to write to, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(String fileName, boolean append) throws IOException {

-        this(fileName, append, null);

-    }

-

-    /**

-     * Constructs a LockableFileWriter.

-     *

-     * @param fileName  the file to write to, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @param lockDir  the directory in which the lock file should be held

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(String fileName, boolean append, String lockDir) throws IOException {

-        this(new File(fileName), append, lockDir);

-    }

-

-    /**

-     * Constructs a LockableFileWriter.

-     * If the file exists, it is overwritten.

-     *

-     * @param file  the file to write to, not null

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(File file) throws IOException {

-        this(file, false, null);

-    }

-

-    /**

-     * Constructs a LockableFileWriter.

-     *

-     * @param file  the file to write to, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(File file, boolean append) throws IOException {

-        this(file, append, null);

-    }

-

-    /**

-     * Constructs a LockableFileWriter.

-     *

-     * @param file  the file to write to, not null

-     * @param append  true if content should be appended, false to overwrite

-     * @param lockDir  the directory in which the lock file should be held

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(File file, boolean append, String lockDir) throws IOException {

-        this(file, null, append, lockDir);

-    }

-

-    /**

-     * Constructs a LockableFileWriter with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, null means platform default

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(File file, String encoding) throws IOException {

-        this(file, encoding, false, null);

-    }

-

-    /**

-     * Constructs a LockableFileWriter with a file encoding.

-     *

-     * @param file  the file to write to, not null

-     * @param encoding  the encoding to use, null means platform default

-     * @param append  true if content should be appended, false to overwrite

-     * @param lockDir  the directory in which the lock file should be held

-     * @throws NullPointerException if the file is null

-     * @throws IOException in case of an I/O error

-     */

-    public LockableFileWriter(File file, String encoding, boolean append,

-            String lockDir) throws IOException {

-        super();

-        // init file to create/append

-        file = file.getAbsoluteFile();

-        if (file.getParentFile() != null) {

-            FileUtils.forceMkdir(file.getParentFile());

-        }

-        if (file.isDirectory()) {

-            throw new IOException("File specified is a directory");

-        }

-        

-        // init lock file

-        if (lockDir == null) {

-            lockDir = System.getProperty("java.io.tmpdir");

-        }

-        File lockDirFile = new File(lockDir);

-        FileUtils.forceMkdir(lockDirFile);

-        testLockDir(lockDirFile);

-        lockFile = new File(lockDirFile, file.getName() + LCK);

-        

-        // check if locked

-        createLock();

-        

-        // init wrapped writer

-        out = initWriter(file, encoding, append);

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Tests that we can write to the lock directory.

-     *

-     * @param lockDir  the File representing the lock directory

-     * @throws IOException if we cannot write to the lock directory

-     * @throws IOException if we cannot find the lock file

-     */

-    private void testLockDir(File lockDir) throws IOException {

-        if (!lockDir.exists()) {

-            throw new IOException(

-                    "Could not find lockDir: " + lockDir.getAbsolutePath());

-        }

-        if (!lockDir.canWrite()) {

-            throw new IOException(

-                    "Could not write to lockDir: " + lockDir.getAbsolutePath());

-        }

-    }

-

-    /**

-     * Creates the lock file.

-     *

-     * @throws IOException if we cannot create the file

-     */

-    private void createLock() throws IOException {

-        synchronized (LockableFileWriter.class) {

-            if (!lockFile.createNewFile()) {

-                throw new IOException("Can't write file, lock " +

-                        lockFile.getAbsolutePath() + " exists");

-            }

-            lockFile.deleteOnExit();

-        }

-    }

-

-    /**

-     * Initialise the wrapped file writer.

-     * Ensure that a cleanup occurs if the writer creation fails.

-     *

-     * @param file  the file to be accessed

-     * @param encoding  the encoding to use

-     * @param append  true to append

-     * @return The initialised writer

-     * @throws IOException if an error occurs

-     */

-    private Writer initWriter(File file, String encoding, boolean append) throws IOException {

-        boolean fileExistedAlready = file.exists();

-        OutputStream stream = null;

-        Writer writer = null;

-        try {

-            if (encoding == null) {

-                writer = new FileWriter(file.getAbsolutePath(), append);

-            } else {

-                stream = new FileOutputStream(file.getAbsolutePath(), append);

-                writer = new OutputStreamWriter(stream, encoding);

-            }

-        } catch (IOException ex) {

-            IOUtils.closeQuietly(writer);

-            IOUtils.closeQuietly(stream);

-            lockFile.delete();

-            if (fileExistedAlready == false) {

-                file.delete();

-            }

-            throw ex;

-        } catch (RuntimeException ex) {

-            IOUtils.closeQuietly(writer);

-            IOUtils.closeQuietly(stream);

-            lockFile.delete();

-            if (fileExistedAlready == false) {

-                file.delete();

-            }

-            throw ex;

-        }

-        return writer;

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Closes the file writer.

-     *

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        try {

-            out.close();

-        } finally {

-            lockFile.delete();

-        }

-    }

-

-    //-----------------------------------------------------------------------

-    /**

-     * Write a character.

-     * @param idx the character to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(int idx) throws IOException {

-        out.write(idx);

-    }

-

-    /**

-     * Write the characters from an array.

-     * @param chr the characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr) throws IOException {

-        out.write(chr);

-    }

-

-    /**

-     * Write the specified characters from an array.

-     * @param chr the characters to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr, int st, int end) throws IOException {

-        out.write(chr, st, end);

-    }

-

-    /**

-     * Write the characters from a string.

-     * @param str the string to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str) throws IOException {

-        out.write(str);

-    }

-

-    /**

-     * Write the specified characters from a string.

-     * @param str the string to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str, int st, int end) throws IOException {

-        out.write(str, st, end);

-    }

-

-    /**

-     * Flush the stream.

-     * @throws IOException if an I/O error occurs

-     */

-    public void flush() throws IOException {

-        out.flush();

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * FileWriter that will create and honor lock files to allow simple
+ * cross thread file lock handling.
+ * <p>
+ * This class provides a simple alternative to <code>FileWriter</code>
+ * that will use a lock file to prevent duplicate writes.
+ * <p>
+ * By default, the file will be overwritten, but this may be changed to append.
+ * The lock directory may be specified, but defaults to the system property
+ * <code>java.io.tmpdir</code>.
+ * The encoding may also be specified, and defaults to the platform default.
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author <a href="mailto:ms@collab.net">Michael Salmon</a>
+ * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author Stephen Colebourne
+ * @author Andy Lehane
+ * @version $Id: LockableFileWriter.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class LockableFileWriter extends Writer {
+    // Cannot extend ProxyWriter, as requires writer to be
+    // known when super() is called
+
+    /** The extension for the lock file. */
+    private static final String LCK = ".lck";
+
+    /** The writer to decorate. */
+    private final Writer out;
+    /** The lock file. */
+    private final File lockFile;
+
+    /**
+     * Constructs a LockableFileWriter.
+     * If the file exists, it is overwritten.
+     *
+     * @param fileName  the file to write to, not null
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(String fileName) throws IOException {
+        this(fileName, false, null);
+    }
+
+    /**
+     * Constructs a LockableFileWriter.
+     *
+     * @param fileName  file to write to, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(String fileName, boolean append) throws IOException {
+        this(fileName, append, null);
+    }
+
+    /**
+     * Constructs a LockableFileWriter.
+     *
+     * @param fileName  the file to write to, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @param lockDir  the directory in which the lock file should be held
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(String fileName, boolean append, String lockDir) throws IOException {
+        this(new File(fileName), append, lockDir);
+    }
+
+    /**
+     * Constructs a LockableFileWriter.
+     * If the file exists, it is overwritten.
+     *
+     * @param file  the file to write to, not null
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(File file) throws IOException {
+        this(file, false, null);
+    }
+
+    /**
+     * Constructs a LockableFileWriter.
+     *
+     * @param file  the file to write to, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(File file, boolean append) throws IOException {
+        this(file, append, null);
+    }
+
+    /**
+     * Constructs a LockableFileWriter.
+     *
+     * @param file  the file to write to, not null
+     * @param append  true if content should be appended, false to overwrite
+     * @param lockDir  the directory in which the lock file should be held
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(File file, boolean append, String lockDir) throws IOException {
+        this(file, null, append, lockDir);
+    }
+
+    /**
+     * Constructs a LockableFileWriter with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, null means platform default
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(File file, String encoding) throws IOException {
+        this(file, encoding, false, null);
+    }
+
+    /**
+     * Constructs a LockableFileWriter with a file encoding.
+     *
+     * @param file  the file to write to, not null
+     * @param encoding  the encoding to use, null means platform default
+     * @param append  true if content should be appended, false to overwrite
+     * @param lockDir  the directory in which the lock file should be held
+     * @throws NullPointerException if the file is null
+     * @throws IOException in case of an I/O error
+     */
+    public LockableFileWriter(File file, String encoding, boolean append,
+            String lockDir) throws IOException {
+        super();
+        // init file to create/append
+        file = file.getAbsoluteFile();
+        if (file.getParentFile() != null) {
+            FileUtils.forceMkdir(file.getParentFile());
+        }
+        if (file.isDirectory()) {
+            throw new IOException("File specified is a directory");
+        }
+        
+        // init lock file
+        if (lockDir == null) {
+            lockDir = System.getProperty("java.io.tmpdir");
+        }
+        File lockDirFile = new File(lockDir);
+        FileUtils.forceMkdir(lockDirFile);
+        testLockDir(lockDirFile);
+        lockFile = new File(lockDirFile, file.getName() + LCK);
+        
+        // check if locked
+        createLock();
+        
+        // init wrapped writer
+        out = initWriter(file, encoding, append);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests that we can write to the lock directory.
+     *
+     * @param lockDir  the File representing the lock directory
+     * @throws IOException if we cannot write to the lock directory
+     * @throws IOException if we cannot find the lock file
+     */
+    private void testLockDir(File lockDir) throws IOException {
+        if (!lockDir.exists()) {
+            throw new IOException(
+                    "Could not find lockDir: " + lockDir.getAbsolutePath());
+        }
+        if (!lockDir.canWrite()) {
+            throw new IOException(
+                    "Could not write to lockDir: " + lockDir.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Creates the lock file.
+     *
+     * @throws IOException if we cannot create the file
+     */
+    private void createLock() throws IOException {
+        synchronized (LockableFileWriter.class) {
+            if (!lockFile.createNewFile()) {
+                throw new IOException("Can't write file, lock " +
+                        lockFile.getAbsolutePath() + " exists");
+            }
+            lockFile.deleteOnExit();
+        }
+    }
+
+    /**
+     * Initialise the wrapped file writer.
+     * Ensure that a cleanup occurs if the writer creation fails.
+     *
+     * @param file  the file to be accessed
+     * @param encoding  the encoding to use
+     * @param append  true to append
+     * @return The initialised writer
+     * @throws IOException if an error occurs
+     */
+    private Writer initWriter(File file, String encoding, boolean append) throws IOException {
+        boolean fileExistedAlready = file.exists();
+        OutputStream stream = null;
+        Writer writer = null;
+        try {
+            if (encoding == null) {
+                writer = new FileWriter(file.getAbsolutePath(), append);
+            } else {
+                stream = new FileOutputStream(file.getAbsolutePath(), append);
+                writer = new OutputStreamWriter(stream, encoding);
+            }
+        } catch (IOException ex) {
+            IOUtils.closeQuietly(writer);
+            IOUtils.closeQuietly(stream);
+            lockFile.delete();
+            if (fileExistedAlready == false) {
+                file.delete();
+            }
+            throw ex;
+        } catch (RuntimeException ex) {
+            IOUtils.closeQuietly(writer);
+            IOUtils.closeQuietly(stream);
+            lockFile.delete();
+            if (fileExistedAlready == false) {
+                file.delete();
+            }
+            throw ex;
+        }
+        return writer;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Closes the file writer.
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        try {
+            out.close();
+        } finally {
+            lockFile.delete();
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Write a character.
+     * @param idx the character to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(int idx) throws IOException {
+        out.write(idx);
+    }
+
+    /**
+     * Write the characters from an array.
+     * @param chr the characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr) throws IOException {
+        out.write(chr);
+    }
+
+    /**
+     * Write the specified characters from an array.
+     * @param chr the characters to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr, int st, int end) throws IOException {
+        out.write(chr, st, end);
+    }
+
+    /**
+     * Write the characters from a string.
+     * @param str the string to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str) throws IOException {
+        out.write(str);
+    }
+
+    /**
+     * Write the specified characters from a string.
+     * @param str the string to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str, int st, int end) throws IOException {
+        out.write(str, st, end);
+    }
+
+    /**
+     * Flush the stream.
+     * @throws IOException if an I/O error occurs
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/NullOutputStream.java b/src/org/apache/commons/io/output/NullOutputStream.java
index 7e3cdaf..5558d30 100644
--- a/src/org/apache/commons/io/output/NullOutputStream.java
+++ b/src/org/apache/commons/io/output/NullOutputStream.java
@@ -1,65 +1,65 @@
-/*

- * 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.commons.io.output;

- 

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * This OutputStream writes all data to the famous <b>/dev/null</b>.

- * <p>

- * This output stream has no destination (file/socket etc.) and all

- * bytes written to it are ignored and lost.

- * 

- * @author Jeremias Maerki

- * @version $Id: NullOutputStream.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class NullOutputStream extends OutputStream {

-    

-    /**

-     * A singleton.

-     */

-    public static final NullOutputStream NULL_OUTPUT_STREAM = new NullOutputStream();

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param b The bytes to write

-     * @param off The start offset

-     * @param len The number of bytes to write

-     */

-    public void write(byte[] b, int off, int len) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param b The byte to write

-     */

-    public void write(int b) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param b The bytes to write

-     * @throws IOException never

-     */

-    public void write(byte[] b) throws IOException {

-        //to /dev/null

-    }

-

-}

+/*
+ * 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.commons.io.output;
+ 
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This OutputStream writes all data to the famous <b>/dev/null</b>.
+ * <p>
+ * This output stream has no destination (file/socket etc.) and all
+ * bytes written to it are ignored and lost.
+ * 
+ * @author Jeremias Maerki
+ * @version $Id: NullOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class NullOutputStream extends OutputStream {
+    
+    /**
+     * A singleton.
+     */
+    public static final NullOutputStream NULL_OUTPUT_STREAM = new NullOutputStream();
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param b The bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     */
+    public void write(byte[] b, int off, int len) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param b The byte to write
+     */
+    public void write(int b) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param b The bytes to write
+     * @throws IOException never
+     */
+    public void write(byte[] b) throws IOException {
+        //to /dev/null
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/NullWriter.java b/src/org/apache/commons/io/output/NullWriter.java
index aed52ab..8fd2bf4 100644
--- a/src/org/apache/commons/io/output/NullWriter.java
+++ b/src/org/apache/commons/io/output/NullWriter.java
@@ -1,96 +1,96 @@
-/*

- * 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.commons.io.output;

-

-import java.io.Writer;

-

-/**

- * This {@link Writer} writes all data to the famous <b>/dev/null</b>.

- * <p>

- * This <code>Writer</code> has no destination (file/socket etc.) and all

- * characters written to it are ignored and lost.

- * 

- * @version $Id: NullWriter.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class NullWriter extends Writer {

-    

-    /**

-     * A singleton.

-     */

-    public static final NullWriter NULL_WRITER = new NullWriter();

-

-    /**

-     * Constructs a new NullWriter.

-     */

-    public NullWriter() {

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param idx The character to write

-     */

-    public void write(int idx) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param chr The characters to write

-     */

-    public void write(char[] chr) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param chr The characters to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     */

-    public void write(char[] chr, int st, int end) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param str The string to write

-     */

-    public void write(String str) {

-        //to /dev/null

-    }

-

-    /**

-     * Does nothing - output to <code>/dev/null</code>.

-     * @param str The string to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     */

-    public void write(String str, int st, int end) {

-        //to /dev/null

-    }

-

-    /** @see java.io.Writer#flush() */

-    public void flush() {

-        //to /dev/null

-    }

-

-    /** @see java.io.Writer#close() */

-    public void close() {

-        //to /dev/null

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.Writer;
+
+/**
+ * This {@link Writer} writes all data to the famous <b>/dev/null</b>.
+ * <p>
+ * This <code>Writer</code> has no destination (file/socket etc.) and all
+ * characters written to it are ignored and lost.
+ * 
+ * @version $Id: NullWriter.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class NullWriter extends Writer {
+    
+    /**
+     * A singleton.
+     */
+    public static final NullWriter NULL_WRITER = new NullWriter();
+
+    /**
+     * Constructs a new NullWriter.
+     */
+    public NullWriter() {
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param idx The character to write
+     */
+    public void write(int idx) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param chr The characters to write
+     */
+    public void write(char[] chr) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param chr The characters to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     */
+    public void write(char[] chr, int st, int end) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param str The string to write
+     */
+    public void write(String str) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to <code>/dev/null</code>.
+     * @param str The string to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     */
+    public void write(String str, int st, int end) {
+        //to /dev/null
+    }
+
+    /** @see java.io.Writer#flush() */
+    public void flush() {
+        //to /dev/null
+    }
+
+    /** @see java.io.Writer#close() */
+    public void close() {
+        //to /dev/null
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/ProxyOutputStream.java b/src/org/apache/commons/io/output/ProxyOutputStream.java
index b63d723..9376d6c 100644
--- a/src/org/apache/commons/io/output/ProxyOutputStream.java
+++ b/src/org/apache/commons/io/output/ProxyOutputStream.java
@@ -1,89 +1,89 @@
-/*

- * 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.commons.io.output;

-

-import java.io.FilterOutputStream;

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * A Proxy stream which acts as expected, that is it passes the method 

- * calls on to the proxied stream and doesn't change which methods are 

- * being called. It is an alternative base class to FilterOutputStream

- * to increase reusability.

- * 

- * @author Stephen Colebourne

- * @version $Id: ProxyOutputStream.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class ProxyOutputStream extends FilterOutputStream {

-

-    /**

-     * Constructs a new ProxyOutputStream.

-     * 

-     * @param proxy  the OutputStream to delegate to

-     */

-    public ProxyOutputStream(OutputStream proxy) {

-        super(proxy);

-        // the proxy is stored in a protected superclass variable named 'out'

-    }

-

-    /**

-     * Invokes the delegate's <code>write(int)</code> method.

-     * @param idx the byte to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(int idx) throws IOException {

-        out.write(idx);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(byte[])</code> method.

-     * @param bts the bytes to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(byte[] bts) throws IOException {

-        out.write(bts);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(byte[])</code> method.

-     * @param bts the bytes to write

-     * @param st The start offset

-     * @param end The number of bytes to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(byte[] bts, int st, int end) throws IOException {

-        out.write(bts, st, end);

-    }

-

-    /**

-     * Invokes the delegate's <code>flush()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void flush() throws IOException {

-        out.flush();

-    }

-

-    /**

-     * Invokes the delegate's <code>close()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        out.close();

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A Proxy stream which acts as expected, that is it passes the method 
+ * calls on to the proxied stream and doesn't change which methods are 
+ * being called. It is an alternative base class to FilterOutputStream
+ * to increase reusability.
+ * 
+ * @author Stephen Colebourne
+ * @version $Id: ProxyOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class ProxyOutputStream extends FilterOutputStream {
+
+    /**
+     * Constructs a new ProxyOutputStream.
+     * 
+     * @param proxy  the OutputStream to delegate to
+     */
+    public ProxyOutputStream(OutputStream proxy) {
+        super(proxy);
+        // the proxy is stored in a protected superclass variable named 'out'
+    }
+
+    /**
+     * Invokes the delegate's <code>write(int)</code> method.
+     * @param idx the byte to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(int idx) throws IOException {
+        out.write(idx);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(byte[])</code> method.
+     * @param bts the bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(byte[] bts) throws IOException {
+        out.write(bts);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(byte[])</code> method.
+     * @param bts the bytes to write
+     * @param st The start offset
+     * @param end The number of bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(byte[] bts, int st, int end) throws IOException {
+        out.write(bts, st, end);
+    }
+
+    /**
+     * Invokes the delegate's <code>flush()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Invokes the delegate's <code>close()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/ProxyWriter.java b/src/org/apache/commons/io/output/ProxyWriter.java
index fbec628..8b617d8 100644
--- a/src/org/apache/commons/io/output/ProxyWriter.java
+++ b/src/org/apache/commons/io/output/ProxyWriter.java
@@ -1,111 +1,111 @@
-/*

- * 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.commons.io.output;

-

-import java.io.FilterWriter;

-import java.io.IOException;

-import java.io.Writer;

-

-/**

- * A Proxy stream which acts as expected, that is it passes the method 

- * calls on to the proxied stream and doesn't change which methods are 

- * being called. It is an alternative base class to FilterWriter

- * to increase reusability, because FilterWriter changes the 

- * methods being called, such as write(char[]) to write(char[], int, int)

- * and write(String) to write(String, int, int).

- * 

- * @author Stephen Colebourne

- * @version $Id: ProxyWriter.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class ProxyWriter extends FilterWriter {

-

-    /**

-     * Constructs a new ProxyWriter.

-     * 

-     * @param proxy  the Writer to delegate to

-     */

-    public ProxyWriter(Writer proxy) {

-        super(proxy);

-        // the proxy is stored in a protected superclass variable named 'out'

-    }

-

-    /**

-     * Invokes the delegate's <code>write(int)</code> method.

-     * @param idx the character to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(int idx) throws IOException {

-        out.write(idx);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(char[])</code> method.

-     * @param chr the characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr) throws IOException {

-        out.write(chr);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(char[], int, int)</code> method.

-     * @param chr the characters to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(char[] chr, int st, int end) throws IOException {

-        out.write(chr, st, end);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(String)</code> method.

-     * @param str the string to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str) throws IOException {

-        out.write(str);

-    }

-

-    /**

-     * Invokes the delegate's <code>write(String)</code> method.

-     * @param str the string to write

-     * @param st The start offset

-     * @param end The number of characters to write

-     * @throws IOException if an I/O error occurs

-     */

-    public void write(String str, int st, int end) throws IOException {

-        out.write(str, st, end);

-    }

-

-    /**

-     * Invokes the delegate's <code>flush()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void flush() throws IOException {

-        out.flush();

-    }

-

-    /**

-     * Invokes the delegate's <code>close()</code> method.

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        out.close();

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * A Proxy stream which acts as expected, that is it passes the method 
+ * calls on to the proxied stream and doesn't change which methods are 
+ * being called. It is an alternative base class to FilterWriter
+ * to increase reusability, because FilterWriter changes the 
+ * methods being called, such as write(char[]) to write(char[], int, int)
+ * and write(String) to write(String, int, int).
+ * 
+ * @author Stephen Colebourne
+ * @version $Id: ProxyWriter.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class ProxyWriter extends FilterWriter {
+
+    /**
+     * Constructs a new ProxyWriter.
+     * 
+     * @param proxy  the Writer to delegate to
+     */
+    public ProxyWriter(Writer proxy) {
+        super(proxy);
+        // the proxy is stored in a protected superclass variable named 'out'
+    }
+
+    /**
+     * Invokes the delegate's <code>write(int)</code> method.
+     * @param idx the character to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(int idx) throws IOException {
+        out.write(idx);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(char[])</code> method.
+     * @param chr the characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr) throws IOException {
+        out.write(chr);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(char[], int, int)</code> method.
+     * @param chr the characters to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(char[] chr, int st, int end) throws IOException {
+        out.write(chr, st, end);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(String)</code> method.
+     * @param str the string to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str) throws IOException {
+        out.write(str);
+    }
+
+    /**
+     * Invokes the delegate's <code>write(String)</code> method.
+     * @param str the string to write
+     * @param st The start offset
+     * @param end The number of characters to write
+     * @throws IOException if an I/O error occurs
+     */
+    public void write(String str, int st, int end) throws IOException {
+        out.write(str, st, end);
+    }
+
+    /**
+     * Invokes the delegate's <code>flush()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Invokes the delegate's <code>close()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/TeeOutputStream.java b/src/org/apache/commons/io/output/TeeOutputStream.java
index ee957fb..6f799d1 100644
--- a/src/org/apache/commons/io/output/TeeOutputStream.java
+++ b/src/org/apache/commons/io/output/TeeOutputStream.java
@@ -1,94 +1,94 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * Classic splitter of OutputStream. Named after the unix 'tee' 

- * command. It allows a stream to be branched off so there 

- * are now two streams.

- *

- * @version $Id: TeeOutputStream.java 610010 2008-01-08 14:50:59Z niallp $

- */

-public class TeeOutputStream extends ProxyOutputStream {

-

-    /** the second OutputStream to write to */

-    protected OutputStream branch;

-

-    /**

-     * Constructs a TeeOutputStream.

-     * @param out the main OutputStream

-     * @param branch the second OutputStream

-     */

-    public TeeOutputStream( OutputStream out, OutputStream branch ) {

-        super(out);

-        this.branch = branch;

-    }

-

-    /**

-     * Write the bytes to both streams.

-     * @param b the bytes to write

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void write(byte[] b) throws IOException {

-        super.write(b);

-        this.branch.write(b);

-    }

-

-    /**

-     * Write the specified bytes to both streams.

-     * @param b the bytes to write

-     * @param off The start offset

-     * @param len The number of bytes to write

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void write(byte[] b, int off, int len) throws IOException {

-        super.write(b, off, len);

-        this.branch.write(b, off, len);

-    }

-

-    /**

-     * Write a byte to both streams.

-     * @param b the byte to write

-     * @throws IOException if an I/O error occurs

-     */

-    public synchronized void write(int b) throws IOException {

-        super.write(b);

-        this.branch.write(b);

-    }

-

-    /**

-     * Flushes both streams.

-     * @throws IOException if an I/O error occurs

-     */

-    public void flush() throws IOException {

-        super.flush();

-        this.branch.flush();

-    }

-

-    /**

-     * Closes both streams. 

-     * @throws IOException if an I/O error occurs

-     */

-    public void close() throws IOException {

-        super.close();

-        this.branch.close();

-    }

-

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Classic splitter of OutputStream. Named after the unix 'tee' 
+ * command. It allows a stream to be branched off so there 
+ * are now two streams.
+ *
+ * @version $Id: TeeOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class TeeOutputStream extends ProxyOutputStream {
+
+    /** the second OutputStream to write to */
+    protected OutputStream branch;
+
+    /**
+     * Constructs a TeeOutputStream.
+     * @param out the main OutputStream
+     * @param branch the second OutputStream
+     */
+    public TeeOutputStream( OutputStream out, OutputStream branch ) {
+        super(out);
+        this.branch = branch;
+    }
+
+    /**
+     * Write the bytes to both streams.
+     * @param b the bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void write(byte[] b) throws IOException {
+        super.write(b);
+        this.branch.write(b);
+    }
+
+    /**
+     * Write the specified bytes to both streams.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void write(byte[] b, int off, int len) throws IOException {
+        super.write(b, off, len);
+        this.branch.write(b, off, len);
+    }
+
+    /**
+     * Write a byte to both streams.
+     * @param b the byte to write
+     * @throws IOException if an I/O error occurs
+     */
+    public synchronized void write(int b) throws IOException {
+        super.write(b);
+        this.branch.write(b);
+    }
+
+    /**
+     * Flushes both streams.
+     * @throws IOException if an I/O error occurs
+     */
+    public void flush() throws IOException {
+        super.flush();
+        this.branch.flush();
+    }
+
+    /**
+     * Closes both streams. 
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        super.close();
+        this.branch.close();
+    }
+
+}
diff --git a/src/org/apache/commons/io/output/ThresholdingOutputStream.java b/src/org/apache/commons/io/output/ThresholdingOutputStream.java
index fa69a80..ca6ede2 100644
--- a/src/org/apache/commons/io/output/ThresholdingOutputStream.java
+++ b/src/org/apache/commons/io/output/ThresholdingOutputStream.java
@@ -1,257 +1,257 @@
-/*

- * 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.commons.io.output;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-

-/**

- * An output stream which triggers an event when a specified number of bytes of

- * data have been written to it. The event can be used, for example, to throw

- * an exception if a maximum has been reached, or to switch the underlying

- * stream type when the threshold is exceeded.

- * <p>

- * This class overrides all <code>OutputStream</code> methods. However, these

- * overrides ultimately call the corresponding methods in the underlying output

- * stream implementation.

- * <p>

- * NOTE: This implementation may trigger the event <em>before</em> the threshold

- * is actually reached, since it triggers when a pending write operation would

- * cause the threshold to be exceeded.

- *

- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>

- *

- * @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $

- */

-public abstract class ThresholdingOutputStream

-    extends OutputStream

-{

-

-    // ----------------------------------------------------------- Data members

-

-

-    /**

-     * The threshold at which the event will be triggered.

-     */

-    private int threshold;

-

-

-    /**

-     * The number of bytes written to the output stream.

-     */

-    private long written;

-

-

-    /**

-     * Whether or not the configured threshold has been exceeded.

-     */

-    private boolean thresholdExceeded;

-

-

-    // ----------------------------------------------------------- Constructors

-

-

-    /**

-     * Constructs an instance of this class which will trigger an event at the

-     * specified threshold.

-     *

-     * @param threshold The number of bytes at which to trigger an event.

-     */

-    public ThresholdingOutputStream(int threshold)

-    {

-        this.threshold = threshold;

-    }

-

-

-    // --------------------------------------------------- OutputStream methods

-

-

-    /**

-     * Writes the specified byte to this output stream.

-     *

-     * @param b The byte to be written.

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void write(int b) throws IOException

-    {

-        checkThreshold(1);

-        getStream().write(b);

-        written++;

-    }

-

-

-    /**

-     * Writes <code>b.length</code> bytes from the specified byte array to this

-     * output stream.

-     *

-     * @param b The array of bytes to be written.

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void write(byte b[]) throws IOException

-    {

-        checkThreshold(b.length);

-        getStream().write(b);

-        written += b.length;

-    }

-

-

-    /**

-     * Writes <code>len</code> bytes from the specified byte array starting at

-     * offset <code>off</code> to this output stream.

-     *

-     * @param b   The byte array from which the data will be written.

-     * @param off The start offset in the byte array.

-     * @param len The number of bytes to write.

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void write(byte b[], int off, int len) throws IOException

-    {

-        checkThreshold(len);

-        getStream().write(b, off, len);

-        written += len;

-    }

-

-

-    /**

-     * Flushes this output stream and forces any buffered output bytes to be

-     * written out.

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void flush() throws IOException

-    {

-        getStream().flush();

-    }

-

-

-    /**

-     * Closes this output stream and releases any system resources associated

-     * with this stream.

-     *

-     * @exception IOException if an error occurs.

-     */

-    public void close() throws IOException

-    {

-        try

-        {

-            flush();

-        }

-        catch (IOException ignored)

-        {

-            // ignore

-        }

-        getStream().close();

-    }

-

-

-    // --------------------------------------------------------- Public methods

-

-

-    /**

-     * Returns the threshold, in bytes, at which an event will be triggered.

-     *

-     * @return The threshold point, in bytes.

-     */

-    public int getThreshold()

-    {

-        return threshold;

-    }

-

-

-    /**

-     * Returns the number of bytes that have been written to this output stream.

-     *

-     * @return The number of bytes written.

-     */

-    public long getByteCount()

-    {

-        return written;

-    }

-

-

-    /**

-     * Determines whether or not the configured threshold has been exceeded for

-     * this output stream.

-     *

-     * @return <code>true</code> if the threshold has been reached;

-     *         <code>false</code> otherwise.

-     */

-    public boolean isThresholdExceeded()

-    {

-        return (written > threshold);

-    }

-

-

-    // ------------------------------------------------------ Protected methods

-

-

-    /**

-     * Checks to see if writing the specified number of bytes would cause the

-     * configured threshold to be exceeded. If so, triggers an event to allow

-     * a concrete implementation to take action on this.

-     *

-     * @param count The number of bytes about to be written to the underlying

-     *              output stream.

-     *

-     * @exception IOException if an error occurs.

-     */

-    protected void checkThreshold(int count) throws IOException

-    {

-        if (!thresholdExceeded && (written + count > threshold))

-        {

-            thresholdExceeded = true;

-            thresholdReached();

-        }

-    }

-

-    /**

-     * Resets the byteCount to zero.  You can call this from 

-     * {@link #thresholdReached()} if you want the event to be triggered again. 

-     */

-    protected void resetByteCount() 

-    {

-        this.thresholdExceeded = false;

-        this.written = 0;

-    }

-

-    // ------------------------------------------------------- Abstract methods

-

-

-    /**

-     * Returns the underlying output stream, to which the corresponding

-     * <code>OutputStream</code> methods in this class will ultimately delegate.

-     *

-     * @return The underlying output stream.

-     *

-     * @exception IOException if an error occurs.

-     */

-    protected abstract OutputStream getStream() throws IOException;

-

-

-    /**

-     * Indicates that the configured threshold has been reached, and that a

-     * subclass should take whatever action necessary on this event. This may

-     * include changing the underlying output stream.

-     *

-     * @exception IOException if an error occurs.

-     */

-    protected abstract void thresholdReached() throws IOException;

-}

+/*
+ * 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.commons.io.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * An output stream which triggers an event when a specified number of bytes of
+ * data have been written to it. The event can be used, for example, to throw
+ * an exception if a maximum has been reached, or to switch the underlying
+ * stream type when the threshold is exceeded.
+ * <p>
+ * This class overrides all <code>OutputStream</code> methods. However, these
+ * overrides ultimately call the corresponding methods in the underlying output
+ * stream implementation.
+ * <p>
+ * NOTE: This implementation may trigger the event <em>before</em> the threshold
+ * is actually reached, since it triggers when a pending write operation would
+ * cause the threshold to be exceeded.
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $
+ */
+public abstract class ThresholdingOutputStream
+    extends OutputStream
+{
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The threshold at which the event will be triggered.
+     */
+    private int threshold;
+
+
+    /**
+     * The number of bytes written to the output stream.
+     */
+    private long written;
+
+
+    /**
+     * Whether or not the configured threshold has been exceeded.
+     */
+    private boolean thresholdExceeded;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold.
+     *
+     * @param threshold The number of bytes at which to trigger an event.
+     */
+    public ThresholdingOutputStream(int threshold)
+    {
+        this.threshold = threshold;
+    }
+
+
+    // --------------------------------------------------- OutputStream methods
+
+
+    /**
+     * Writes the specified byte to this output stream.
+     *
+     * @param b The byte to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(int b) throws IOException
+    {
+        checkThreshold(1);
+        getStream().write(b);
+        written++;
+    }
+
+
+    /**
+     * Writes <code>b.length</code> bytes from the specified byte array to this
+     * output stream.
+     *
+     * @param b The array of bytes to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[]) throws IOException
+    {
+        checkThreshold(b.length);
+        getStream().write(b);
+        written += b.length;
+    }
+
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array starting at
+     * offset <code>off</code> to this output stream.
+     *
+     * @param b   The byte array from which the data will be written.
+     * @param off The start offset in the byte array.
+     * @param len The number of bytes to write.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[], int off, int len) throws IOException
+    {
+        checkThreshold(len);
+        getStream().write(b, off, len);
+        written += len;
+    }
+
+
+    /**
+     * Flushes this output stream and forces any buffered output bytes to be
+     * written out.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void flush() throws IOException
+    {
+        getStream().flush();
+    }
+
+
+    /**
+     * Closes this output stream and releases any system resources associated
+     * with this stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void close() throws IOException
+    {
+        try
+        {
+            flush();
+        }
+        catch (IOException ignored)
+        {
+            // ignore
+        }
+        getStream().close();
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Returns the threshold, in bytes, at which an event will be triggered.
+     *
+     * @return The threshold point, in bytes.
+     */
+    public int getThreshold()
+    {
+        return threshold;
+    }
+
+
+    /**
+     * Returns the number of bytes that have been written to this output stream.
+     *
+     * @return The number of bytes written.
+     */
+    public long getByteCount()
+    {
+        return written;
+    }
+
+
+    /**
+     * Determines whether or not the configured threshold has been exceeded for
+     * this output stream.
+     *
+     * @return <code>true</code> if the threshold has been reached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isThresholdExceeded()
+    {
+        return (written > threshold);
+    }
+
+
+    // ------------------------------------------------------ Protected methods
+
+
+    /**
+     * Checks to see if writing the specified number of bytes would cause the
+     * configured threshold to be exceeded. If so, triggers an event to allow
+     * a concrete implementation to take action on this.
+     *
+     * @param count The number of bytes about to be written to the underlying
+     *              output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected void checkThreshold(int count) throws IOException
+    {
+        if (!thresholdExceeded && (written + count > threshold))
+        {
+            thresholdExceeded = true;
+            thresholdReached();
+        }
+    }
+
+    /**
+     * Resets the byteCount to zero.  You can call this from 
+     * {@link #thresholdReached()} if you want the event to be triggered again. 
+     */
+    protected void resetByteCount() 
+    {
+        this.thresholdExceeded = false;
+        this.written = 0;
+    }
+
+    // ------------------------------------------------------- Abstract methods
+
+
+    /**
+     * Returns the underlying output stream, to which the corresponding
+     * <code>OutputStream</code> methods in this class will ultimately delegate.
+     *
+     * @return The underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract OutputStream getStream() throws IOException;
+
+
+    /**
+     * Indicates that the configured threshold has been reached, and that a
+     * subclass should take whatever action necessary on this event. This may
+     * include changing the underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract void thresholdReached() throws IOException;
+}
diff --git a/src/org/apache/commons/io/output/package.html b/src/org/apache/commons/io/output/package.html
index db2cbce..4415613 100644
--- a/src/org/apache/commons/io/output/package.html
+++ b/src/org/apache/commons/io/output/package.html
@@ -1,25 +1,25 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body>

-<p>

-This package provides implementations of output classes, such as

-<code>OutputStream</code> and <code>Writer</code>.

-</p>

-</body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body>
+<p>
+This package provides implementations of output classes, such as
+<code>OutputStream</code> and <code>Writer</code>.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/commons/io/overview.html b/src/org/apache/commons/io/overview.html
index 31311b5..5f2ff1e 100644
--- a/src/org/apache/commons/io/overview.html
+++ b/src/org/apache/commons/io/overview.html
@@ -1,32 +1,32 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body bgcolor="white">

-<p>

-The commons-io component contains utility classes,

-filters, streams, readers and writers.

-</p>

-<p>

-These classes aim to add to the standard JDK IO classes.

-The utilities provide convenience wrappers around the JDK, simplifying

-various operations into pre-tested units of code.

-The filters and streams provide useful implementations that perhaps should

-be in the JDK itself.

-</p>

-</body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body bgcolor="white">
+<p>
+The commons-io component contains utility classes,
+filters, streams, readers and writers.
+</p>
+<p>
+These classes aim to add to the standard JDK IO classes.
+The utilities provide convenience wrappers around the JDK, simplifying
+various operations into pre-tested units of code.
+The filters and streams provide useful implementations that perhaps should
+be in the JDK itself.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/commons/io/package.html b/src/org/apache/commons/io/package.html
index e5ba9b0..914a0a6 100644
--- a/src/org/apache/commons/io/package.html
+++ b/src/org/apache/commons/io/package.html
@@ -1,47 +1,47 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

-<!--

-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.

--->

-<html>

-<body bgcolor="white">

-<p>

-This package defines utility classes for working with streams, readers,

-writers and files. The most commonly used classes are described here:

-</p>

-<p>

-<b>IOUtils</b> is the most frequently used class.

-It provides operations to read, write, copy and close streams.

-</p>

-<p>

-<b>FileUtils</b> provides operations based around the JDK File class.

-These include reading, writing, copying, comparing and deleting.

-</p>

-<p>

-<b>FilenameUtils</b> provides utilities based on filenames.

-This utility class manipulates filenames without using File objects.

-It aims to simplify the transition between Windows and Unix.

-Before using this class however, you should consider whether you should

-be using File objects.

-</p>

-<p>

-<b>FileSystemUtils</b> allows access to the filing system in ways the JDK

-does not support. At present this allows you to get the free space on a drive.

-</p>

-<p>

-<b>EndianUtils</b> swaps data between Big-Endian and Little-Endian formats.

-</p>

-</body>

-</html>

+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+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.
+-->
+<html>
+<body bgcolor="white">
+<p>
+This package defines utility classes for working with streams, readers,
+writers and files. The most commonly used classes are described here:
+</p>
+<p>
+<b>IOUtils</b> is the most frequently used class.
+It provides operations to read, write, copy and close streams.
+</p>
+<p>
+<b>FileUtils</b> provides operations based around the JDK File class.
+These include reading, writing, copying, comparing and deleting.
+</p>
+<p>
+<b>FilenameUtils</b> provides utilities based on filenames.
+This utility class manipulates filenames without using File objects.
+It aims to simplify the transition between Windows and Unix.
+Before using this class however, you should consider whether you should
+be using File objects.
+</p>
+<p>
+<b>FileSystemUtils</b> allows access to the filing system in ways the JDK
+does not support. At present this allows you to get the free space on a drive.
+</p>
+<p>
+<b>EndianUtils</b> swaps data between Big-Endian and Little-Endian formats.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/james/mime4j/AbstractContentHandler.java b/src/org/apache/james/mime4j/AbstractContentHandler.java
index 06c9b90..3df6fb7 100644
--- a/src/org/apache/james/mime4j/AbstractContentHandler.java
+++ b/src/org/apache/james/mime4j/AbstractContentHandler.java
@@ -1,113 +1,113 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-

-/**

- * Abstract <code>ContentHandler</code> with default implementations of all

- * the methods of the <code>ContentHandler</code> interface.

- * 

- * The default is to todo nothing.

- *

- * 

- * @version $Id: AbstractContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $

- */

-public abstract class AbstractContentHandler implements ContentHandler {

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#endMultipart()

-     */

-    public void endMultipart() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)

-     */

-    public void startMultipart(BodyDescriptor bd) {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)

-     */

-    public void body(BodyDescriptor bd, InputStream is) throws IOException {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#endBodyPart()

-     */

-    public void endBodyPart() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#endHeader()

-     */

-    public void endHeader() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#endMessage()

-     */

-    public void endMessage() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)

-     */

-    public void epilogue(InputStream is) throws IOException {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)

-     */

-    public void field(String fieldData) {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)

-     */

-    public void preamble(InputStream is) throws IOException {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#startBodyPart()

-     */

-    public void startBodyPart() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#startHeader()

-     */

-    public void startHeader() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#startMessage()

-     */

-    public void startMessage() {

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)

-     */

-    public void raw(InputStream is) throws IOException {

-    }

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+
+/**
+ * Abstract <code>ContentHandler</code> with default implementations of all
+ * the methods of the <code>ContentHandler</code> interface.
+ * 
+ * The default is to todo nothing.
+ *
+ * 
+ * @version $Id: AbstractContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $
+ */
+public abstract class AbstractContentHandler implements ContentHandler {
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#endMultipart()
+     */
+    public void endMultipart() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
+     */
+    public void startMultipart(BodyDescriptor bd) {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
+     */
+    public void body(BodyDescriptor bd, InputStream is) throws IOException {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#endBodyPart()
+     */
+    public void endBodyPart() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#endHeader()
+     */
+    public void endHeader() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#endMessage()
+     */
+    public void endMessage() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
+     */
+    public void epilogue(InputStream is) throws IOException {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
+     */
+    public void field(String fieldData) {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
+     */
+    public void preamble(InputStream is) throws IOException {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#startBodyPart()
+     */
+    public void startBodyPart() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#startHeader()
+     */
+    public void startHeader() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#startMessage()
+     */
+    public void startMessage() {
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
+     */
+    public void raw(InputStream is) throws IOException {
+    }
+}
diff --git a/src/org/apache/james/mime4j/BodyDescriptor.java b/src/org/apache/james/mime4j/BodyDescriptor.java
index 2fef0be..956aa14 100644
--- a/src/org/apache/james/mime4j/BodyDescriptor.java
+++ b/src/org/apache/james/mime4j/BodyDescriptor.java
@@ -1,392 +1,392 @@
-/****************************************************************

- * 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.util.HashMap;

-import java.util.Map;

-

-/**

- * Encapsulates the values of the MIME-specific header fields

- * (which starts with <code>Content-</code>).

- *

- *

- * @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $

- */

-public class BodyDescriptor {

-    private static Log log = LogFactory.getLog(BodyDescriptor.class);

-

-    private String mimeType = "text/plain";

-    private String boundary = null;

-    private String charset = "us-ascii";

-    private String transferEncoding = "7bit";

-    private Map<String, String> parameters = new HashMap<String, String>();

-    private boolean contentTypeSet = false;

-    private boolean contentTransferEncSet = false;

-

-    /**

-     * Creates a new root <code>BodyDescriptor</code> instance.

-     */

-    public BodyDescriptor() {

-        this(null);

-    }

-

-    /**

-     * Creates a new <code>BodyDescriptor</code> instance.

-     *

-     * @param parent the descriptor of the parent or <code>null</code> if this

-     *        is the root descriptor.

-     */

-    public BodyDescriptor(BodyDescriptor parent) {

-        if (parent != null && parent.isMimeType("multipart/digest")) {

-            mimeType = "message/rfc822";

-        } else {

-            mimeType = "text/plain";

-        }

-    }

-

-    /**

-     * Should be called for each <code>Content-</code> header field of

-     * a MIME message or part.

-     *

-     * @param name the field name.

-     * @param value the field value.

-     */

-    public void addField(String name, String value) {

-

-        name = name.trim().toLowerCase();

-

-        if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {

-            contentTransferEncSet = true;

-

-            value = value.trim().toLowerCase();

-            if (value.length() > 0) {

-                transferEncoding = value;

-            }

-

-        } else if (name.equals("content-type") && !contentTypeSet) {

-            contentTypeSet = true;

-

-            value = value.trim();

-

-            /*

-             * Unfold Content-Type value

-             */

-            StringBuffer sb = new StringBuffer();

-            for (int i = 0; i < value.length(); i++) {

-                char c = value.charAt(i);

-                if (c == '\r' || c == '\n') {

-                    continue;

-                }

-                sb.append(c);

-            }

-

-            Map<String, String> params = getHeaderParams(sb.toString());

-

-            String main = params.get("");

-            if (main != null) {

-                main = main.toLowerCase().trim();

-                int index = main.indexOf('/');

-                boolean valid = false;

-                if (index != -1) {

-                    String type = main.substring(0, index).trim();

-                    String subtype = main.substring(index + 1).trim();

-                    if (type.length() > 0 && subtype.length() > 0) {

-                        main = type + "/" + subtype;

-                        valid = true;

-                    }

-                }

-

-                if (!valid) {

-                    main = null;

-                }

-            }

-            String b = params.get("boundary");

-

-            if (main != null

-                    && ((main.startsWith("multipart/") && b != null)

-                            || !main.startsWith("multipart/"))) {

-

-                mimeType = main;

-            }

-

-            if (isMultipart()) {

-                boundary = b;

-            }

-

-            String c = params.get("charset");

-            if (c != null) {

-                c = c.trim();

-                if (c.length() > 0) {

-                    charset = c.toLowerCase();

-                }

-            }

-

-            /*

-             * Add all other parameters to parameters.

-             */

-            parameters.putAll(params);

-            parameters.remove("");

-            parameters.remove("boundary");

-            parameters.remove("charset");

-        }

-    }

-

-    private Map<String, String> getHeaderParams(String headerValue) {

-        Map<String, String> result = new HashMap<String, String>();

-

-        // split main value and parameters

-        String main;

-        String rest;

-        if (headerValue.indexOf(";") == -1) {

-            main = headerValue;

-            rest = null;

-        } else {

-            main = headerValue.substring(0, headerValue.indexOf(";"));

-            rest = headerValue.substring(main.length() + 1);

-        }

-

-        result.put("", main);

-        if (rest != null) {

-            char[] chars = rest.toCharArray();

-            StringBuffer paramName = new StringBuffer();

-            StringBuffer paramValue = new StringBuffer();

-

-            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 (int i = 0; i < chars.length; i++) {

-                char c = chars[i];

-

-                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 = new StringBuffer();

-                        paramValue = new StringBuffer();

-

-                        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;

-

-                        // $FALL-THROUGH$

-

-                    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;

-    }

-

-

-    public boolean isMimeType(String mimeType) {

-        return this.mimeType.equals(mimeType.toLowerCase());

-    }

-

-    /**

-     * Return true if the BodyDescriptor belongs to a message

-     */

-    public boolean isMessage() {

-        return mimeType.equals("message/rfc822");

-    }

-

-    /**

-     * Return true if the BodyDescripotro belongs to a multipart

-     */

-    public boolean isMultipart() {

-        return mimeType.startsWith("multipart/");

-    }

-

-    /**

-     * Return the MimeType

-     */

-    public String getMimeType() {

-        return mimeType;

-    }

-

-    /**

-     * Return the boundary

-     */

-    public String getBoundary() {

-        return boundary;

-    }

-

-    /**

-     * Return the charset

-     */

-    public String getCharset() {

-        return charset;

-    }

-

-    /**

-     * Return all parameters for the BodyDescriptor

-     */

-    public Map<String, String> getParameters() {

-        return parameters;

-    }

-

-    /**

-     * Return the TransferEncoding

-     */

-    public String getTransferEncoding() {

-        return transferEncoding;

-    }

-

-    /**

-     * Return true if it's base64 encoded

-     */

-    public boolean isBase64Encoded() {

-        return "base64".equals(transferEncoding);

-    }

-

-    /**

-     * Return true if it's quoted-printable

-     */

-    public boolean isQuotedPrintableEncoded() {

-        return "quoted-printable".equals(transferEncoding);

-    }

-

-    @Override

-    public String toString() {

-        return mimeType;

-    }

-}

+/****************************************************************
+ * 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.util.HashMap;
+import java.util.Map;
+
+/**
+ * Encapsulates the values of the MIME-specific header fields
+ * (which starts with <code>Content-</code>).
+ *
+ *
+ * @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $
+ */
+public class BodyDescriptor {
+    private static Log log = LogFactory.getLog(BodyDescriptor.class);
+
+    private String mimeType = "text/plain";
+    private String boundary = null;
+    private String charset = "us-ascii";
+    private String transferEncoding = "7bit";
+    private Map<String, String> parameters = new HashMap<String, String>();
+    private boolean contentTypeSet = false;
+    private boolean contentTransferEncSet = false;
+
+    /**
+     * Creates a new root <code>BodyDescriptor</code> instance.
+     */
+    public BodyDescriptor() {
+        this(null);
+    }
+
+    /**
+     * Creates a new <code>BodyDescriptor</code> instance.
+     *
+     * @param parent the descriptor of the parent or <code>null</code> if this
+     *        is the root descriptor.
+     */
+    public BodyDescriptor(BodyDescriptor parent) {
+        if (parent != null && parent.isMimeType("multipart/digest")) {
+            mimeType = "message/rfc822";
+        } else {
+            mimeType = "text/plain";
+        }
+    }
+
+    /**
+     * Should be called for each <code>Content-</code> header field of
+     * a MIME message or part.
+     *
+     * @param name the field name.
+     * @param value the field value.
+     */
+    public void addField(String name, String value) {
+
+        name = name.trim().toLowerCase();
+
+        if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {
+            contentTransferEncSet = true;
+
+            value = value.trim().toLowerCase();
+            if (value.length() > 0) {
+                transferEncoding = value;
+            }
+
+        } else if (name.equals("content-type") && !contentTypeSet) {
+            contentTypeSet = true;
+
+            value = value.trim();
+
+            /*
+             * Unfold Content-Type value
+             */
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < value.length(); i++) {
+                char c = value.charAt(i);
+                if (c == '\r' || c == '\n') {
+                    continue;
+                }
+                sb.append(c);
+            }
+
+            Map<String, String> params = getHeaderParams(sb.toString());
+
+            String main = params.get("");
+            if (main != null) {
+                main = main.toLowerCase().trim();
+                int index = main.indexOf('/');
+                boolean valid = false;
+                if (index != -1) {
+                    String type = main.substring(0, index).trim();
+                    String subtype = main.substring(index + 1).trim();
+                    if (type.length() > 0 && subtype.length() > 0) {
+                        main = type + "/" + subtype;
+                        valid = true;
+                    }
+                }
+
+                if (!valid) {
+                    main = null;
+                }
+            }
+            String b = params.get("boundary");
+
+            if (main != null
+                    && ((main.startsWith("multipart/") && b != null)
+                            || !main.startsWith("multipart/"))) {
+
+                mimeType = main;
+            }
+
+            if (isMultipart()) {
+                boundary = b;
+            }
+
+            String c = params.get("charset");
+            if (c != null) {
+                c = c.trim();
+                if (c.length() > 0) {
+                    charset = c.toLowerCase();
+                }
+            }
+
+            /*
+             * Add all other parameters to parameters.
+             */
+            parameters.putAll(params);
+            parameters.remove("");
+            parameters.remove("boundary");
+            parameters.remove("charset");
+        }
+    }
+
+    private Map<String, String> getHeaderParams(String headerValue) {
+        Map<String, String> result = new HashMap<String, String>();
+
+        // split main value and parameters
+        String main;
+        String rest;
+        if (headerValue.indexOf(";") == -1) {
+            main = headerValue;
+            rest = null;
+        } else {
+            main = headerValue.substring(0, headerValue.indexOf(";"));
+            rest = headerValue.substring(main.length() + 1);
+        }
+
+        result.put("", main);
+        if (rest != null) {
+            char[] chars = rest.toCharArray();
+            StringBuffer paramName = new StringBuffer();
+            StringBuffer paramValue = new StringBuffer();
+
+            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 (int i = 0; i < chars.length; i++) {
+                char c = chars[i];
+
+                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 = new StringBuffer();
+                        paramValue = new StringBuffer();
+
+                        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;
+
+                        // $FALL-THROUGH$
+
+                    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;
+    }
+
+
+    public boolean isMimeType(String mimeType) {
+        return this.mimeType.equals(mimeType.toLowerCase());
+    }
+
+    /**
+     * Return true if the BodyDescriptor belongs to a message
+     */
+    public boolean isMessage() {
+        return mimeType.equals("message/rfc822");
+    }
+
+    /**
+     * Return true if the BodyDescripotro belongs to a multipart
+     */
+    public boolean isMultipart() {
+        return mimeType.startsWith("multipart/");
+    }
+
+    /**
+     * Return the MimeType
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    /**
+     * Return the boundary
+     */
+    public String getBoundary() {
+        return boundary;
+    }
+
+    /**
+     * Return the charset
+     */
+    public String getCharset() {
+        return charset;
+    }
+
+    /**
+     * Return all parameters for the BodyDescriptor
+     */
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Return the TransferEncoding
+     */
+    public String getTransferEncoding() {
+        return transferEncoding;
+    }
+
+    /**
+     * Return true if it's base64 encoded
+     */
+    public boolean isBase64Encoded() {
+        return "base64".equals(transferEncoding);
+    }
+
+    /**
+     * Return true if it's quoted-printable
+     */
+    public boolean isQuotedPrintableEncoded() {
+        return "quoted-printable".equals(transferEncoding);
+    }
+
+    @Override
+    public String toString() {
+        return mimeType;
+    }
+}
diff --git a/src/org/apache/james/mime4j/CloseShieldInputStream.java b/src/org/apache/james/mime4j/CloseShieldInputStream.java
index 94995d1..3acc98f 100644
--- a/src/org/apache/james/mime4j/CloseShieldInputStream.java
+++ b/src/org/apache/james/mime4j/CloseShieldInputStream.java
@@ -1,129 +1,129 @@
-/****************************************************************

- * 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.InputStream;

-import java.io.IOException;

-

-/**

- * InputStream that shields its underlying input stream from

- * being closed.

- * 

- * 

- * @version $Id: CloseShieldInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $

- */

-public class CloseShieldInputStream extends InputStream {

-

-    /**

-     * Underlying InputStream

-     */

-    private InputStream is;

-

-    public CloseShieldInputStream(InputStream is) {

-        this.is = is;

-    }

-

-    public InputStream getUnderlyingStream() {

-        return is;

-    }

-

-    /**

-     * @see java.io.InputStream#read()

-     */

-    public int read() throws IOException {

-        checkIfClosed();

-        return is.read();

-    }

-

-    /**

-     * @see java.io.InputStream#available()

-     */

-    public int available() throws IOException {

-        checkIfClosed();

-        return is.available();

-    }

-

-    

-    /**

-     * Set the underlying InputStream to null

-     */

-    public void close() throws IOException {

-        is = null;

-    }

-

-    /**

-     * @see java.io.FilterInputStream#reset()

-     */

-    public synchronized void reset() throws IOException {

-        checkIfClosed();

-        is.reset();

-    }

-

-    /**

-     * @see java.io.FilterInputStream#markSupported()

-     */

-    public boolean markSupported() {

-        if (is == null)

-            return false;

-        return is.markSupported();

-    }

-

-    /**

-     * @see java.io.FilterInputStream#mark(int)

-     */

-    public synchronized void mark(int readlimit) {

-        if (is != null)

-            is.mark(readlimit);

-    }

-

-    /**

-     * @see java.io.FilterInputStream#skip(long)

-     */

-    public long skip(long n) throws IOException {

-        checkIfClosed();

-        return is.skip(n);

-    }

-

-    /**

-     * @see java.io.FilterInputStream#read(byte[])

-     */

-    public int read(byte b[]) throws IOException {

-        checkIfClosed();

-        return is.read(b);

-    }

-

-    /**

-     * @see java.io.FilterInputStream#read(byte[], int, int)

-     */

-    public int read(byte b[], int off, int len) throws IOException {

-        checkIfClosed();

-        return is.read(b, off, len);

-    }

-

-    /**

-     * Check if the underlying InputStream is null. If so throw an Exception

-     * 

-     * @throws IOException if the underlying InputStream is null

-     */

-    private void checkIfClosed() throws IOException {

-        if (is == null)

-            throw new IOException("Stream is closed");

-    }

-}

+/****************************************************************
+ * 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.InputStream;
+import java.io.IOException;
+
+/**
+ * InputStream that shields its underlying input stream from
+ * being closed.
+ * 
+ * 
+ * @version $Id: CloseShieldInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
+ */
+public class CloseShieldInputStream extends InputStream {
+
+    /**
+     * Underlying InputStream
+     */
+    private InputStream is;
+
+    public CloseShieldInputStream(InputStream is) {
+        this.is = is;
+    }
+
+    public InputStream getUnderlyingStream() {
+        return is;
+    }
+
+    /**
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        checkIfClosed();
+        return is.read();
+    }
+
+    /**
+     * @see java.io.InputStream#available()
+     */
+    public int available() throws IOException {
+        checkIfClosed();
+        return is.available();
+    }
+
+    
+    /**
+     * Set the underlying InputStream to null
+     */
+    public void close() throws IOException {
+        is = null;
+    }
+
+    /**
+     * @see java.io.FilterInputStream#reset()
+     */
+    public synchronized void reset() throws IOException {
+        checkIfClosed();
+        is.reset();
+    }
+
+    /**
+     * @see java.io.FilterInputStream#markSupported()
+     */
+    public boolean markSupported() {
+        if (is == null)
+            return false;
+        return is.markSupported();
+    }
+
+    /**
+     * @see java.io.FilterInputStream#mark(int)
+     */
+    public synchronized void mark(int readlimit) {
+        if (is != null)
+            is.mark(readlimit);
+    }
+
+    /**
+     * @see java.io.FilterInputStream#skip(long)
+     */
+    public long skip(long n) throws IOException {
+        checkIfClosed();
+        return is.skip(n);
+    }
+
+    /**
+     * @see java.io.FilterInputStream#read(byte[])
+     */
+    public int read(byte b[]) throws IOException {
+        checkIfClosed();
+        return is.read(b);
+    }
+
+    /**
+     * @see java.io.FilterInputStream#read(byte[], int, int)
+     */
+    public int read(byte b[], int off, int len) throws IOException {
+        checkIfClosed();
+        return is.read(b, off, len);
+    }
+
+    /**
+     * Check if the underlying InputStream is null. If so throw an Exception
+     * 
+     * @throws IOException if the underlying InputStream is null
+     */
+    private void checkIfClosed() throws IOException {
+        if (is == null)
+            throw new IOException("Stream is closed");
+    }
+}
diff --git a/src/org/apache/james/mime4j/ContentHandler.java b/src/org/apache/james/mime4j/ContentHandler.java
index 946c894..f8213a0 100644
--- a/src/org/apache/james/mime4j/ContentHandler.java
+++ b/src/org/apache/james/mime4j/ContentHandler.java
@@ -1,177 +1,177 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-

-/**

- * <p>

- * Receives notifications of the content of a plain RFC822 or MIME message.

- * Implement this interface and register an instance of that implementation

- * with a <code>MimeStreamParser</code> instance using its 

- * {@link org.apache.james.mime4j.MimeStreamParser#setContentHandler(ContentHandler)}

- * method. The parser uses the <code>ContentHandler</code> instance to report

- * basic message-related events like the start and end of the body of a

- * part in a multipart MIME entity.

- * </p>

- * <p>

- * Events will be generated in the order the corresponding elements occur in

- * the message stream parsed by the parser. E.g.:

- * <pre>

- *      startMessage()

- *          startHeader()

- *              field(...)

- *              field(...)

- *              ...

- *          endHeader()

- *          startMultipart()

- *              preamble(...)

- *              startBodyPart()

- *                  startHeader()

- *                      field(...)

- *                      field(...)

- *                      ...

- *                  endHeader()

- *                  body()

- *              endBodyPart()

- *              startBodyPart()

- *                  startHeader()

- *                      field(...)

- *                      field(...)

- *                      ...

- *                  endHeader()

- *                  body()

- *              endBodyPart()

- *              epilogue(...)

- *          endMultipart()

- *      endMessage()

- * </pre>

- * The above shows an example of a MIME message consisting of a multipart

- * body containing two body parts.

- * </p>

- * <p>

- * See MIME RFCs 2045-2049 for more information on the structure of MIME 

- * messages and RFC 822 and 2822 for the general structure of Internet mail

- * messages.

- * </p>

- *

- * 

- * @version $Id: ContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $

- */

-public interface ContentHandler {

-    /**

-     * Called when a new message starts (a top level message or an embedded 

-     * rfc822 message).

-     */

-    void startMessage();

-    

-    /**

-     * Called when a message ends.

-     */

-    void endMessage();

-    

-    /**

-     * Called when a new body part starts inside a

-     * <code>multipart/*</code> entity.

-     */

-    void startBodyPart();

-    

-    /**

-     * Called when a body part ends.

-     */

-    void endBodyPart();

-    

-    /**

-     * Called when a header (of a message or body part) is about to be parsed.

-     */

-    void startHeader();

-    

-    /**

-     * Called for each field of a header.

-     * 

-     * @param fieldData the raw contents of the field 

-     *        (<code>Field-Name: field value</code>). The value will not be 

-     *        unfolded.

-     */

-    void field(String fieldData);

-    

-    /**

-     * Called when there are no more header fields in a message or body part.

-     */

-    void endHeader();

-    

-    /**

-     * Called for the preamble (whatever comes before the first body part)

-     * of a <code>multipart/*</code> entity.

-     * 

-     * @param is used to get the contents of the preamble.

-     * @throws IOException should be thrown on I/O errors.

-     */

-    void preamble(InputStream is) throws IOException;

-    

-    /**

-     * Called for the epilogue (whatever comes after the final body part) 

-     * of a <code>multipart/*</code> entity.

-     * 

-     * @param is used to get the contents of the epilogue.

-     * @throws IOException should be thrown on I/O errors.

-     */

-    void epilogue(InputStream is) throws IOException;

-    

-    /**

-     * Called when the body of a multipart entity is about to be parsed.

-     * 

-     * @param bd encapsulates the values (either read from the 

-     *        message stream or, if not present, determined implictly 

-     *        as described in the 

-     *        MIME rfc:s) of the <code>Content-Type</code> and 

-     *        <code>Content-Transfer-Encoding</code> header fields.

-     */

-    void startMultipart(BodyDescriptor bd);

-    

-    /**

-     * Called when the body of an entity has been parsed.

-     */

-    void endMultipart();

-    

-    /**

-     * Called when the body of a discrete (non-multipart) entity is about to 

-     * be parsed.

-     * 

-     * @param bd see {@link #startMultipart(BodyDescriptor)}

-     * @param is the contents of the body. NOTE: this is the raw body contents 

-     *           - it will not be decoded if encoded. The <code>bd</code>

-     *           parameter should be used to determine how the stream data

-     *           should be decoded.

-     * @throws IOException should be thrown on I/O errors.

-     */

-    void body(BodyDescriptor bd, InputStream is) throws IOException;

-    

-    /**

-     * Called when a new entity (message or body part) starts and the 

-     * parser is in <code>raw</code> mode.

-     * 

-     * @param is the raw contents of the entity.

-     * @throws IOException should be thrown on I/O errors.

-     * @see MimeStreamParser#setRaw(boolean)

-     */

-    void raw(InputStream is) throws IOException;

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+
+/**
+ * <p>
+ * Receives notifications of the content of a plain RFC822 or MIME message.
+ * Implement this interface and register an instance of that implementation
+ * with a <code>MimeStreamParser</code> instance using its 
+ * {@link org.apache.james.mime4j.MimeStreamParser#setContentHandler(ContentHandler)}
+ * method. The parser uses the <code>ContentHandler</code> instance to report
+ * basic message-related events like the start and end of the body of a
+ * part in a multipart MIME entity.
+ * </p>
+ * <p>
+ * Events will be generated in the order the corresponding elements occur in
+ * the message stream parsed by the parser. E.g.:
+ * <pre>
+ *      startMessage()
+ *          startHeader()
+ *              field(...)
+ *              field(...)
+ *              ...
+ *          endHeader()
+ *          startMultipart()
+ *              preamble(...)
+ *              startBodyPart()
+ *                  startHeader()
+ *                      field(...)
+ *                      field(...)
+ *                      ...
+ *                  endHeader()
+ *                  body()
+ *              endBodyPart()
+ *              startBodyPart()
+ *                  startHeader()
+ *                      field(...)
+ *                      field(...)
+ *                      ...
+ *                  endHeader()
+ *                  body()
+ *              endBodyPart()
+ *              epilogue(...)
+ *          endMultipart()
+ *      endMessage()
+ * </pre>
+ * The above shows an example of a MIME message consisting of a multipart
+ * body containing two body parts.
+ * </p>
+ * <p>
+ * See MIME RFCs 2045-2049 for more information on the structure of MIME 
+ * messages and RFC 822 and 2822 for the general structure of Internet mail
+ * messages.
+ * </p>
+ *
+ * 
+ * @version $Id: ContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $
+ */
+public interface ContentHandler {
+    /**
+     * Called when a new message starts (a top level message or an embedded 
+     * rfc822 message).
+     */
+    void startMessage();
+    
+    /**
+     * Called when a message ends.
+     */
+    void endMessage();
+    
+    /**
+     * Called when a new body part starts inside a
+     * <code>multipart/*</code> entity.
+     */
+    void startBodyPart();
+    
+    /**
+     * Called when a body part ends.
+     */
+    void endBodyPart();
+    
+    /**
+     * Called when a header (of a message or body part) is about to be parsed.
+     */
+    void startHeader();
+    
+    /**
+     * Called for each field of a header.
+     * 
+     * @param fieldData the raw contents of the field 
+     *        (<code>Field-Name: field value</code>). The value will not be 
+     *        unfolded.
+     */
+    void field(String fieldData);
+    
+    /**
+     * Called when there are no more header fields in a message or body part.
+     */
+    void endHeader();
+    
+    /**
+     * Called for the preamble (whatever comes before the first body part)
+     * of a <code>multipart/*</code> entity.
+     * 
+     * @param is used to get the contents of the preamble.
+     * @throws IOException should be thrown on I/O errors.
+     */
+    void preamble(InputStream is) throws IOException;
+    
+    /**
+     * Called for the epilogue (whatever comes after the final body part) 
+     * of a <code>multipart/*</code> entity.
+     * 
+     * @param is used to get the contents of the epilogue.
+     * @throws IOException should be thrown on I/O errors.
+     */
+    void epilogue(InputStream is) throws IOException;
+    
+    /**
+     * Called when the body of a multipart entity is about to be parsed.
+     * 
+     * @param bd encapsulates the values (either read from the 
+     *        message stream or, if not present, determined implictly 
+     *        as described in the 
+     *        MIME rfc:s) of the <code>Content-Type</code> and 
+     *        <code>Content-Transfer-Encoding</code> header fields.
+     */
+    void startMultipart(BodyDescriptor bd);
+    
+    /**
+     * Called when the body of an entity has been parsed.
+     */
+    void endMultipart();
+    
+    /**
+     * Called when the body of a discrete (non-multipart) entity is about to 
+     * be parsed.
+     * 
+     * @param bd see {@link #startMultipart(BodyDescriptor)}
+     * @param is the contents of the body. NOTE: this is the raw body contents 
+     *           - it will not be decoded if encoded. The <code>bd</code>
+     *           parameter should be used to determine how the stream data
+     *           should be decoded.
+     * @throws IOException should be thrown on I/O errors.
+     */
+    void body(BodyDescriptor bd, InputStream is) throws IOException;
+    
+    /**
+     * Called when a new entity (message or body part) starts and the 
+     * parser is in <code>raw</code> mode.
+     * 
+     * @param is the raw contents of the entity.
+     * @throws IOException should be thrown on I/O errors.
+     * @see MimeStreamParser#setRaw(boolean)
+     */
+    void raw(InputStream is) throws IOException;
+}
diff --git a/src/org/apache/james/mime4j/EOLConvertingInputStream.java b/src/org/apache/james/mime4j/EOLConvertingInputStream.java
index c033660..986063b 100644
--- a/src/org/apache/james/mime4j/EOLConvertingInputStream.java
+++ b/src/org/apache/james/mime4j/EOLConvertingInputStream.java
@@ -1,139 +1,139 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-import java.io.PushbackInputStream;

-

-/**

- * InputStream which converts <code>\r</code>

- * bytes not followed by <code>\n</code> and <code>\n</code> not 

- * preceded by <code>\r</code> to <code>\r\n</code>.

- *

- * 

- * @version $Id: EOLConvertingInputStream.java,v 1.4 2004/11/29 13:15:42 ntherning Exp $

- */

-public class EOLConvertingInputStream extends InputStream {

-    /** Converts single '\r' to '\r\n' */

-    public static final int CONVERT_CR   = 1;

-    /** Converts single '\n' to '\r\n' */

-    public static final int CONVERT_LF   = 2;

-    /** Converts single '\r' and '\n' to '\r\n' */

-    public static final int CONVERT_BOTH = 3;

-

-    private PushbackInputStream in = null;

-    private int previous = 0;

-    private int flags = CONVERT_BOTH;

-    private int size = 0;

-    private int pos = 0;

-    private int nextTenPctPos;

-    private int tenPctSize;

-    private Callback callback;

-

-    public interface Callback {

-        public void report(int bytesRead);

-    }

-

-    /**

-     * Creates a new <code>EOLConvertingInputStream</code>

-     * instance converting bytes in the given <code>InputStream</code>.

-     * The flag <code>CONVERT_BOTH</code> is the default.

-     * 

-     * @param in the <code>InputStream</code> to read from.

-     */

-    public EOLConvertingInputStream(InputStream _in) {

-        super();

-        in = new PushbackInputStream(_in, 2);

-    }

-

-    /**

-     * Creates a new <code>EOLConvertingInputStream</code>

-     * instance converting bytes in the given <code>InputStream</code>.

-     * 

-     * @param _in the <code>InputStream</code> to read from.

-     * @param _size the size of the input stream (need not be exact)

-     * @param _callback a callback reporting when each 10% of stream's size is reached

-     */

-    public EOLConvertingInputStream(InputStream _in, int _size, Callback _callback) {

-        this(_in);

-        size = _size;

-        tenPctSize = size / 10;

-        nextTenPctPos = tenPctSize;

-        callback = _callback;

-    }

-

-    /**

-     * Closes the underlying stream.

-     * 

-     * @throws IOException on I/O errors.

-     */

-    public void close() throws IOException {

-        in.close();

-    }

-    

-    private int readByte() throws IOException {

-        int b = in.read();

-        if (b != -1) {

-            if (callback != null && pos++ == nextTenPctPos) {

-                nextTenPctPos += tenPctSize;

-                if (callback != null) {

-                    callback.report(pos);

-                }

-            }

-        }

-        return b;

-    }

-

-    private void unreadByte(int c) throws IOException {

-        in.unread(c);

-        pos--;

-    }

-

-    /**

-     * @see java.io.InputStream#read()

-     */

-    public int read() throws IOException {

-        int b = readByte();

-        

-        if (b == -1) {

-            pos = size;

-            return -1;

-        }

-        

-        if ((flags & CONVERT_CR) != 0 && b == '\r') {

-            int c = readByte();

-            if (c != -1) {

-                unreadByte(c);

-            }

-            if (c != '\n') {

-                unreadByte('\n');

-            }

-        } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {

-            b = '\r';

-            unreadByte('\n');

-        }

-        

-        previous = b;

-        

-        return b;

-    }

-

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/**
+ * InputStream which converts <code>\r</code>
+ * bytes not followed by <code>\n</code> and <code>\n</code> not 
+ * preceded by <code>\r</code> to <code>\r\n</code>.
+ *
+ * 
+ * @version $Id: EOLConvertingInputStream.java,v 1.4 2004/11/29 13:15:42 ntherning Exp $
+ */
+public class EOLConvertingInputStream extends InputStream {
+    /** Converts single '\r' to '\r\n' */
+    public static final int CONVERT_CR   = 1;
+    /** Converts single '\n' to '\r\n' */
+    public static final int CONVERT_LF   = 2;
+    /** Converts single '\r' and '\n' to '\r\n' */
+    public static final int CONVERT_BOTH = 3;
+
+    private PushbackInputStream in = null;
+    private int previous = 0;
+    private int flags = CONVERT_BOTH;
+    private int size = 0;
+    private int pos = 0;
+    private int nextTenPctPos;
+    private int tenPctSize;
+    private Callback callback;
+
+    public interface Callback {
+        public void report(int bytesRead);
+    }
+
+    /**
+     * Creates a new <code>EOLConvertingInputStream</code>
+     * instance converting bytes in the given <code>InputStream</code>.
+     * The flag <code>CONVERT_BOTH</code> is the default.
+     * 
+     * @param in the <code>InputStream</code> to read from.
+     */
+    public EOLConvertingInputStream(InputStream _in) {
+        super();
+        in = new PushbackInputStream(_in, 2);
+    }
+
+    /**
+     * Creates a new <code>EOLConvertingInputStream</code>
+     * instance converting bytes in the given <code>InputStream</code>.
+     * 
+     * @param _in the <code>InputStream</code> to read from.
+     * @param _size the size of the input stream (need not be exact)
+     * @param _callback a callback reporting when each 10% of stream's size is reached
+     */
+    public EOLConvertingInputStream(InputStream _in, int _size, Callback _callback) {
+        this(_in);
+        size = _size;
+        tenPctSize = size / 10;
+        nextTenPctPos = tenPctSize;
+        callback = _callback;
+    }
+
+    /**
+     * Closes the underlying stream.
+     * 
+     * @throws IOException on I/O errors.
+     */
+    public void close() throws IOException {
+        in.close();
+    }
+    
+    private int readByte() throws IOException {
+        int b = in.read();
+        if (b != -1) {
+            if (callback != null && pos++ == nextTenPctPos) {
+                nextTenPctPos += tenPctSize;
+                if (callback != null) {
+                    callback.report(pos);
+                }
+            }
+        }
+        return b;
+    }
+
+    private void unreadByte(int c) throws IOException {
+        in.unread(c);
+        pos--;
+    }
+
+    /**
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        int b = readByte();
+        
+        if (b == -1) {
+            pos = size;
+            return -1;
+        }
+        
+        if ((flags & CONVERT_CR) != 0 && b == '\r') {
+            int c = readByte();
+            if (c != -1) {
+                unreadByte(c);
+            }
+            if (c != '\n') {
+                unreadByte('\n');
+            }
+        } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {
+            b = '\r';
+            unreadByte('\n');
+        }
+        
+        previous = b;
+        
+        return b;
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/MimeBoundaryInputStream.java b/src/org/apache/james/mime4j/MimeBoundaryInputStream.java
index 0fffb78..75d1bf7 100644
--- a/src/org/apache/james/mime4j/MimeBoundaryInputStream.java
+++ b/src/org/apache/james/mime4j/MimeBoundaryInputStream.java
@@ -1,184 +1,184 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-import java.io.PushbackInputStream;

-

-/**

- * Stream that constrains itself to a single MIME body part.

- * After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()}

- * can be used to determine if a final boundary has been seen or not.

- * If {@link #parentEOF()} is <code>true</code> an unexpected end of stream

- * has been detected in the parent stream.

- * 

- * 

- * 

- * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $

- */

-public class MimeBoundaryInputStream extends InputStream {

-    

-    private PushbackInputStream s = null;

-    private byte[] boundary = null;

-    private boolean first = true;

-    private boolean eof = false;

-    private boolean parenteof = false;

-    private boolean moreParts = true;

-

-    /**

-     * Creates a new MimeBoundaryInputStream.

-     * @param s The underlying stream.

-     * @param boundary Boundary string (not including leading hyphens).

-     */

-    public MimeBoundaryInputStream(InputStream s, String boundary) 

-            throws IOException {

-        

-        this.s = new PushbackInputStream(s, boundary.length() + 4);

-

-        boundary = "--" + boundary;

-        this.boundary = new byte[boundary.length()];

-        for (int i = 0; i < this.boundary.length; i++) {

-            this.boundary[i] = (byte) boundary.charAt(i);

-        }

-        

-        /*

-         * By reading one byte we will update moreParts to be as expected

-         * before any bytes have been read.

-         */

-        int b = read();

-        if (b != -1) {

-            this.s.unread(b);

-        }

-    }

-

-    /**

-     * Closes the underlying stream.

-     * 

-     * @throws IOException on I/O errors.

-     */

-    public void close() throws IOException {

-        s.close();

-    }

-

-    /**

-     * Determines if the underlying stream has more parts (this stream has

-     * not seen an end boundary).

-     * 

-     * @return <code>true</code> if there are more parts in the underlying 

-     *         stream, <code>false</code> otherwise.

-     */

-    public boolean hasMoreParts() {

-        return moreParts;

-    }

-

-    /**

-     * Determines if the parent stream has reached EOF

-     * 

-     * @return <code>true</code>  if EOF has been reached for the parent stream, 

-     *         <code>false</code> otherwise.

-     */

-    public boolean parentEOF() {

-        return parenteof;

-    }

-    

-    /**

-     * Consumes all unread bytes of this stream. After a call to this method

-     * this stream will have reached EOF.

-     * 

-     * @throws IOException on I/O errors.

-     */

-    public void consume() throws IOException {

-        while (read() != -1) {

-        }

-    }

-    

-    /**

-     * @see java.io.InputStream#read()

-     */

-    public int read() throws IOException {

-        if (eof) {

-            return -1;

-        }

-        

-        if (first) {

-            first = false;

-            if (matchBoundary()) {

-                return -1;

-            }

-        }

-        

-        int b1 = s.read();

-        int b2 = s.read();

-        

-        if (b1 == '\r' && b2 == '\n') {

-            if (matchBoundary()) {

-                return -1;

-            }

-        }

-        

-        if (b2 != -1) {

-            s.unread(b2);

-        }

-

-        parenteof = b1 == -1;

-        eof = parenteof;

-        

-        return b1;

-    }

-    

-    private boolean matchBoundary() throws IOException {

-        

-        for (int i = 0; i < boundary.length; i++) {

-            int b = s.read();

-            if (b != boundary[i]) {

-                if (b != -1) {

-                    s.unread(b);

-                }

-                for (int j = i - 1; j >= 0; j--) {

-                    s.unread(boundary[j]);

-                }

-                return false;

-            }

-        }

-        

-        /*

-         * We have a match. Is it an end boundary?

-         */

-        int prev = s.read();

-        int curr = s.read();

-        moreParts = !(prev == '-' && curr == '-');

-        do {

-            if (curr == '\n' && prev == '\r') {

-                break;

-            }

-            prev = curr;

-        } while ((curr = s.read()) != -1);

-        

-        if (curr == -1) {

-            moreParts = false;

-            parenteof = true;

-        }

-        

-        eof = true;

-        

-        return true;

-    }

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/**
+ * Stream that constrains itself to a single MIME body part.
+ * After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()}
+ * can be used to determine if a final boundary has been seen or not.
+ * If {@link #parentEOF()} is <code>true</code> an unexpected end of stream
+ * has been detected in the parent stream.
+ * 
+ * 
+ * 
+ * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
+ */
+public class MimeBoundaryInputStream extends InputStream {
+    
+    private PushbackInputStream s = null;
+    private byte[] boundary = null;
+    private boolean first = true;
+    private boolean eof = false;
+    private boolean parenteof = false;
+    private boolean moreParts = true;
+
+    /**
+     * Creates a new MimeBoundaryInputStream.
+     * @param s The underlying stream.
+     * @param boundary Boundary string (not including leading hyphens).
+     */
+    public MimeBoundaryInputStream(InputStream s, String boundary) 
+            throws IOException {
+        
+        this.s = new PushbackInputStream(s, boundary.length() + 4);
+
+        boundary = "--" + boundary;
+        this.boundary = new byte[boundary.length()];
+        for (int i = 0; i < this.boundary.length; i++) {
+            this.boundary[i] = (byte) boundary.charAt(i);
+        }
+        
+        /*
+         * By reading one byte we will update moreParts to be as expected
+         * before any bytes have been read.
+         */
+        int b = read();
+        if (b != -1) {
+            this.s.unread(b);
+        }
+    }
+
+    /**
+     * Closes the underlying stream.
+     * 
+     * @throws IOException on I/O errors.
+     */
+    public void close() throws IOException {
+        s.close();
+    }
+
+    /**
+     * Determines if the underlying stream has more parts (this stream has
+     * not seen an end boundary).
+     * 
+     * @return <code>true</code> if there are more parts in the underlying 
+     *         stream, <code>false</code> otherwise.
+     */
+    public boolean hasMoreParts() {
+        return moreParts;
+    }
+
+    /**
+     * Determines if the parent stream has reached EOF
+     * 
+     * @return <code>true</code>  if EOF has been reached for the parent stream, 
+     *         <code>false</code> otherwise.
+     */
+    public boolean parentEOF() {
+        return parenteof;
+    }
+    
+    /**
+     * Consumes all unread bytes of this stream. After a call to this method
+     * this stream will have reached EOF.
+     * 
+     * @throws IOException on I/O errors.
+     */
+    public void consume() throws IOException {
+        while (read() != -1) {
+        }
+    }
+    
+    /**
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        if (eof) {
+            return -1;
+        }
+        
+        if (first) {
+            first = false;
+            if (matchBoundary()) {
+                return -1;
+            }
+        }
+        
+        int b1 = s.read();
+        int b2 = s.read();
+        
+        if (b1 == '\r' && b2 == '\n') {
+            if (matchBoundary()) {
+                return -1;
+            }
+        }
+        
+        if (b2 != -1) {
+            s.unread(b2);
+        }
+
+        parenteof = b1 == -1;
+        eof = parenteof;
+        
+        return b1;
+    }
+    
+    private boolean matchBoundary() throws IOException {
+        
+        for (int i = 0; i < boundary.length; i++) {
+            int b = s.read();
+            if (b != boundary[i]) {
+                if (b != -1) {
+                    s.unread(b);
+                }
+                for (int j = i - 1; j >= 0; j--) {
+                    s.unread(boundary[j]);
+                }
+                return false;
+            }
+        }
+        
+        /*
+         * We have a match. Is it an end boundary?
+         */
+        int prev = s.read();
+        int curr = s.read();
+        moreParts = !(prev == '-' && curr == '-');
+        do {
+            if (curr == '\n' && prev == '\r') {
+                break;
+            }
+            prev = curr;
+        } while ((curr = s.read()) != -1);
+        
+        if (curr == -1) {
+            moreParts = false;
+            parenteof = true;
+        }
+        
+        eof = true;
+        
+        return true;
+    }
+}
diff --git a/src/org/apache/james/mime4j/MimeStreamParser.java b/src/org/apache/james/mime4j/MimeStreamParser.java
index b030a44..dd50e04 100644
--- a/src/org/apache/james/mime4j/MimeStreamParser.java
+++ b/src/org/apache/james/mime4j/MimeStreamParser.java
@@ -1,324 +1,324 @@
-/****************************************************************

- * 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 org.apache.james.mime4j.decoder.Base64InputStream;

-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.util.BitSet;

-import java.util.LinkedList;

-

-/**

- * <p>

- * Parses MIME (or RFC822) message streams of bytes or characters and reports

- * parsing events to a <code>ContentHandler</code> instance.

- * </p>

- * <p>

- * Typical usage:<br/>

- * <pre>

- *      ContentHandler handler = new MyHandler();

- *      MimeStreamParser parser = new MimeStreamParser();

- *      parser.setContentHandler(handler);

- *      parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));

- * </pre>

- * <strong>NOTE:</strong> All lines must end with CRLF

- * (<code>\r\n</code>). If you are unsure of the line endings in your stream

- * you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance.

- *

- *

- * @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $

- */

-public class MimeStreamParser {

-    private static final Log log = LogFactory.getLog(MimeStreamParser.class);

-

-    private static BitSet fieldChars = null;

-

-    private RootInputStream rootStream = null;

-    private LinkedList<BodyDescriptor> bodyDescriptors = new LinkedList<BodyDescriptor>();

-    private ContentHandler handler = null;

-    private boolean raw = false;

-    private boolean prematureEof = false;

-

-    static {

-        fieldChars = new BitSet();

-        for (int i = 0x21; i <= 0x39; i++) {

-            fieldChars.set(i);

-        }

-        for (int i = 0x3b; i <= 0x7e; i++) {

-            fieldChars.set(i);

-        }

-    }

-

-    /**

-     * Creates a new <code>MimeStreamParser</code> instance.

-     */

-    public MimeStreamParser() {

-    }

-

-    /**

-     * Parses a stream of bytes containing a MIME message.

-     *

-     * @param is the stream to parse.

-     * @throws IOException on I/O errors.

-     */

-    public void parse(InputStream is) throws IOException {

-        rootStream = new RootInputStream(is);

-        parseMessage(rootStream);

-    }

-

-    /**

-     * Determines if this parser is currently in raw mode.

-     *

-     * @return <code>true</code> if in raw mode, <code>false</code>

-     *         otherwise.

-     * @see #setRaw(boolean)

-     */

-    public boolean isRaw() {

-        return raw;

-    }

-

-    /**

-     * Enables or disables raw mode. In raw mode all future entities

-     * (messages or body parts) in the stream will be reported to the

-     * {@link ContentHandler#raw(InputStream)} handler method only.

-     * The stream will contain the entire unparsed entity contents

-     * including header fields and whatever is in the body.

-     *

-     * @param raw <code>true</code> enables raw mode, <code>false</code>

-     *        disables it.

-     */

-    public void setRaw(boolean raw) {

-        this.raw = raw;

-    }

-

-    /**

-     * Finishes the parsing and stops reading lines.

-     * NOTE: No more lines will be parsed but the parser

-     * will still call

-     * {@link ContentHandler#endMultipart()},

-     * {@link ContentHandler#endBodyPart()},

-     * {@link ContentHandler#endMessage()}, etc to match previous calls

-     * to

-     * {@link ContentHandler#startMultipart(BodyDescriptor)},

-     * {@link ContentHandler#startBodyPart()},

-     * {@link ContentHandler#startMessage()}, etc.

-     */

-    public void stop() {

-        rootStream.truncate();

-    }

-

-    /**

-     * Parses an entity which consists of a header followed by a body containing

-     * arbitrary data, body parts or an embedded message.

-     *

-     * @param is the stream to parse.

-     * @throws IOException on I/O errors.

-     */

-    private void parseEntity(InputStream is) throws IOException {

-        BodyDescriptor bd = parseHeader(is);

-

-        if (bd.isMultipart()) {

-            bodyDescriptors.addFirst(bd);

-

-            handler.startMultipart(bd);

-

-            MimeBoundaryInputStream tempIs =

-                new MimeBoundaryInputStream(is, bd.getBoundary());

-            handler.preamble(new CloseShieldInputStream(tempIs));

-            tempIs.consume();

-

-            while (tempIs.hasMoreParts()) {

-                tempIs = new MimeBoundaryInputStream(is, bd.getBoundary());

-                parseBodyPart(tempIs);

-                tempIs.consume();

-                if (tempIs.parentEOF()) {

-                    prematureEof = true;

-//                    if (log.isWarnEnabled()) {

-//                        log.warn("Line " + rootStream.getLineNumber()

-//                                + ": Body part ended prematurely. "

-//                                + "Higher level boundary detected or "

-//                                + "EOF reached.");

-//                    }

-                    break;

-                }

-            }

-

-            handler.epilogue(new CloseShieldInputStream(is));

-

-            handler.endMultipart();

-

-            bodyDescriptors.removeFirst();

-

-        } else if (bd.isMessage()) {

-            if (bd.isBase64Encoded()) {

-                log.warn("base64 encoded message/rfc822 detected");

-                is = new EOLConvertingInputStream(

-                        new Base64InputStream(is));

-            } else if (bd.isQuotedPrintableEncoded()) {

-                log.warn("quoted-printable encoded message/rfc822 detected");

-                is = new EOLConvertingInputStream(

-                        new QuotedPrintableInputStream(is));

-            }

-            bodyDescriptors.addFirst(bd);

-            parseMessage(is);

-            bodyDescriptors.removeFirst();

-        } else {

-            handler.body(bd, new CloseShieldInputStream(is));

-        }

-

-        /*

-         * Make sure the stream has been consumed.

-         */

-        while (is.read() != -1) {

-        }

-    }

-

-    private void parseMessage(InputStream is) throws IOException {

-        if (raw) {

-            handler.raw(new CloseShieldInputStream(is));

-        } else {

-            handler.startMessage();

-            parseEntity(is);

-            handler.endMessage();

-        }

-    }

-

-    public boolean getPrematureEof() {

-        return prematureEof;

-    }

-

-    private void parseBodyPart(InputStream is) throws IOException {

-        if (raw) {

-            handler.raw(new CloseShieldInputStream(is));

-        } else {

-            handler.startBodyPart();

-            parseEntity(is);

-            handler.endBodyPart();

-        }

-    }

-

-    /**

-     * Parses a header.

-     *

-     * @param is the stream to parse.

-     * @return a <code>BodyDescriptor</code> describing the body following

-     *         the header.

-     */

-    private BodyDescriptor parseHeader(InputStream is) throws IOException {

-        BodyDescriptor bd = new BodyDescriptor(bodyDescriptors.isEmpty()

-                        ? null : (BodyDescriptor) bodyDescriptors.getFirst());

-

-        handler.startHeader();

-

-        int lineNumber = rootStream.getLineNumber();

-

-        StringBuffer sb = new StringBuffer();

-        int curr = 0;

-        int prev = 0;

-        while ((curr = is.read()) != -1) {

-            if (curr == '\n' && (prev == '\n' || prev == 0)) {

-                /*

-                 * [\r]\n[\r]\n or an immediate \r\n have been seen.

-                 */

-                sb.deleteCharAt(sb.length() - 1);

-                break;

-            }

-            sb.append((char) curr);

-            prev = curr == '\r' ? prev : curr;

-        }

-

-//        if (curr == -1 && log.isWarnEnabled()) {

-//            log.warn("Line " + rootStream.getLineNumber()

-//                    + ": Unexpected end of headers detected. "

-//                    + "Boundary detected in header or EOF reached.");

-//        }

-

-        int start = 0;

-        int pos = 0;

-        int startLineNumber = lineNumber;

-        while (pos < sb.length()) {

-            while (pos < sb.length() && sb.charAt(pos) != '\r') {

-                pos++;

-            }

-            if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {

-                pos++;

-                continue;

-            }

-

-            if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {

-

-                /*

-                 * field should be the complete field data excluding the

-                 * trailing \r\n.

-                 */

-                String field = sb.substring(start, pos);

-                start = pos + 2;

-

-                /*

-                 * Check for a valid field.

-                 */

-                int index = field.indexOf(':');

-                boolean valid = false;

-                if (index != -1 && fieldChars.get(field.charAt(0))) {

-                    valid = true;

-                    String fieldName = field.substring(0, index).trim();

-                    for (int i = 0; i < fieldName.length(); i++) {

-                        if (!fieldChars.get(fieldName.charAt(i))) {

-                            valid = false;

-                            break;

-                        }

-                    }

-

-                    if (valid) {

-                        handler.field(field);

-                        bd.addField(fieldName, field.substring(index + 1));

-                    }

-                }

-

-                if (!valid && log.isWarnEnabled()) {

-                    log.warn("Line " + startLineNumber

-                            + ": Ignoring invalid field: '" + field.trim() + "'");

-                }

-

-                startLineNumber = lineNumber;

-            }

-

-            pos += 2;

-            lineNumber++;

-        }

-

-        handler.endHeader();

-

-        return bd;

-    }

-

-    /**

-     * Sets the <code>ContentHandler</code> to use when reporting

-     * parsing events.

-     *

-     * @param h the <code>ContentHandler</code>.

-     */

-    public void setContentHandler(ContentHandler h) {

-        this.handler = h;

-    }

-

-}

+/****************************************************************
+ * 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 org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.BitSet;
+import java.util.LinkedList;
+
+/**
+ * <p>
+ * Parses MIME (or RFC822) message streams of bytes or characters and reports
+ * parsing events to a <code>ContentHandler</code> instance.
+ * </p>
+ * <p>
+ * Typical usage:<br/>
+ * <pre>
+ *      ContentHandler handler = new MyHandler();
+ *      MimeStreamParser parser = new MimeStreamParser();
+ *      parser.setContentHandler(handler);
+ *      parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
+ * </pre>
+ * <strong>NOTE:</strong> All lines must end with CRLF
+ * (<code>\r\n</code>). If you are unsure of the line endings in your stream
+ * you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance.
+ *
+ *
+ * @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $
+ */
+public class MimeStreamParser {
+    private static final Log log = LogFactory.getLog(MimeStreamParser.class);
+
+    private static BitSet fieldChars = null;
+
+    private RootInputStream rootStream = null;
+    private LinkedList<BodyDescriptor> bodyDescriptors = new LinkedList<BodyDescriptor>();
+    private ContentHandler handler = null;
+    private boolean raw = false;
+    private boolean prematureEof = false;
+
+    static {
+        fieldChars = new BitSet();
+        for (int i = 0x21; i <= 0x39; i++) {
+            fieldChars.set(i);
+        }
+        for (int i = 0x3b; i <= 0x7e; i++) {
+            fieldChars.set(i);
+        }
+    }
+
+    /**
+     * Creates a new <code>MimeStreamParser</code> instance.
+     */
+    public MimeStreamParser() {
+    }
+
+    /**
+     * Parses a stream of bytes containing a MIME message.
+     *
+     * @param is the stream to parse.
+     * @throws IOException on I/O errors.
+     */
+    public void parse(InputStream is) throws IOException {
+        rootStream = new RootInputStream(is);
+        parseMessage(rootStream);
+    }
+
+    /**
+     * Determines if this parser is currently in raw mode.
+     *
+     * @return <code>true</code> if in raw mode, <code>false</code>
+     *         otherwise.
+     * @see #setRaw(boolean)
+     */
+    public boolean isRaw() {
+        return raw;
+    }
+
+    /**
+     * Enables or disables raw mode. In raw mode all future entities
+     * (messages or body parts) in the stream will be reported to the
+     * {@link ContentHandler#raw(InputStream)} handler method only.
+     * The stream will contain the entire unparsed entity contents
+     * including header fields and whatever is in the body.
+     *
+     * @param raw <code>true</code> enables raw mode, <code>false</code>
+     *        disables it.
+     */
+    public void setRaw(boolean raw) {
+        this.raw = raw;
+    }
+
+    /**
+     * Finishes the parsing and stops reading lines.
+     * NOTE: No more lines will be parsed but the parser
+     * will still call
+     * {@link ContentHandler#endMultipart()},
+     * {@link ContentHandler#endBodyPart()},
+     * {@link ContentHandler#endMessage()}, etc to match previous calls
+     * to
+     * {@link ContentHandler#startMultipart(BodyDescriptor)},
+     * {@link ContentHandler#startBodyPart()},
+     * {@link ContentHandler#startMessage()}, etc.
+     */
+    public void stop() {
+        rootStream.truncate();
+    }
+
+    /**
+     * Parses an entity which consists of a header followed by a body containing
+     * arbitrary data, body parts or an embedded message.
+     *
+     * @param is the stream to parse.
+     * @throws IOException on I/O errors.
+     */
+    private void parseEntity(InputStream is) throws IOException {
+        BodyDescriptor bd = parseHeader(is);
+
+        if (bd.isMultipart()) {
+            bodyDescriptors.addFirst(bd);
+
+            handler.startMultipart(bd);
+
+            MimeBoundaryInputStream tempIs =
+                new MimeBoundaryInputStream(is, bd.getBoundary());
+            handler.preamble(new CloseShieldInputStream(tempIs));
+            tempIs.consume();
+
+            while (tempIs.hasMoreParts()) {
+                tempIs = new MimeBoundaryInputStream(is, bd.getBoundary());
+                parseBodyPart(tempIs);
+                tempIs.consume();
+                if (tempIs.parentEOF()) {
+                    prematureEof = true;
+//                    if (log.isWarnEnabled()) {
+//                        log.warn("Line " + rootStream.getLineNumber()
+//                                + ": Body part ended prematurely. "
+//                                + "Higher level boundary detected or "
+//                                + "EOF reached.");
+//                    }
+                    break;
+                }
+            }
+
+            handler.epilogue(new CloseShieldInputStream(is));
+
+            handler.endMultipart();
+
+            bodyDescriptors.removeFirst();
+
+        } else if (bd.isMessage()) {
+            if (bd.isBase64Encoded()) {
+                log.warn("base64 encoded message/rfc822 detected");
+                is = new EOLConvertingInputStream(
+                        new Base64InputStream(is));
+            } else if (bd.isQuotedPrintableEncoded()) {
+                log.warn("quoted-printable encoded message/rfc822 detected");
+                is = new EOLConvertingInputStream(
+                        new QuotedPrintableInputStream(is));
+            }
+            bodyDescriptors.addFirst(bd);
+            parseMessage(is);
+            bodyDescriptors.removeFirst();
+        } else {
+            handler.body(bd, new CloseShieldInputStream(is));
+        }
+
+        /*
+         * Make sure the stream has been consumed.
+         */
+        while (is.read() != -1) {
+        }
+    }
+
+    private void parseMessage(InputStream is) throws IOException {
+        if (raw) {
+            handler.raw(new CloseShieldInputStream(is));
+        } else {
+            handler.startMessage();
+            parseEntity(is);
+            handler.endMessage();
+        }
+    }
+
+    public boolean getPrematureEof() {
+        return prematureEof;
+    }
+
+    private void parseBodyPart(InputStream is) throws IOException {
+        if (raw) {
+            handler.raw(new CloseShieldInputStream(is));
+        } else {
+            handler.startBodyPart();
+            parseEntity(is);
+            handler.endBodyPart();
+        }
+    }
+
+    /**
+     * Parses a header.
+     *
+     * @param is the stream to parse.
+     * @return a <code>BodyDescriptor</code> describing the body following
+     *         the header.
+     */
+    private BodyDescriptor parseHeader(InputStream is) throws IOException {
+        BodyDescriptor bd = new BodyDescriptor(bodyDescriptors.isEmpty()
+                        ? null : (BodyDescriptor) bodyDescriptors.getFirst());
+
+        handler.startHeader();
+
+        int lineNumber = rootStream.getLineNumber();
+
+        StringBuffer sb = new StringBuffer();
+        int curr = 0;
+        int prev = 0;
+        while ((curr = is.read()) != -1) {
+            if (curr == '\n' && (prev == '\n' || prev == 0)) {
+                /*
+                 * [\r]\n[\r]\n or an immediate \r\n have been seen.
+                 */
+                sb.deleteCharAt(sb.length() - 1);
+                break;
+            }
+            sb.append((char) curr);
+            prev = curr == '\r' ? prev : curr;
+        }
+
+//        if (curr == -1 && log.isWarnEnabled()) {
+//            log.warn("Line " + rootStream.getLineNumber()
+//                    + ": Unexpected end of headers detected. "
+//                    + "Boundary detected in header or EOF reached.");
+//        }
+
+        int start = 0;
+        int pos = 0;
+        int startLineNumber = lineNumber;
+        while (pos < sb.length()) {
+            while (pos < sb.length() && sb.charAt(pos) != '\r') {
+                pos++;
+            }
+            if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {
+                pos++;
+                continue;
+            }
+
+            if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {
+
+                /*
+                 * field should be the complete field data excluding the
+                 * trailing \r\n.
+                 */
+                String field = sb.substring(start, pos);
+                start = pos + 2;
+
+                /*
+                 * Check for a valid field.
+                 */
+                int index = field.indexOf(':');
+                boolean valid = false;
+                if (index != -1 && fieldChars.get(field.charAt(0))) {
+                    valid = true;
+                    String fieldName = field.substring(0, index).trim();
+                    for (int i = 0; i < fieldName.length(); i++) {
+                        if (!fieldChars.get(fieldName.charAt(i))) {
+                            valid = false;
+                            break;
+                        }
+                    }
+
+                    if (valid) {
+                        handler.field(field);
+                        bd.addField(fieldName, field.substring(index + 1));
+                    }
+                }
+
+                if (!valid && log.isWarnEnabled()) {
+                    log.warn("Line " + startLineNumber
+                            + ": Ignoring invalid field: '" + field.trim() + "'");
+                }
+
+                startLineNumber = lineNumber;
+            }
+
+            pos += 2;
+            lineNumber++;
+        }
+
+        handler.endHeader();
+
+        return bd;
+    }
+
+    /**
+     * Sets the <code>ContentHandler</code> to use when reporting
+     * parsing events.
+     *
+     * @param h the <code>ContentHandler</code>.
+     */
+    public void setContentHandler(ContentHandler h) {
+        this.handler = h;
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/RootInputStream.java b/src/org/apache/james/mime4j/RootInputStream.java
index fa848df..ebf36dc 100644
--- a/src/org/apache/james/mime4j/RootInputStream.java
+++ b/src/org/apache/james/mime4j/RootInputStream.java
@@ -1,111 +1,111 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-

-/**

- * <code>InputStream</code> used by the parser to wrap the original user

- * supplied stream. This stream keeps track of the current line number and

- * can also be truncated. When truncated the stream will appear to have

- * reached end of file. This is used by the parser's 

- * {@link org.apache.james.mime4j.MimeStreamParser#stop()} method.

- *

- * 

- * @version $Id: RootInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $

- */

-class RootInputStream extends InputStream {

-    private InputStream is = null;

-    private int lineNumber = 1;

-    private int prev = -1;

-    private boolean truncated = false;

-

-    /**

-     * Creates a new <code>RootInputStream</code>.

-     * 

-     * @param in the stream to read from.

-     */

-    public RootInputStream(InputStream is) {

-        this.is = is;

-    }

-

-    /**

-     * Gets the current line number starting at 1 

-     * (the number of <code>\r\n</code> read so far plus 1).

-     * 

-     * @return the current line number.

-     */

-    public int getLineNumber() {

-        return lineNumber;

-    }

-    

-    /**

-     * Truncates this <code>InputStream</code>. After this call any 

-     * call to {@link #read()}, {@link #read(byte[]) or 

-     * {@link #read(byte[], int, int)} will return

-     * -1 as if end-of-file had been reached.

-     */

-    public void truncate() {

-        this.truncated = true;

-    }

-    

-    /**

-     * @see java.io.InputStream#read()

-     */

-    public int read() throws IOException {

-        if (truncated) {

-            return -1;

-        }

-        

-        int b = is.read();

-        if (prev == '\r' && b == '\n') {

-            lineNumber++;

-        }

-        prev = b;

-        return b;

-    }

-    

-    /**

-     * 

-     * @see java.io.InputStream#read(byte[], int, int)

-     */

-    public int read(byte[] b, int off, int len) throws IOException {

-        if (truncated) {

-            return -1;

-        }

-        

-        int n = is.read(b, off, len);

-        for (int i = off; i < off + n; i++) {

-            if (prev == '\r' && b[i] == '\n') {

-                lineNumber++;

-            }

-            prev = b[i];

-        }

-        return n;

-    }

-    

-    /**

-     * @see java.io.InputStream#read(byte[])

-     */

-    public int read(byte[] b) throws IOException {

-        return read(b, 0, b.length);

-    }

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+
+/**
+ * <code>InputStream</code> used by the parser to wrap the original user
+ * supplied stream. This stream keeps track of the current line number and
+ * can also be truncated. When truncated the stream will appear to have
+ * reached end of file. This is used by the parser's 
+ * {@link org.apache.james.mime4j.MimeStreamParser#stop()} method.
+ *
+ * 
+ * @version $Id: RootInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
+ */
+class RootInputStream extends InputStream {
+    private InputStream is = null;
+    private int lineNumber = 1;
+    private int prev = -1;
+    private boolean truncated = false;
+
+    /**
+     * Creates a new <code>RootInputStream</code>.
+     * 
+     * @param in the stream to read from.
+     */
+    public RootInputStream(InputStream is) {
+        this.is = is;
+    }
+
+    /**
+     * Gets the current line number starting at 1 
+     * (the number of <code>\r\n</code> read so far plus 1).
+     * 
+     * @return the current line number.
+     */
+    public int getLineNumber() {
+        return lineNumber;
+    }
+    
+    /**
+     * Truncates this <code>InputStream</code>. After this call any 
+     * call to {@link #read()}, {@link #read(byte[]) or 
+     * {@link #read(byte[], int, int)} will return
+     * -1 as if end-of-file had been reached.
+     */
+    public void truncate() {
+        this.truncated = true;
+    }
+    
+    /**
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        if (truncated) {
+            return -1;
+        }
+        
+        int b = is.read();
+        if (prev == '\r' && b == '\n') {
+            lineNumber++;
+        }
+        prev = b;
+        return b;
+    }
+    
+    /**
+     * 
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (truncated) {
+            return -1;
+        }
+        
+        int n = is.read(b, off, len);
+        for (int i = off; i < off + n; i++) {
+            if (prev == '\r' && b[i] == '\n') {
+                lineNumber++;
+            }
+            prev = b[i];
+        }
+        return n;
+    }
+    
+    /**
+     * @see java.io.InputStream#read(byte[])
+     */
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+}
diff --git a/src/org/apache/james/mime4j/SimpleContentHandler.java b/src/org/apache/james/mime4j/SimpleContentHandler.java
index 13f1fd2..7d25d08 100644
--- a/src/org/apache/james/mime4j/SimpleContentHandler.java
+++ b/src/org/apache/james/mime4j/SimpleContentHandler.java
@@ -1,100 +1,100 @@
-/****************************************************************

- * 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 org.apache.james.mime4j.decoder.Base64InputStream;

-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;

-import org.apache.james.mime4j.field.Field;

-import org.apache.james.mime4j.message.Header;

-

-import java.io.InputStream;

-import java.io.IOException;

-

-/**

- * Abstract implementation of ContentHandler that automates common

- * tasks. Currently performs header parsing and applies content-transfer

- * decoding to body parts.

- *

- * 

- */

-public abstract class SimpleContentHandler extends  AbstractContentHandler {

-

-    /**

-     * Called after headers are parsed.

-     */

-    public abstract void headers(Header header);

-

-    /**

-     * Called when the body of a discrete (non-multipart) entity is encountered.

-

-     * @param bd encapsulates the values (either read from the

-     *        message stream or, if not present, determined implictly

-     *        as described in the

-     *        MIME rfc:s) of the <code>Content-Type</code> and

-     *        <code>Content-Transfer-Encoding</code> header fields.

-     * @param is the contents of the body. Base64 or quoted-printable

-     *        decoding will be applied transparently.

-     * @throws IOException should be thrown on I/O errors.

-     */

-    public abstract void bodyDecoded(BodyDescriptor bd, InputStream is) throws IOException;

-

-

-    /* Implement introduced callbacks. */

-

-    private Header currHeader;

-

-    /**

-     * @see org.apache.james.mime4j.AbstractContentHandler#startHeader()

-     */

-    public final void startHeader() {

-        currHeader = new Header();

-    }

-

-    /**

-     * @see org.apache.james.mime4j.AbstractContentHandler#field(java.lang.String)

-     */

-    public final void field(String fieldData) {

-        currHeader.addField(Field.parse(fieldData));

-    }

-

-    /**

-     * @see org.apache.james.mime4j.AbstractContentHandler#endHeader()

-     */

-    public final void endHeader() {

-        Header tmp = currHeader;

-        currHeader = null;

-        headers(tmp);

-    }

-

-    /**

-     * @see org.apache.james.mime4j.AbstractContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)

-     */

-    public final void body(BodyDescriptor bd, InputStream is) throws IOException {

-        if (bd.isBase64Encoded()) {

-            bodyDecoded(bd, new Base64InputStream(is));

-        }

-        else if (bd.isQuotedPrintableEncoded()) {

-            bodyDecoded(bd, new QuotedPrintableInputStream(is));

-        }

-        else {

-            bodyDecoded(bd, is);

-        }

-    }

-}

+/****************************************************************
+ * 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 org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+import org.apache.james.mime4j.field.Field;
+import org.apache.james.mime4j.message.Header;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Abstract implementation of ContentHandler that automates common
+ * tasks. Currently performs header parsing and applies content-transfer
+ * decoding to body parts.
+ *
+ * 
+ */
+public abstract class SimpleContentHandler extends  AbstractContentHandler {
+
+    /**
+     * Called after headers are parsed.
+     */
+    public abstract void headers(Header header);
+
+    /**
+     * Called when the body of a discrete (non-multipart) entity is encountered.
+
+     * @param bd encapsulates the values (either read from the
+     *        message stream or, if not present, determined implictly
+     *        as described in the
+     *        MIME rfc:s) of the <code>Content-Type</code> and
+     *        <code>Content-Transfer-Encoding</code> header fields.
+     * @param is the contents of the body. Base64 or quoted-printable
+     *        decoding will be applied transparently.
+     * @throws IOException should be thrown on I/O errors.
+     */
+    public abstract void bodyDecoded(BodyDescriptor bd, InputStream is) throws IOException;
+
+
+    /* Implement introduced callbacks. */
+
+    private Header currHeader;
+
+    /**
+     * @see org.apache.james.mime4j.AbstractContentHandler#startHeader()
+     */
+    public final void startHeader() {
+        currHeader = new Header();
+    }
+
+    /**
+     * @see org.apache.james.mime4j.AbstractContentHandler#field(java.lang.String)
+     */
+    public final void field(String fieldData) {
+        currHeader.addField(Field.parse(fieldData));
+    }
+
+    /**
+     * @see org.apache.james.mime4j.AbstractContentHandler#endHeader()
+     */
+    public final void endHeader() {
+        Header tmp = currHeader;
+        currHeader = null;
+        headers(tmp);
+    }
+
+    /**
+     * @see org.apache.james.mime4j.AbstractContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
+     */
+    public final void body(BodyDescriptor bd, InputStream is) throws IOException {
+        if (bd.isBase64Encoded()) {
+            bodyDecoded(bd, new Base64InputStream(is));
+        }
+        else if (bd.isQuotedPrintableEncoded()) {
+            bodyDecoded(bd, new QuotedPrintableInputStream(is));
+        }
+        else {
+            bodyDecoded(bd, is);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/decoder/Base64InputStream.java b/src/org/apache/james/mime4j/decoder/Base64InputStream.java
index 2461ed5..77f5d7d 100644
--- a/src/org/apache/james/mime4j/decoder/Base64InputStream.java
+++ b/src/org/apache/james/mime4j/decoder/Base64InputStream.java
@@ -1,151 +1,151 @@
-/****************************************************************

- * 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.                                           *

- ****************************************************************/

-

-/**

- * Modified to improve efficiency by Android   21-Aug-2009

- */

-

-package org.apache.james.mime4j.decoder;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * Performs Base-64 decoding on an underlying stream.

- * 

- * 

- * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $

- */

-public class Base64InputStream extends InputStream {

-    private final InputStream s;

-    private int outCount = 0;

-    private int outIndex = 0;

-    private final int[] outputBuffer = new int[3];

-    private final byte[] inputBuffer = new byte[4];

-    private boolean done = false;

-

-    public Base64InputStream(InputStream s) {

-        this.s = s;

-    }

-

-    /**

-     * Closes the underlying stream.

-     * 

-     * @throws IOException on I/O errors.

-     */

-    @Override

-    public void close() throws IOException {

-        s.close();

-    }

-    

-    @Override

-    public int read() throws IOException {

-        if (outIndex == outCount) {

-            fillBuffer();

-            if (outIndex == outCount) {

-                return -1;

-            }

-        }

-

-        return outputBuffer[outIndex++];

-    }

-

-    /**

-     * Retrieve data from the underlying stream, decode it,

-     * and put the results in the byteq.

-     * @throws IOException

-     */

-    private void fillBuffer() throws IOException {

-        outCount = 0;

-        outIndex = 0;

-        int inCount = 0;

-

-        int i;

-        // "done" is needed for the two successive '=' at the end

-        while (!done) {

-            switch (i = s.read()) {

-                case -1:

-                    // No more input - just return, let outputBuffer drain out, and be done

-                    return;

-                case '=':

-                    // once we meet the first '=', avoid reading the second '='

-                    done = true;

-                    decodeAndEnqueue(inCount);

-                    return;

-                default:

-                    byte sX = TRANSLATION[i];

-                    if (sX < 0) continue;

-                    inputBuffer[inCount++] = sX;

-                    if (inCount == 4) {

-                        decodeAndEnqueue(inCount);

-                        return;

-                    }

-                    break;

-            }

-        }

-    }

-

-    private void decodeAndEnqueue(int len) {

-        int accum = 0;

-        accum |= inputBuffer[0] << 18;

-        accum |= inputBuffer[1] << 12;

-        accum |= inputBuffer[2] << 6;

-        accum |= inputBuffer[3];

-

-        // There's a bit of duplicated code here because we want to have straight-through operation

-        // for the most common case of len==4

-        if (len == 4) {

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outputBuffer[1] = (accum >> 8) & 0xFF;

-            outputBuffer[2] = (accum) & 0xFF;

-            outCount = 3;

-            return;

-        } else if (len == 3) {

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outputBuffer[1] = (accum >> 8) & 0xFF;

-            outCount = 2;

-            return;

-        } else {    // len == 2

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outCount = 1;

-            return;

-        }

-    }

-

-    private static byte[] TRANSLATION = {

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */

-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */

-        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */

-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */

-        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */

-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1     /* 0xF0 */

-    };

-

-

-}

+/****************************************************************
+ * 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.                                           *
+ ****************************************************************/
+
+/**
+ * Modified to improve efficiency by Android   21-Aug-2009
+ */
+
+package org.apache.james.mime4j.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Performs Base-64 decoding on an underlying stream.
+ * 
+ * 
+ * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
+ */
+public class Base64InputStream extends InputStream {
+    private final InputStream s;
+    private int outCount = 0;
+    private int outIndex = 0;
+    private final int[] outputBuffer = new int[3];
+    private final byte[] inputBuffer = new byte[4];
+    private boolean done = false;
+
+    public Base64InputStream(InputStream s) {
+        this.s = s;
+    }
+
+    /**
+     * Closes the underlying stream.
+     * 
+     * @throws IOException on I/O errors.
+     */
+    @Override
+    public void close() throws IOException {
+        s.close();
+    }
+    
+    @Override
+    public int read() throws IOException {
+        if (outIndex == outCount) {
+            fillBuffer();
+            if (outIndex == outCount) {
+                return -1;
+            }
+        }
+
+        return outputBuffer[outIndex++];
+    }
+
+    /**
+     * Retrieve data from the underlying stream, decode it,
+     * and put the results in the byteq.
+     * @throws IOException
+     */
+    private void fillBuffer() throws IOException {
+        outCount = 0;
+        outIndex = 0;
+        int inCount = 0;
+
+        int i;
+        // "done" is needed for the two successive '=' at the end
+        while (!done) {
+            switch (i = s.read()) {
+                case -1:
+                    // No more input - just return, let outputBuffer drain out, and be done
+                    return;
+                case '=':
+                    // once we meet the first '=', avoid reading the second '='
+                    done = true;
+                    decodeAndEnqueue(inCount);
+                    return;
+                default:
+                    byte sX = TRANSLATION[i];
+                    if (sX < 0) continue;
+                    inputBuffer[inCount++] = sX;
+                    if (inCount == 4) {
+                        decodeAndEnqueue(inCount);
+                        return;
+                    }
+                    break;
+            }
+        }
+    }
+
+    private void decodeAndEnqueue(int len) {
+        int accum = 0;
+        accum |= inputBuffer[0] << 18;
+        accum |= inputBuffer[1] << 12;
+        accum |= inputBuffer[2] << 6;
+        accum |= inputBuffer[3];
+
+        // There's a bit of duplicated code here because we want to have straight-through operation
+        // for the most common case of len==4
+        if (len == 4) {
+            outputBuffer[0] = (accum >> 16) & 0xFF;
+            outputBuffer[1] = (accum >> 8) & 0xFF;
+            outputBuffer[2] = (accum) & 0xFF;
+            outCount = 3;
+            return;
+        } else if (len == 3) {
+            outputBuffer[0] = (accum >> 16) & 0xFF;
+            outputBuffer[1] = (accum >> 8) & 0xFF;
+            outCount = 2;
+            return;
+        } else {    // len == 2
+            outputBuffer[0] = (accum >> 16) & 0xFF;
+            outCount = 1;
+            return;
+        }
+    }
+
+    private static byte[] TRANSLATION = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
+        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
+        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1     /* 0xF0 */
+    };
+
+
+}
diff --git a/src/org/apache/james/mime4j/decoder/ByteQueue.java b/src/org/apache/james/mime4j/decoder/ByteQueue.java
index 68e7d33..6d7ccef 100644
--- a/src/org/apache/james/mime4j/decoder/ByteQueue.java
+++ b/src/org/apache/james/mime4j/decoder/ByteQueue.java
@@ -1,62 +1,62 @@
-/****************************************************************

- * 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.decoder;

-

-import java.util.Iterator;

-

-public class ByteQueue {

-

-    private UnboundedFifoByteBuffer buf;

-    private int initialCapacity = -1;

-

-    public ByteQueue() {

-        buf = new UnboundedFifoByteBuffer();

-    }

-

-    public ByteQueue(int initialCapacity) {

-        buf = new UnboundedFifoByteBuffer(initialCapacity);

-        this.initialCapacity = initialCapacity;

-    }

-

-    public void enqueue(byte b) {

-        buf.add(b);

-    }

-

-    public byte dequeue() {

-        return buf.remove();

-    }

-

-    public int count() {

-        return buf.size();

-    }

-

-    public void clear() {

-        if (initialCapacity != -1)

-            buf = new UnboundedFifoByteBuffer(initialCapacity);

-        else

-            buf = new UnboundedFifoByteBuffer();

-    }

-

-    public Iterator iterator() {

-        return buf.iterator();

-    }

-

-

-}

+/****************************************************************
+ * 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.decoder;
+
+import java.util.Iterator;
+
+public class ByteQueue {
+
+    private UnboundedFifoByteBuffer buf;
+    private int initialCapacity = -1;
+
+    public ByteQueue() {
+        buf = new UnboundedFifoByteBuffer();
+    }
+
+    public ByteQueue(int initialCapacity) {
+        buf = new UnboundedFifoByteBuffer(initialCapacity);
+        this.initialCapacity = initialCapacity;
+    }
+
+    public void enqueue(byte b) {
+        buf.add(b);
+    }
+
+    public byte dequeue() {
+        return buf.remove();
+    }
+
+    public int count() {
+        return buf.size();
+    }
+
+    public void clear() {
+        if (initialCapacity != -1)
+            buf = new UnboundedFifoByteBuffer(initialCapacity);
+        else
+            buf = new UnboundedFifoByteBuffer();
+    }
+
+    public Iterator iterator() {
+        return buf.iterator();
+    }
+
+
+}
diff --git a/src/org/apache/james/mime4j/decoder/DecoderUtil.java b/src/org/apache/james/mime4j/decoder/DecoderUtil.java
index 551be0f..8a5dfb4 100644
--- a/src/org/apache/james/mime4j/decoder/DecoderUtil.java
+++ b/src/org/apache/james/mime4j/decoder/DecoderUtil.java
@@ -1,279 +1,279 @@
-/****************************************************************

- * 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.decoder;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.util.CharsetUtil;

-

-import java.io.ByteArrayInputStream;

-import java.io.ByteArrayOutputStream;

-import java.io.IOException;

-import java.io.UnsupportedEncodingException;

-

-/**

- * Static methods for decoding strings, byte arrays and encoded words.

- *

- * 

- * @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $

- */

-public class DecoderUtil {

-    private static Log log = LogFactory.getLog(DecoderUtil.class);

-    

-    /**

-     * Decodes a string containing quoted-printable encoded data. 

-     * 

-     * @param s the string to decode.

-     * @return the decoded bytes.

-     */

-    public static byte[] decodeBaseQuotedPrintable(String s) {

-        ByteArrayOutputStream baos = new ByteArrayOutputStream();

-        

-        try {

-            byte[] bytes = s.getBytes("US-ASCII");

-            

-            QuotedPrintableInputStream is = new QuotedPrintableInputStream(

-                                               new ByteArrayInputStream(bytes));

-            

-            int b = 0;

-            while ((b = is.read()) != -1) {

-                baos.write(b);

-            }

-        } catch (IOException e) {

-            /*

-             * This should never happen!

-             */

-            log.error(e);

-        }

-        

-        return baos.toByteArray();

-    }

-    

-    /**

-     * Decodes a string containing base64 encoded data. 

-     * 

-     * @param s the string to decode.

-     * @return the decoded bytes.

-     */

-    public static byte[] decodeBase64(String s) {

-        ByteArrayOutputStream baos = new ByteArrayOutputStream();

-        

-        try {

-            byte[] bytes = s.getBytes("US-ASCII");

-            

-            Base64InputStream is = new Base64InputStream(

-                                        new ByteArrayInputStream(bytes));

-            

-            int b = 0;

-            while ((b = is.read()) != -1) {

-                baos.write(b);

-            }

-        } catch (IOException e) {

-            /*

-             * This should never happen!

-             */

-            log.error(e);

-        }

-        

-        return baos.toByteArray();

-    }

-    

-    /**

-     * Decodes an encoded word encoded with the 'B' encoding (described in 

-     * RFC 2047) found in a header field body.

-     * 

-     * @param encodedWord the encoded word to decode.

-     * @param charset the Java charset to use.

-     * @return the decoded string.

-     * @throws UnsupportedEncodingException if the given Java charset isn't 

-     *         supported.

-     */

-    public static String decodeB(String encodedWord, String charset) 

-            throws UnsupportedEncodingException {

-        

-        return new String(decodeBase64(encodedWord), charset);

-    }

-    

-    /**

-     * Decodes an encoded word encoded with the 'Q' encoding (described in 

-     * RFC 2047) found in a header field body.

-     * 

-     * @param encodedWord the encoded word to decode.

-     * @param charset the Java charset to use.

-     * @return the decoded string.

-     * @throws UnsupportedEncodingException if the given Java charset isn't 

-     *         supported.

-     */

-    public static String decodeQ(String encodedWord, String charset)

-            throws UnsupportedEncodingException {

-           

-        /*

-         * Replace _ with =20

-         */

-        StringBuffer sb = new StringBuffer();

-        for (int i = 0; i < encodedWord.length(); i++) {

-            char c = encodedWord.charAt(i);

-            if (c == '_') {

-                sb.append("=20");

-            } else {

-                sb.append(c);

-            }

-        }

-        

-        return new String(decodeBaseQuotedPrintable(sb.toString()), charset);

-    }

-    

-    /**

-     * Decodes a string containing encoded words as defined by RFC 2047.

-     * Encoded words in have the form 

-     * =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for 

-     * quoted-printable and 'B' or 'b' for Base64.

-     * 

-     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J

-     * 

-     * @param body the string to decode.

-     * @return the decoded string.

-     */

-    public static String decodeEncodedWords(String body) {

-        

-        // ANDROID:  Most strings will not include "=?" so a quick test can prevent unneeded

-        // object creation.  This could also be handled via lazy creation of the StringBuilder.

-        if (body.indexOf("=?") == -1) {

-            return body;

-        }

-        

-        int previousEnd = 0;

-        boolean previousWasEncoded = false;

-

-        StringBuilder sb = new StringBuilder();

-

-        while (true) {

-            int begin = body.indexOf("=?", previousEnd);

-            

-            // ANDROID:  The mime4j original version has an error here.  It gets confused if

-            // the encoded string begins with an '=' (just after "?Q?").  This patch seeks forward

-            // to find the two '?' in the "header", before looking for the final "?=".

-            int endScan = begin + 2;

-            if (begin != -1) {

-                int qm1 = body.indexOf('?', endScan + 2);

-                int qm2 = body.indexOf('?', qm1 + 1);

-                if (qm2 != -1) {

-                    endScan = qm2 + 1;

-                }

-            }

-            

-            int end = begin == -1 ? -1 : body.indexOf("?=", endScan);

-            if (end == -1) {

-                if (previousEnd == 0)

-                    return body;

-

-                sb.append(body.substring(previousEnd));

-                return sb.toString();

-            }

-            end += 2;

-

-            String sep = body.substring(previousEnd, begin);

-

-            String decoded = decodeEncodedWord(body, begin, end);

-            if (decoded == null) {

-                sb.append(sep);

-                sb.append(body.substring(begin, end));

-            } else {

-                if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) {

-                    sb.append(sep);

-                }

-                sb.append(decoded);

-            }

-

-            previousEnd = end;

-            previousWasEncoded = decoded != null;

-        }

-    }

-

-    // return null on error

-    private static String decodeEncodedWord(String body, int begin, int end) {

-        int qm1 = body.indexOf('?', begin + 2);

-        if (qm1 == end - 2)

-            return null;

-

-        int qm2 = body.indexOf('?', qm1 + 1);

-        if (qm2 == end - 2)

-            return null;

-

-        String mimeCharset = body.substring(begin + 2, qm1);

-        String encoding = body.substring(qm1 + 1, qm2);

-        String encodedText = body.substring(qm2 + 1, end - 2);

-

-        String charset = CharsetUtil.toJavaCharset(mimeCharset);

-        if (charset == null) {

-            if (log.isWarnEnabled()) {

-                log.warn("MIME charset '" + mimeCharset + "' in encoded word '"

-                        + body.substring(begin, end) + "' doesn't have a "

-                        + "corresponding Java charset");

-            }

-            return null;

-        } else if (!CharsetUtil.isDecodingSupported(charset)) {

-            if (log.isWarnEnabled()) {

-                log.warn("Current JDK doesn't support decoding of charset '"

-                        + charset + "' (MIME charset '" + mimeCharset

-                        + "' in encoded word '" + body.substring(begin, end)

-                        + "')");

-            }

-            return null;

-        }

-

-        if (encodedText.length() == 0) {

-            if (log.isWarnEnabled()) {

-                log.warn("Missing encoded text in encoded word: '"

-                        + body.substring(begin, end) + "'");

-            }

-            return null;

-        }

-

-        try {

-            if (encoding.equalsIgnoreCase("Q")) {

-                return DecoderUtil.decodeQ(encodedText, charset);

-            } else if (encoding.equalsIgnoreCase("B")) {

-                return DecoderUtil.decodeB(encodedText, charset);

-            } else {

-                if (log.isWarnEnabled()) {

-                    log.warn("Warning: Unknown encoding in encoded word '"

-                            + body.substring(begin, end) + "'");

-                }

-                return null;

-            }

-        } catch (UnsupportedEncodingException e) {

-            // should not happen because of isDecodingSupported check above

-            if (log.isWarnEnabled()) {

-                log.warn("Unsupported encoding in encoded word '"

-                        + body.substring(begin, end) + "'", e);

-            }

-            return null;

-        } catch (RuntimeException e) {

-            if (log.isWarnEnabled()) {

-                log.warn("Could not decode encoded word '"

-                        + body.substring(begin, end) + "'", e);

-            }

-            return null;

-        }

-    }

-}

+/****************************************************************
+ * 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.decoder;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.CharsetUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Static methods for decoding strings, byte arrays and encoded words.
+ *
+ * 
+ * @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $
+ */
+public class DecoderUtil {
+    private static Log log = LogFactory.getLog(DecoderUtil.class);
+    
+    /**
+     * Decodes a string containing quoted-printable encoded data. 
+     * 
+     * @param s the string to decode.
+     * @return the decoded bytes.
+     */
+    public static byte[] decodeBaseQuotedPrintable(String s) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        
+        try {
+            byte[] bytes = s.getBytes("US-ASCII");
+            
+            QuotedPrintableInputStream is = new QuotedPrintableInputStream(
+                                               new ByteArrayInputStream(bytes));
+            
+            int b = 0;
+            while ((b = is.read()) != -1) {
+                baos.write(b);
+            }
+        } catch (IOException e) {
+            /*
+             * This should never happen!
+             */
+            log.error(e);
+        }
+        
+        return baos.toByteArray();
+    }
+    
+    /**
+     * Decodes a string containing base64 encoded data. 
+     * 
+     * @param s the string to decode.
+     * @return the decoded bytes.
+     */
+    public static byte[] decodeBase64(String s) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        
+        try {
+            byte[] bytes = s.getBytes("US-ASCII");
+            
+            Base64InputStream is = new Base64InputStream(
+                                        new ByteArrayInputStream(bytes));
+            
+            int b = 0;
+            while ((b = is.read()) != -1) {
+                baos.write(b);
+            }
+        } catch (IOException e) {
+            /*
+             * This should never happen!
+             */
+            log.error(e);
+        }
+        
+        return baos.toByteArray();
+    }
+    
+    /**
+     * Decodes an encoded word encoded with the 'B' encoding (described in 
+     * RFC 2047) found in a header field body.
+     * 
+     * @param encodedWord the encoded word to decode.
+     * @param charset the Java charset to use.
+     * @return the decoded string.
+     * @throws UnsupportedEncodingException if the given Java charset isn't 
+     *         supported.
+     */
+    public static String decodeB(String encodedWord, String charset) 
+            throws UnsupportedEncodingException {
+        
+        return new String(decodeBase64(encodedWord), charset);
+    }
+    
+    /**
+     * Decodes an encoded word encoded with the 'Q' encoding (described in 
+     * RFC 2047) found in a header field body.
+     * 
+     * @param encodedWord the encoded word to decode.
+     * @param charset the Java charset to use.
+     * @return the decoded string.
+     * @throws UnsupportedEncodingException if the given Java charset isn't 
+     *         supported.
+     */
+    public static String decodeQ(String encodedWord, String charset)
+            throws UnsupportedEncodingException {
+           
+        /*
+         * Replace _ with =20
+         */
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < encodedWord.length(); i++) {
+            char c = encodedWord.charAt(i);
+            if (c == '_') {
+                sb.append("=20");
+            } else {
+                sb.append(c);
+            }
+        }
+        
+        return new String(decodeBaseQuotedPrintable(sb.toString()), charset);
+    }
+    
+    /**
+     * Decodes a string containing encoded words as defined by RFC 2047.
+     * Encoded words in have the form 
+     * =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for 
+     * quoted-printable and 'B' or 'b' for Base64.
+     * 
+     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J
+     * 
+     * @param body the string to decode.
+     * @return the decoded string.
+     */
+    public static String decodeEncodedWords(String body) {
+        
+        // ANDROID:  Most strings will not include "=?" so a quick test can prevent unneeded
+        // object creation.  This could also be handled via lazy creation of the StringBuilder.
+        if (body.indexOf("=?") == -1) {
+            return body;
+        }
+        
+        int previousEnd = 0;
+        boolean previousWasEncoded = false;
+
+        StringBuilder sb = new StringBuilder();
+
+        while (true) {
+            int begin = body.indexOf("=?", previousEnd);
+            
+            // ANDROID:  The mime4j original version has an error here.  It gets confused if
+            // the encoded string begins with an '=' (just after "?Q?").  This patch seeks forward
+            // to find the two '?' in the "header", before looking for the final "?=".
+            int endScan = begin + 2;
+            if (begin != -1) {
+                int qm1 = body.indexOf('?', endScan + 2);
+                int qm2 = body.indexOf('?', qm1 + 1);
+                if (qm2 != -1) {
+                    endScan = qm2 + 1;
+                }
+            }
+            
+            int end = begin == -1 ? -1 : body.indexOf("?=", endScan);
+            if (end == -1) {
+                if (previousEnd == 0)
+                    return body;
+
+                sb.append(body.substring(previousEnd));
+                return sb.toString();
+            }
+            end += 2;
+
+            String sep = body.substring(previousEnd, begin);
+
+            String decoded = decodeEncodedWord(body, begin, end);
+            if (decoded == null) {
+                sb.append(sep);
+                sb.append(body.substring(begin, end));
+            } else {
+                if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) {
+                    sb.append(sep);
+                }
+                sb.append(decoded);
+            }
+
+            previousEnd = end;
+            previousWasEncoded = decoded != null;
+        }
+    }
+
+    // return null on error
+    private static String decodeEncodedWord(String body, int begin, int end) {
+        int qm1 = body.indexOf('?', begin + 2);
+        if (qm1 == end - 2)
+            return null;
+
+        int qm2 = body.indexOf('?', qm1 + 1);
+        if (qm2 == end - 2)
+            return null;
+
+        String mimeCharset = body.substring(begin + 2, qm1);
+        String encoding = body.substring(qm1 + 1, qm2);
+        String encodedText = body.substring(qm2 + 1, end - 2);
+
+        String charset = CharsetUtil.toJavaCharset(mimeCharset);
+        if (charset == null) {
+            if (log.isWarnEnabled()) {
+                log.warn("MIME charset '" + mimeCharset + "' in encoded word '"
+                        + body.substring(begin, end) + "' doesn't have a "
+                        + "corresponding Java charset");
+            }
+            return null;
+        } else if (!CharsetUtil.isDecodingSupported(charset)) {
+            if (log.isWarnEnabled()) {
+                log.warn("Current JDK doesn't support decoding of charset '"
+                        + charset + "' (MIME charset '" + mimeCharset
+                        + "' in encoded word '" + body.substring(begin, end)
+                        + "')");
+            }
+            return null;
+        }
+
+        if (encodedText.length() == 0) {
+            if (log.isWarnEnabled()) {
+                log.warn("Missing encoded text in encoded word: '"
+                        + body.substring(begin, end) + "'");
+            }
+            return null;
+        }
+
+        try {
+            if (encoding.equalsIgnoreCase("Q")) {
+                return DecoderUtil.decodeQ(encodedText, charset);
+            } else if (encoding.equalsIgnoreCase("B")) {
+                return DecoderUtil.decodeB(encodedText, charset);
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn("Warning: Unknown encoding in encoded word '"
+                            + body.substring(begin, end) + "'");
+                }
+                return null;
+            }
+        } catch (UnsupportedEncodingException e) {
+            // should not happen because of isDecodingSupported check above
+            if (log.isWarnEnabled()) {
+                log.warn("Unsupported encoding in encoded word '"
+                        + body.substring(begin, end) + "'", e);
+            }
+            return null;
+        } catch (RuntimeException e) {
+            if (log.isWarnEnabled()) {
+                log.warn("Could not decode encoded word '"
+                        + body.substring(begin, end) + "'", e);
+            }
+            return null;
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java b/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java
index cbcb226..e43f398 100644
--- a/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java
+++ b/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java
@@ -1,229 +1,229 @@
-/****************************************************************

- * 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.decoder;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-

-/**

- * Performs Quoted-Printable decoding on an underlying stream.

- * 

- * 

- * 

- * @version $Id: QuotedPrintableInputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $

- */

-public class QuotedPrintableInputStream extends InputStream {

-    private static Log log = LogFactory.getLog(QuotedPrintableInputStream.class);

-    

-    private InputStream stream;

-    ByteQueue byteq = new ByteQueue();

-    ByteQueue pushbackq = new ByteQueue();

-    private byte state = 0;

-

-    public QuotedPrintableInputStream(InputStream stream) {

-        this.stream = stream;

-    }

-    

-    /**

-     * Closes the underlying stream.

-     * 

-     * @throws IOException on I/O errors.

-     */

-    public void close() throws IOException {

-        stream.close();

-    }

-

-    public int read() throws IOException {

-        fillBuffer();

-        if (byteq.count() == 0)

-            return -1;

-        else {

-            byte val = byteq.dequeue();

-            if (val >= 0)

-                return val;

-            else

-                return val & 0xFF;

-        }

-    }

-

-    /**

-     * Pulls bytes out of the underlying stream and places them in the

-     * pushback queue.  This is necessary (vs. reading from the

-     * underlying stream directly) to detect and filter out "transport

-     * padding" whitespace, i.e., all whitespace that appears immediately

-     * before a CRLF.

-     *

-     * @throws IOException Underlying stream threw IOException.

-     */

-    private void populatePushbackQueue() throws IOException {

-        //Debug.verify(pushbackq.count() == 0, "PopulatePushbackQueue called when pushback queue was not empty!");

-

-        if (pushbackq.count() != 0)

-            return;

-

-        while (true) {

-            int i = stream.read();

-            switch (i) {

-                case -1:

-                    // stream is done

-                    pushbackq.clear();  // discard any whitespace preceding EOF

-                    return;

-                case ' ':

-                case '\t':

-                    pushbackq.enqueue((byte)i);

-                    break;

-                case '\r':

-                case '\n':

-                    pushbackq.clear();  // discard any whitespace preceding EOL

-                    pushbackq.enqueue((byte)i);

-                    return;

-                default:

-                    pushbackq.enqueue((byte)i);

-                    return;

-            }

-        }

-    }

-

-    /**

-     * Causes the pushback queue to get populated if it is empty, then

-     * consumes and decodes bytes out of it until one or more bytes are

-     * in the byte queue.  This decoding step performs the actual QP

-     * decoding.

-     *

-     * @throws IOException Underlying stream threw IOException.

-     */

-    private void fillBuffer() throws IOException {

-        byte msdChar = 0;  // first digit of escaped num

-        while (byteq.count() == 0) {

-            if (pushbackq.count() == 0) {

-                populatePushbackQueue();

-                if (pushbackq.count() == 0)

-                    return;

-            }

-

-            byte b = (byte)pushbackq.dequeue();

-

-            switch (state) {

-                case 0:  // start state, no bytes pending

-                    if (b != '=') {

-                        byteq.enqueue(b);

-                        break;  // state remains 0

-                    } else {

-                        state = 1;

-                        break;

-                    }

-                case 1:  // encountered "=" so far

-                    if (b == '\r') {

-                        state = 2;

-                        break;

-                    } else if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {

-                        state = 3;

-                        msdChar = b;  // save until next digit encountered

-                        break;

-                    } else if (b == '=') {

-                        /*

-                         * Special case when == is encountered.

-                         * Emit one = and stay in this state.

-                         */

-                        if (log.isWarnEnabled()) {

-                            log.warn("Malformed MIME; got ==");

-                        }

-                        byteq.enqueue((byte)'=');

-                        break;

-                    } else {

-                        if (log.isWarnEnabled()) {

-                            log.warn("Malformed MIME; expected \\r or "

-                                    + "[0-9A-Z], got " + b);

-                        }

-                        state = 0;

-                        byteq.enqueue((byte)'=');

-                        byteq.enqueue(b);

-                        break;

-                    }

-                case 2:  // encountered "=\r" so far

-                    if (b == '\n') {

-                        state = 0;

-                        break;

-                    } else {

-                        if (log.isWarnEnabled()) {

-                            log.warn("Malformed MIME; expected " 

-                                    + (int)'\n' + ", got " + b);

-                        }

-                        state = 0;

-                        byteq.enqueue((byte)'=');

-                        byteq.enqueue((byte)'\r');

-                        byteq.enqueue(b);

-                        break;

-                    }

-                case 3:  // encountered =<digit> so far; expecting another <digit> to complete the octet

-                    if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {

-                        byte msd = asciiCharToNumericValue(msdChar);

-                        byte low = asciiCharToNumericValue(b);

-                        state = 0;

-                        byteq.enqueue((byte)((msd << 4) | low));

-                        break;

-                    } else {

-                        if (log.isWarnEnabled()) {

-                            log.warn("Malformed MIME; expected "

-                                     + "[0-9A-Z], got " + b);

-                        }

-                        state = 0;

-                        byteq.enqueue((byte)'=');

-                        byteq.enqueue(msdChar);

-                        byteq.enqueue(b);

-                        break;

-                    }

-                default:  // should never happen

-                    log.error("Illegal state: " + state);

-                    state = 0;

-                    byteq.enqueue(b);

-                    break;

-            }

-        }

-    }

-

-    /**

-     * Converts '0' => 0, 'A' => 10, etc.

-     * @param c ASCII character value.

-     * @return Numeric value of hexadecimal character.

-     */

-    private byte asciiCharToNumericValue(byte c) {

-        if (c >= '0' && c <= '9') {

-            return (byte)(c - '0');

-        } else if (c >= 'A' && c <= 'Z') {

-            return (byte)(0xA + (c - 'A'));

-        } else if (c >= 'a' && c <= 'z') {

-            return (byte)(0xA + (c - 'a'));

-        } else {

-            /*

-             * This should never happen since all calls to this method

-             * are preceded by a check that c is in [0-9A-Za-z]

-             */

-            throw new IllegalArgumentException((char) c 

-                    + " is not a hexadecimal digit");

-        }

-    }

-

-}

+/****************************************************************
+ * 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.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * Performs Quoted-Printable decoding on an underlying stream.
+ * 
+ * 
+ * 
+ * @version $Id: QuotedPrintableInputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
+ */
+public class QuotedPrintableInputStream extends InputStream {
+    private static Log log = LogFactory.getLog(QuotedPrintableInputStream.class);
+    
+    private InputStream stream;
+    ByteQueue byteq = new ByteQueue();
+    ByteQueue pushbackq = new ByteQueue();
+    private byte state = 0;
+
+    public QuotedPrintableInputStream(InputStream stream) {
+        this.stream = stream;
+    }
+    
+    /**
+     * Closes the underlying stream.
+     * 
+     * @throws IOException on I/O errors.
+     */
+    public void close() throws IOException {
+        stream.close();
+    }
+
+    public int read() throws IOException {
+        fillBuffer();
+        if (byteq.count() == 0)
+            return -1;
+        else {
+            byte val = byteq.dequeue();
+            if (val >= 0)
+                return val;
+            else
+                return val & 0xFF;
+        }
+    }
+
+    /**
+     * Pulls bytes out of the underlying stream and places them in the
+     * pushback queue.  This is necessary (vs. reading from the
+     * underlying stream directly) to detect and filter out "transport
+     * padding" whitespace, i.e., all whitespace that appears immediately
+     * before a CRLF.
+     *
+     * @throws IOException Underlying stream threw IOException.
+     */
+    private void populatePushbackQueue() throws IOException {
+        //Debug.verify(pushbackq.count() == 0, "PopulatePushbackQueue called when pushback queue was not empty!");
+
+        if (pushbackq.count() != 0)
+            return;
+
+        while (true) {
+            int i = stream.read();
+            switch (i) {
+                case -1:
+                    // stream is done
+                    pushbackq.clear();  // discard any whitespace preceding EOF
+                    return;
+                case ' ':
+                case '\t':
+                    pushbackq.enqueue((byte)i);
+                    break;
+                case '\r':
+                case '\n':
+                    pushbackq.clear();  // discard any whitespace preceding EOL
+                    pushbackq.enqueue((byte)i);
+                    return;
+                default:
+                    pushbackq.enqueue((byte)i);
+                    return;
+            }
+        }
+    }
+
+    /**
+     * Causes the pushback queue to get populated if it is empty, then
+     * consumes and decodes bytes out of it until one or more bytes are
+     * in the byte queue.  This decoding step performs the actual QP
+     * decoding.
+     *
+     * @throws IOException Underlying stream threw IOException.
+     */
+    private void fillBuffer() throws IOException {
+        byte msdChar = 0;  // first digit of escaped num
+        while (byteq.count() == 0) {
+            if (pushbackq.count() == 0) {
+                populatePushbackQueue();
+                if (pushbackq.count() == 0)
+                    return;
+            }
+
+            byte b = (byte)pushbackq.dequeue();
+
+            switch (state) {
+                case 0:  // start state, no bytes pending
+                    if (b != '=') {
+                        byteq.enqueue(b);
+                        break;  // state remains 0
+                    } else {
+                        state = 1;
+                        break;
+                    }
+                case 1:  // encountered "=" so far
+                    if (b == '\r') {
+                        state = 2;
+                        break;
+                    } else if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
+                        state = 3;
+                        msdChar = b;  // save until next digit encountered
+                        break;
+                    } else if (b == '=') {
+                        /*
+                         * Special case when == is encountered.
+                         * Emit one = and stay in this state.
+                         */
+                        if (log.isWarnEnabled()) {
+                            log.warn("Malformed MIME; got ==");
+                        }
+                        byteq.enqueue((byte)'=');
+                        break;
+                    } else {
+                        if (log.isWarnEnabled()) {
+                            log.warn("Malformed MIME; expected \\r or "
+                                    + "[0-9A-Z], got " + b);
+                        }
+                        state = 0;
+                        byteq.enqueue((byte)'=');
+                        byteq.enqueue(b);
+                        break;
+                    }
+                case 2:  // encountered "=\r" so far
+                    if (b == '\n') {
+                        state = 0;
+                        break;
+                    } else {
+                        if (log.isWarnEnabled()) {
+                            log.warn("Malformed MIME; expected " 
+                                    + (int)'\n' + ", got " + b);
+                        }
+                        state = 0;
+                        byteq.enqueue((byte)'=');
+                        byteq.enqueue((byte)'\r');
+                        byteq.enqueue(b);
+                        break;
+                    }
+                case 3:  // encountered =<digit> so far; expecting another <digit> to complete the octet
+                    if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
+                        byte msd = asciiCharToNumericValue(msdChar);
+                        byte low = asciiCharToNumericValue(b);
+                        state = 0;
+                        byteq.enqueue((byte)((msd << 4) | low));
+                        break;
+                    } else {
+                        if (log.isWarnEnabled()) {
+                            log.warn("Malformed MIME; expected "
+                                     + "[0-9A-Z], got " + b);
+                        }
+                        state = 0;
+                        byteq.enqueue((byte)'=');
+                        byteq.enqueue(msdChar);
+                        byteq.enqueue(b);
+                        break;
+                    }
+                default:  // should never happen
+                    log.error("Illegal state: " + state);
+                    state = 0;
+                    byteq.enqueue(b);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Converts '0' => 0, 'A' => 10, etc.
+     * @param c ASCII character value.
+     * @return Numeric value of hexadecimal character.
+     */
+    private byte asciiCharToNumericValue(byte c) {
+        if (c >= '0' && c <= '9') {
+            return (byte)(c - '0');
+        } else if (c >= 'A' && c <= 'Z') {
+            return (byte)(0xA + (c - 'A'));
+        } else if (c >= 'a' && c <= 'z') {
+            return (byte)(0xA + (c - 'a'));
+        } else {
+            /*
+             * This should never happen since all calls to this method
+             * are preceded by a check that c is in [0-9A-Za-z]
+             */
+            throw new IllegalArgumentException((char) c 
+                    + " is not a hexadecimal digit");
+        }
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java b/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java
index dc32caf..f01194f 100644
--- a/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java
+++ b/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java
@@ -1,272 +1,272 @@
-/****************************************************************

- * 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.decoder;

-

-import java.util.Iterator;

-import java.util.NoSuchElementException;

-

-/**

- * UnboundedFifoByteBuffer is a very efficient buffer implementation.

- * According to performance testing, it exhibits a constant access time, but it

- * also outperforms ArrayList when used for the same purpose.

- * <p>

- * The removal order of an <code>UnboundedFifoByteBuffer</code> is based on the insertion

- * order; elements are removed in the same order in which they were added.

- * The iteration order is the same as the removal order.

- * <p>

- * The {@link #remove()} and {@link #get()} operations perform in constant time.

- * The {@link #add(Object)} operation performs in amortized constant time.  All

- * other operations perform in linear time or worse.

- * <p>

- * Note that this implementation is not synchronized.  The following can be

- * used to provide synchronized access to your <code>UnboundedFifoByteBuffer</code>:

- * <pre>

- *   Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());

- * </pre>

- * <p>

- * This buffer prevents null objects from being added.

- *

- * @since Commons Collections 3.0 (previously in main package v2.1)

- * @version $Revision: 1.1 $ $Date: 2004/08/24 06:52:02 $

- *

- * 

- * 

- * 

- * 

- * 

- */

-class UnboundedFifoByteBuffer {

-

-    protected byte[] buffer;

-    protected int head;

-    protected int tail;

-

-    /**

-     * Constructs an UnboundedFifoByteBuffer with the default number of elements.

-     * It is exactly the same as performing the following:

-     *

-     * <pre>

-     *   new UnboundedFifoByteBuffer(32);

-     * </pre>

-     */

-    public UnboundedFifoByteBuffer() {

-        this(32);

-    }

-

-    /**

-     * Constructs an UnboundedFifoByteBuffer with the specified number of elements.

-     * The integer must be a positive integer.

-     *

-     * @param initialSize  the initial size of the buffer

-     * @throws IllegalArgumentException  if the size is less than 1

-     */

-    public UnboundedFifoByteBuffer(int initialSize) {

-        if (initialSize <= 0) {

-            throw new IllegalArgumentException("The size must be greater than 0");

-        }

-        buffer = new byte[initialSize + 1];

-        head = 0;

-        tail = 0;

-    }

-

-    /**

-     * Returns the number of elements stored in the buffer.

-     *

-     * @return this buffer's size

-     */

-    public int size() {

-        int size = 0;

-

-        if (tail < head) {

-            size = buffer.length - head + tail;

-        } else {

-            size = tail - head;

-        }

-

-        return size;

-    }

-

-    /**

-     * Returns true if this buffer is empty; false otherwise.

-     *

-     * @return true if this buffer is empty

-     */

-    public boolean isEmpty() {

-        return (size() == 0);

-    }

-

-    /**

-     * Adds the given element to this buffer.

-     *

-     * @param b  the byte to add

-     * @return true, always

-     */

-    public boolean add(final byte b) {

-

-        if (size() + 1 >= buffer.length) {

-            byte[] tmp = new byte[((buffer.length - 1) * 2) + 1];

-

-            int j = 0;

-            for (int i = head; i != tail;) {

-                tmp[j] = buffer[i];

-                buffer[i] = 0;

-

-                j++;

-                i++;

-                if (i == buffer.length) {

-                    i = 0;

-                }

-            }

-

-            buffer = tmp;

-            head = 0;

-            tail = j;

-        }

-

-        buffer[tail] = b;

-        tail++;

-        if (tail >= buffer.length) {

-            tail = 0;

-        }

-        return true;

-    }

-

-    /**

-     * Returns the next object in the buffer.

-     *

-     * @return the next object in the buffer

-     * @throws BufferUnderflowException  if this buffer is empty

-     */

-    public byte get() {

-        if (isEmpty()) {

-            throw new IllegalStateException("The buffer is already empty");

-        }

-

-        return buffer[head];

-    }

-

-    /**

-     * Removes the next object from the buffer

-     *

-     * @return the removed object

-     * @throws BufferUnderflowException  if this buffer is empty

-     */

-    public byte remove() {

-        if (isEmpty()) {

-            throw new IllegalStateException("The buffer is already empty");

-        }

-

-        byte element = buffer[head];

-

-        head++;

-        if (head >= buffer.length) {

-            head = 0;

-        }

-

-        return element;

-    }

-

-    /**

-     * Increments the internal index.

-     *

-     * @param index  the index to increment

-     * @return the updated index

-     */

-    private int increment(int index) {

-        index++;

-        if (index >= buffer.length) {

-            index = 0;

-        }

-        return index;

-    }

-

-    /**

-     * Decrements the internal index.

-     *

-     * @param index  the index to decrement

-     * @return the updated index

-     */

-    private int decrement(int index) {

-        index--;

-        if (index < 0) {

-            index = buffer.length - 1;

-        }

-        return index;

-    }

-

-    /**

-     * Returns an iterator over this buffer's elements.

-     *

-     * @return an iterator over this buffer's elements

-     */

-    public Iterator iterator() {

-        return new Iterator() {

-

-            private int index = head;

-            private int lastReturnedIndex = -1;

-

-            public boolean hasNext() {

-                return index != tail;

-

-            }

-

-            public Object next() {

-                if (!hasNext()) {

-                    throw new NoSuchElementException();

-                }

-                lastReturnedIndex = index;

-                index = increment(index);

-                return new Byte(buffer[lastReturnedIndex]);

-            }

-

-            public void remove() {

-                if (lastReturnedIndex == -1) {

-                    throw new IllegalStateException();

-                }

-

-                // First element can be removed quickly

-                if (lastReturnedIndex == head) {

-                    UnboundedFifoByteBuffer.this.remove();

-                    lastReturnedIndex = -1;

-                    return;

-                }

-

-                // Other elements require us to shift the subsequent elements

-                int i = lastReturnedIndex + 1;

-                while (i != tail) {

-                    if (i >= buffer.length) {

-                        buffer[i - 1] = buffer[0];

-                        i = 0;

-                    } else {

-                        buffer[i - 1] = buffer[i];

-                        i++;

-                    }

-                }

-

-                lastReturnedIndex = -1;

-                tail = decrement(tail);

-                buffer[tail] = 0;

-                index = decrement(index);

-            }

-

-        };

-    }

-

+/****************************************************************
+ * 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.decoder;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * UnboundedFifoByteBuffer is a very efficient buffer implementation.
+ * According to performance testing, it exhibits a constant access time, but it
+ * also outperforms ArrayList when used for the same purpose.
+ * <p>
+ * The removal order of an <code>UnboundedFifoByteBuffer</code> is based on the insertion
+ * order; elements are removed in the same order in which they were added.
+ * The iteration order is the same as the removal order.
+ * <p>
+ * The {@link #remove()} and {@link #get()} operations perform in constant time.
+ * The {@link #add(Object)} operation performs in amortized constant time.  All
+ * other operations perform in linear time or worse.
+ * <p>
+ * Note that this implementation is not synchronized.  The following can be
+ * used to provide synchronized access to your <code>UnboundedFifoByteBuffer</code>:
+ * <pre>
+ *   Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
+ * </pre>
+ * <p>
+ * This buffer prevents null objects from being added.
+ *
+ * @since Commons Collections 3.0 (previously in main package v2.1)
+ * @version $Revision: 1.1 $ $Date: 2004/08/24 06:52:02 $
+ *
+ * 
+ * 
+ * 
+ * 
+ * 
+ */
+class UnboundedFifoByteBuffer {
+
+    protected byte[] buffer;
+    protected int head;
+    protected int tail;
+
+    /**
+     * Constructs an UnboundedFifoByteBuffer with the default number of elements.
+     * It is exactly the same as performing the following:
+     *
+     * <pre>
+     *   new UnboundedFifoByteBuffer(32);
+     * </pre>
+     */
+    public UnboundedFifoByteBuffer() {
+        this(32);
+    }
+
+    /**
+     * Constructs an UnboundedFifoByteBuffer with the specified number of elements.
+     * The integer must be a positive integer.
+     *
+     * @param initialSize  the initial size of the buffer
+     * @throws IllegalArgumentException  if the size is less than 1
+     */
+    public UnboundedFifoByteBuffer(int initialSize) {
+        if (initialSize <= 0) {
+            throw new IllegalArgumentException("The size must be greater than 0");
+        }
+        buffer = new byte[initialSize + 1];
+        head = 0;
+        tail = 0;
+    }
+
+    /**
+     * Returns the number of elements stored in the buffer.
+     *
+     * @return this buffer's size
+     */
+    public int size() {
+        int size = 0;
+
+        if (tail < head) {
+            size = buffer.length - head + tail;
+        } else {
+            size = tail - head;
+        }
+
+        return size;
+    }
+
+    /**
+     * Returns true if this buffer is empty; false otherwise.
+     *
+     * @return true if this buffer is empty
+     */
+    public boolean isEmpty() {
+        return (size() == 0);
+    }
+
+    /**
+     * Adds the given element to this buffer.
+     *
+     * @param b  the byte to add
+     * @return true, always
+     */
+    public boolean add(final byte b) {
+
+        if (size() + 1 >= buffer.length) {
+            byte[] tmp = new byte[((buffer.length - 1) * 2) + 1];
+
+            int j = 0;
+            for (int i = head; i != tail;) {
+                tmp[j] = buffer[i];
+                buffer[i] = 0;
+
+                j++;
+                i++;
+                if (i == buffer.length) {
+                    i = 0;
+                }
+            }
+
+            buffer = tmp;
+            head = 0;
+            tail = j;
+        }
+
+        buffer[tail] = b;
+        tail++;
+        if (tail >= buffer.length) {
+            tail = 0;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the next object in the buffer.
+     *
+     * @return the next object in the buffer
+     * @throws BufferUnderflowException  if this buffer is empty
+     */
+    public byte get() {
+        if (isEmpty()) {
+            throw new IllegalStateException("The buffer is already empty");
+        }
+
+        return buffer[head];
+    }
+
+    /**
+     * Removes the next object from the buffer
+     *
+     * @return the removed object
+     * @throws BufferUnderflowException  if this buffer is empty
+     */
+    public byte remove() {
+        if (isEmpty()) {
+            throw new IllegalStateException("The buffer is already empty");
+        }
+
+        byte element = buffer[head];
+
+        head++;
+        if (head >= buffer.length) {
+            head = 0;
+        }
+
+        return element;
+    }
+
+    /**
+     * Increments the internal index.
+     *
+     * @param index  the index to increment
+     * @return the updated index
+     */
+    private int increment(int index) {
+        index++;
+        if (index >= buffer.length) {
+            index = 0;
+        }
+        return index;
+    }
+
+    /**
+     * Decrements the internal index.
+     *
+     * @param index  the index to decrement
+     * @return the updated index
+     */
+    private int decrement(int index) {
+        index--;
+        if (index < 0) {
+            index = buffer.length - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns an iterator over this buffer's elements.
+     *
+     * @return an iterator over this buffer's elements
+     */
+    public Iterator iterator() {
+        return new Iterator() {
+
+            private int index = head;
+            private int lastReturnedIndex = -1;
+
+            public boolean hasNext() {
+                return index != tail;
+
+            }
+
+            public Object next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                lastReturnedIndex = index;
+                index = increment(index);
+                return new Byte(buffer[lastReturnedIndex]);
+            }
+
+            public void remove() {
+                if (lastReturnedIndex == -1) {
+                    throw new IllegalStateException();
+                }
+
+                // First element can be removed quickly
+                if (lastReturnedIndex == head) {
+                    UnboundedFifoByteBuffer.this.remove();
+                    lastReturnedIndex = -1;
+                    return;
+                }
+
+                // Other elements require us to shift the subsequent elements
+                int i = lastReturnedIndex + 1;
+                while (i != tail) {
+                    if (i >= buffer.length) {
+                        buffer[i - 1] = buffer[0];
+                        i = 0;
+                    } else {
+                        buffer[i - 1] = buffer[i];
+                        i++;
+                    }
+                }
+
+                lastReturnedIndex = -1;
+                tail = decrement(tail);
+                buffer[tail] = 0;
+                index = decrement(index);
+            }
+
+        };
+    }
+
 }
\ No newline at end of file
diff --git a/src/org/apache/james/mime4j/field/AddressListField.java b/src/org/apache/james/mime4j/field/AddressListField.java
index b37abbb..df9f398 100644
--- a/src/org/apache/james/mime4j/field/AddressListField.java
+++ b/src/org/apache/james/mime4j/field/AddressListField.java
@@ -1,65 +1,65 @@
-/****************************************************************

- * 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.field;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.field.address.AddressList;

-import org.apache.james.mime4j.field.address.parser.ParseException;

-

-public class AddressListField extends Field {

-    private AddressList addressList;

-    private ParseException parseException;

-

-    protected AddressListField(String name, String body, String raw, AddressList addressList, ParseException parseException) {

-        super(name, body, raw);

-        this.addressList = addressList;

-        this.parseException = parseException;

-    }

-

-    public AddressList getAddressList() {

-        return addressList;

-    }

-

-    public ParseException getParseException() {

-        return parseException;

-    }

-

-    public static class Parser implements FieldParser {

-        private static Log log = LogFactory.getLog(Parser.class);

-

-        public Field parse(final String name, final String body, final String raw) {

-            AddressList addressList = null;

-            ParseException parseException = null;

-            try {

-                addressList = AddressList.parse(body);

-            }

-            catch (ParseException e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = e;

-            }

-            return new AddressListField(name, body, raw, addressList, parseException);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class AddressListField extends Field {
+    private AddressList addressList;
+    private ParseException parseException;
+
+    protected AddressListField(String name, String body, String raw, AddressList addressList, ParseException parseException) {
+        super(name, body, raw);
+        this.addressList = addressList;
+        this.parseException = parseException;
+    }
+
+    public AddressList getAddressList() {
+        return addressList;
+    }
+
+    public ParseException getParseException() {
+        return parseException;
+    }
+
+    public static class Parser implements FieldParser {
+        private static Log log = LogFactory.getLog(Parser.class);
+
+        public Field parse(final String name, final String body, final String raw) {
+            AddressList addressList = null;
+            ParseException parseException = null;
+            try {
+                addressList = AddressList.parse(body);
+            }
+            catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = e;
+            }
+            return new AddressListField(name, body, raw, addressList, parseException);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java b/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java
index eb61513..73d8d23 100644
--- a/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java
+++ b/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java
@@ -1,88 +1,88 @@
-/****************************************************************

- * 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.field;

-

-

-

-/**

- * Represents a <code>Content-Transfer-Encoding</code> field.

- *

- * 

- * @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-public class ContentTransferEncodingField extends Field {

-    /**

-     * The <code>7bit</code> encoding.

-     */

-    public static final String ENC_7BIT = "7bit";

-    /**

-     * The <code>8bit</code> encoding.

-     */

-    public static final String ENC_8BIT = "8bit";

-    /**

-     * The <code>binary</code> encoding.

-     */

-    public static final String ENC_BINARY = "binary";

-    /**

-     * The <code>quoted-printable</code> encoding.

-     */

-    public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";

-    /**

-     * The <code>base64</code> encoding.

-     */

-    public static final String ENC_BASE64 = "base64";

-    

-    private String encoding;

-    

-    protected ContentTransferEncodingField(String name, String body, String raw, String encoding) {

-        super(name, body, raw);

-        this.encoding = encoding;

-    }

-

-    /**

-     * Gets the encoding defined in this field.

-     * 

-     * @return the encoding or an empty string if not set.

-     */

-    public String getEncoding() {

-        return encoding;

-    }

-    

-    /**

-     * Gets the encoding of the given field if. Returns the default 

-     * <code>7bit</code> if not set or if

-     * <code>f</code> is <code>null</code>.

-     * 

-     * @return the encoding.

-     */

-    public static String getEncoding(ContentTransferEncodingField f) {

-        if (f != null && f.getEncoding().length() != 0) {

-            return f.getEncoding();

-        }

-        return ENC_7BIT;

-    }

-    

-    public static class Parser implements FieldParser {

-        public Field parse(final String name, final String body, final String raw) {

-            final String encoding = body.trim().toLowerCase();

-            return new ContentTransferEncodingField(name, body, raw, encoding);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+
+
+/**
+ * Represents a <code>Content-Transfer-Encoding</code> field.
+ *
+ * 
+ * @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class ContentTransferEncodingField extends Field {
+    /**
+     * The <code>7bit</code> encoding.
+     */
+    public static final String ENC_7BIT = "7bit";
+    /**
+     * The <code>8bit</code> encoding.
+     */
+    public static final String ENC_8BIT = "8bit";
+    /**
+     * The <code>binary</code> encoding.
+     */
+    public static final String ENC_BINARY = "binary";
+    /**
+     * The <code>quoted-printable</code> encoding.
+     */
+    public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";
+    /**
+     * The <code>base64</code> encoding.
+     */
+    public static final String ENC_BASE64 = "base64";
+    
+    private String encoding;
+    
+    protected ContentTransferEncodingField(String name, String body, String raw, String encoding) {
+        super(name, body, raw);
+        this.encoding = encoding;
+    }
+
+    /**
+     * Gets the encoding defined in this field.
+     * 
+     * @return the encoding or an empty string if not set.
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+    
+    /**
+     * Gets the encoding of the given field if. Returns the default 
+     * <code>7bit</code> if not set or if
+     * <code>f</code> is <code>null</code>.
+     * 
+     * @return the encoding.
+     */
+    public static String getEncoding(ContentTransferEncodingField f) {
+        if (f != null && f.getEncoding().length() != 0) {
+            return f.getEncoding();
+        }
+        return ENC_7BIT;
+    }
+    
+    public static class Parser implements FieldParser {
+        public Field parse(final String name, final String body, final String raw) {
+            final String encoding = body.trim().toLowerCase();
+            return new ContentTransferEncodingField(name, body, raw, encoding);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/ContentTypeField.java b/src/org/apache/james/mime4j/field/ContentTypeField.java
index 646cf14..ad9f7f9 100644
--- a/src/org/apache/james/mime4j/field/ContentTypeField.java
+++ b/src/org/apache/james/mime4j/field/ContentTypeField.java
@@ -1,259 +1,259 @@
-/****************************************************************

- * 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.field;

-

-import java.io.StringReader;

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.Map;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;

-import org.apache.james.mime4j.field.contenttype.parser.ParseException;

-import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;

-

-/**

- * Represents a <code>Content-Type</code> field.

- *

- * <p>TODO: Remove dependency on Java 1.4 regexps</p>

- *

- *

- * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $

- */

-public class ContentTypeField extends Field {

-

-    /**

-     * The prefix of all <code>multipart</code> MIME types.

-     */

-    public static final String TYPE_MULTIPART_PREFIX = "multipart/";

-    /**

-     * The <code>multipart/digest</code> MIME type.

-     */

-    public static final String TYPE_MULTIPART_DIGEST = "multipart/digest";

-    /**

-     * The <code>text/plain</code> MIME type.

-     */

-    public static final String TYPE_TEXT_PLAIN = "text/plain";

-    /**

-     * The <code>message/rfc822</code> MIME type.

-     */

-    public static final String TYPE_MESSAGE_RFC822 = "message/rfc822";

-    /**

-     * The name of the <code>boundary</code> parameter.

-     */

-    public static final String PARAM_BOUNDARY = "boundary";

-    /**

-     * The name of the <code>charset</code> parameter.

-     */

-    public static final String PARAM_CHARSET = "charset";

-

-    private String mimeType = "";

-    private Map<String, String> parameters = null;

-    private ParseException parseException;

-

-    protected ContentTypeField(String name, String body, String raw, String mimeType, Map<String, String> parameters, ParseException parseException) {

-        super(name, body, raw);

-        this.mimeType = mimeType;

-        this.parameters = parameters;

-        this.parseException = parseException;

-    }

-

-    /**

-     * Gets the exception that was raised during parsing of

-     * the field value, if any; otherwise, null.

-     */

-    public ParseException getParseException() {

-        return parseException;

-    }

-

-    /**

-     * Gets the MIME type defined in this Content-Type field.

-     *

-     * @return the MIME type or an empty string if not set.

-     */

-    public String getMimeType() {

-        return mimeType;

-    }

-

-    /**

-     * Gets the MIME type defined in the child's

-     * Content-Type field or derives a MIME type from the parent

-     * if child is <code>null</code> or hasn't got a MIME type value set.

-     * If child's MIME type is multipart but no boundary

-     * has been set the MIME type of child will be derived from

-     * the parent.

-     *

-     * @param child the child.

-     * @param parent the parent.

-     * @return the MIME type.

-     */

-    public static String getMimeType(ContentTypeField child,

-                                     ContentTypeField parent) {

-

-        if (child == null || child.getMimeType().length() == 0

-                || child.isMultipart() && child.getBoundary() == null) {

-

-            if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {

-                return TYPE_MESSAGE_RFC822;

-            } else {

-                return TYPE_TEXT_PLAIN;

-            }

-        }

-

-        return child.getMimeType();

-    }

-

-    /**

-     * Gets the value of a parameter. Parameter names are case-insensitive.

-     *

-     * @param name the name of the parameter to get.

-     * @return the parameter value or <code>null</code> if not set.

-     */

-    public String getParameter(String name) {

-        return parameters != null

-                    ? parameters.get(name.toLowerCase())

-                    : null;

-    }

-

-    /**

-     * Gets all parameters.

-     *

-     * @return the parameters.

-     */

-    public Map<String, String> getParameters() {

-        if (parameters != null) {

-            return Collections.unmodifiableMap(parameters);

-        }

-        return Collections.emptyMap();

-    }

-

-    /**

-     * Gets the value of the <code>boundary</code> parameter if set.

-     *

-     * @return the <code>boundary</code> parameter value or <code>null</code>

-     *             if not set.

-     */

-    public String getBoundary() {

-        return getParameter(PARAM_BOUNDARY);

-    }

-

-    /**

-     * Gets the value of the <code>charset</code> parameter if set.

-     *

-     * @return the <code>charset</code> parameter value or <code>null</code>

-     *         if not set.

-     */

-    public String getCharset() {

-        return getParameter(PARAM_CHARSET);

-    }

-

-    /**

-     * Gets the value of the <code>charset</code> parameter if set for the

-     * given field. Returns the default <code>us-ascii</code> if not set or if

-     * <code>f</code> is <code>null</code>.

-     *

-     * @return the <code>charset</code> parameter value.

-     */

-    public static String getCharset(ContentTypeField f) {

-        if (f != null) {

-            if (f.getCharset() != null && f.getCharset().length() > 0) {

-                return f.getCharset();

-            }

-        }

-        return "us-ascii";

-    }

-

-    /**

-     * Determines if the MIME type of this field matches the given one.

-     *

-     * @param mimeType the MIME type to match against.

-     * @return <code>true</code> if the MIME type of this field matches,

-     *         <code>false</code> otherwise.

-     */

-    public boolean isMimeType(String mimeType) {

-        return this.mimeType.equalsIgnoreCase(mimeType);

-    }

-

-    /**

-     * Determines if the MIME type of this field is <code>multipart/*</code>.

-     *

-     * @return <code>true</code> if this field is has a <code>multipart/*</code>

-     *         MIME type, <code>false</code> otherwise.

-     */

-    public boolean isMultipart() {

-        return mimeType.startsWith(TYPE_MULTIPART_PREFIX);

-    }

-

-    public static class Parser implements FieldParser {

-        private static Log log = LogFactory.getLog(Parser.class);

-

-        public Field parse(final String name, final String body, final String raw) {

-            ParseException parseException = null;

-            String mimeType = "";

-            Map<String, String> parameters = null;

-

-            ContentTypeParser parser = new ContentTypeParser(new StringReader(body));

-            try {

-                parser.parseAll();

-            }

-            catch (ParseException e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = e;

-            }

-            catch (TokenMgrError e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = new ParseException(e.getMessage());

-            }

-

-            try {

-                final String type = parser.getType();

-                final String subType = parser.getSubType();

-

-                if (type != null && subType != null) {

-                    mimeType = (type + "/" + parser.getSubType()).toLowerCase();

-

-                    ArrayList<String> paramNames = parser.getParamNames();

-                    ArrayList<String> paramValues = parser.getParamValues();

-

-                    if (paramNames != null && paramValues != null) {

-                        for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) {

-                            if (parameters == null)

-                                parameters = new HashMap<String, String>((int)(paramNames.size() * 1.3 + 1));

-                            String paramName = paramNames.get(i).toLowerCase();

-                            String paramValue = paramValues.get(i);

-                            parameters.put(paramName, paramValue);

-                        }

-                    }

-                }

-            }

-            catch (NullPointerException npe) {

-            }

-            return new ContentTypeField(name, body, raw, mimeType, parameters, parseException);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;
+import org.apache.james.mime4j.field.contenttype.parser.ParseException;
+import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
+
+/**
+ * Represents a <code>Content-Type</code> field.
+ *
+ * <p>TODO: Remove dependency on Java 1.4 regexps</p>
+ *
+ *
+ * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $
+ */
+public class ContentTypeField extends Field {
+
+    /**
+     * The prefix of all <code>multipart</code> MIME types.
+     */
+    public static final String TYPE_MULTIPART_PREFIX = "multipart/";
+    /**
+     * The <code>multipart/digest</code> MIME type.
+     */
+    public static final String TYPE_MULTIPART_DIGEST = "multipart/digest";
+    /**
+     * The <code>text/plain</code> MIME type.
+     */
+    public static final String TYPE_TEXT_PLAIN = "text/plain";
+    /**
+     * The <code>message/rfc822</code> MIME type.
+     */
+    public static final String TYPE_MESSAGE_RFC822 = "message/rfc822";
+    /**
+     * The name of the <code>boundary</code> parameter.
+     */
+    public static final String PARAM_BOUNDARY = "boundary";
+    /**
+     * The name of the <code>charset</code> parameter.
+     */
+    public static final String PARAM_CHARSET = "charset";
+
+    private String mimeType = "";
+    private Map<String, String> parameters = null;
+    private ParseException parseException;
+
+    protected ContentTypeField(String name, String body, String raw, String mimeType, Map<String, String> parameters, ParseException parseException) {
+        super(name, body, raw);
+        this.mimeType = mimeType;
+        this.parameters = parameters;
+        this.parseException = parseException;
+    }
+
+    /**
+     * Gets the exception that was raised during parsing of
+     * the field value, if any; otherwise, null.
+     */
+    public ParseException getParseException() {
+        return parseException;
+    }
+
+    /**
+     * Gets the MIME type defined in this Content-Type field.
+     *
+     * @return the MIME type or an empty string if not set.
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    /**
+     * Gets the MIME type defined in the child's
+     * Content-Type field or derives a MIME type from the parent
+     * if child is <code>null</code> or hasn't got a MIME type value set.
+     * If child's MIME type is multipart but no boundary
+     * has been set the MIME type of child will be derived from
+     * the parent.
+     *
+     * @param child the child.
+     * @param parent the parent.
+     * @return the MIME type.
+     */
+    public static String getMimeType(ContentTypeField child,
+                                     ContentTypeField parent) {
+
+        if (child == null || child.getMimeType().length() == 0
+                || child.isMultipart() && child.getBoundary() == null) {
+
+            if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {
+                return TYPE_MESSAGE_RFC822;
+            } else {
+                return TYPE_TEXT_PLAIN;
+            }
+        }
+
+        return child.getMimeType();
+    }
+
+    /**
+     * Gets the value of a parameter. Parameter names are case-insensitive.
+     *
+     * @param name the name of the parameter to get.
+     * @return the parameter value or <code>null</code> if not set.
+     */
+    public String getParameter(String name) {
+        return parameters != null
+                    ? parameters.get(name.toLowerCase())
+                    : null;
+    }
+
+    /**
+     * Gets all parameters.
+     *
+     * @return the parameters.
+     */
+    public Map<String, String> getParameters() {
+        if (parameters != null) {
+            return Collections.unmodifiableMap(parameters);
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Gets the value of the <code>boundary</code> parameter if set.
+     *
+     * @return the <code>boundary</code> parameter value or <code>null</code>
+     *             if not set.
+     */
+    public String getBoundary() {
+        return getParameter(PARAM_BOUNDARY);
+    }
+
+    /**
+     * Gets the value of the <code>charset</code> parameter if set.
+     *
+     * @return the <code>charset</code> parameter value or <code>null</code>
+     *         if not set.
+     */
+    public String getCharset() {
+        return getParameter(PARAM_CHARSET);
+    }
+
+    /**
+     * Gets the value of the <code>charset</code> parameter if set for the
+     * given field. Returns the default <code>us-ascii</code> if not set or if
+     * <code>f</code> is <code>null</code>.
+     *
+     * @return the <code>charset</code> parameter value.
+     */
+    public static String getCharset(ContentTypeField f) {
+        if (f != null) {
+            if (f.getCharset() != null && f.getCharset().length() > 0) {
+                return f.getCharset();
+            }
+        }
+        return "us-ascii";
+    }
+
+    /**
+     * Determines if the MIME type of this field matches the given one.
+     *
+     * @param mimeType the MIME type to match against.
+     * @return <code>true</code> if the MIME type of this field matches,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isMimeType(String mimeType) {
+        return this.mimeType.equalsIgnoreCase(mimeType);
+    }
+
+    /**
+     * Determines if the MIME type of this field is <code>multipart/*</code>.
+     *
+     * @return <code>true</code> if this field is has a <code>multipart/*</code>
+     *         MIME type, <code>false</code> otherwise.
+     */
+    public boolean isMultipart() {
+        return mimeType.startsWith(TYPE_MULTIPART_PREFIX);
+    }
+
+    public static class Parser implements FieldParser {
+        private static Log log = LogFactory.getLog(Parser.class);
+
+        public Field parse(final String name, final String body, final String raw) {
+            ParseException parseException = null;
+            String mimeType = "";
+            Map<String, String> parameters = null;
+
+            ContentTypeParser parser = new ContentTypeParser(new StringReader(body));
+            try {
+                parser.parseAll();
+            }
+            catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = e;
+            }
+            catch (TokenMgrError e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = new ParseException(e.getMessage());
+            }
+
+            try {
+                final String type = parser.getType();
+                final String subType = parser.getSubType();
+
+                if (type != null && subType != null) {
+                    mimeType = (type + "/" + parser.getSubType()).toLowerCase();
+
+                    ArrayList<String> paramNames = parser.getParamNames();
+                    ArrayList<String> paramValues = parser.getParamValues();
+
+                    if (paramNames != null && paramValues != null) {
+                        for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) {
+                            if (parameters == null)
+                                parameters = new HashMap<String, String>((int)(paramNames.size() * 1.3 + 1));
+                            String paramName = paramNames.get(i).toLowerCase();
+                            String paramValue = paramValues.get(i);
+                            parameters.put(paramName, paramValue);
+                        }
+                    }
+                }
+            }
+            catch (NullPointerException npe) {
+            }
+            return new ContentTypeField(name, body, raw, mimeType, parameters, parseException);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/DateTimeField.java b/src/org/apache/james/mime4j/field/DateTimeField.java
index d4d12f5..7600d88 100644
--- a/src/org/apache/james/mime4j/field/DateTimeField.java
+++ b/src/org/apache/james/mime4j/field/DateTimeField.java
@@ -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.field;

-

-//BEGIN android-changed: Stubbing out logging

-

-import com.android.mail.utils.LogUtils;

-

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END

-import org.apache.james.mime4j.field.datetime.DateTime;

-import org.apache.james.mime4j.field.datetime.parser.ParseException;

-

-import java.util.Date;

-

-public class DateTimeField extends Field {

-    private Date date;

-    private ParseException parseException;

-

-    protected DateTimeField(String name, String body, String raw, Date date, ParseException parseException) {

-        super(name, body, raw);

-        this.date = date;

-        this.parseException = parseException;

-    }

-

-    public Date getDate() {

-        return date;

-    }

-

-    public ParseException getParseException() {

-        return parseException;

-    }

-

-    public static class Parser implements FieldParser {

-        private static Log log = LogFactory.getLog(Parser.class);

-

-        public Field parse(final String name, String body, final String raw) {

-            Date date = null;

-            ParseException parseException = null;

-            //BEGIN android-changed

-            body = LogUtils.cleanUpMimeDate(body);

-            //END android-changed

-            try {

-                date = DateTime.parse(body).getDate();

-            }

-            catch (ParseException e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = e;

-            }

-            return new DateTimeField(name, body, raw, date, parseException);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+//BEGIN android-changed: Stubbing out logging
+
+import com.android.mail.utils.LogUtils;
+
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END
+import org.apache.james.mime4j.field.datetime.DateTime;
+import org.apache.james.mime4j.field.datetime.parser.ParseException;
+
+import java.util.Date;
+
+public class DateTimeField extends Field {
+    private Date date;
+    private ParseException parseException;
+
+    protected DateTimeField(String name, String body, String raw, Date date, ParseException parseException) {
+        super(name, body, raw);
+        this.date = date;
+        this.parseException = parseException;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public ParseException getParseException() {
+        return parseException;
+    }
+
+    public static class Parser implements FieldParser {
+        private static Log log = LogFactory.getLog(Parser.class);
+
+        public Field parse(final String name, String body, final String raw) {
+            Date date = null;
+            ParseException parseException = null;
+            //BEGIN android-changed
+            body = LogUtils.cleanUpMimeDate(body);
+            //END android-changed
+            try {
+                date = DateTime.parse(body).getDate();
+            }
+            catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = e;
+            }
+            return new DateTimeField(name, body, raw, date, parseException);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/DefaultFieldParser.java b/src/org/apache/james/mime4j/field/DefaultFieldParser.java
index 84fcdcb..3695afe 100644
--- a/src/org/apache/james/mime4j/field/DefaultFieldParser.java
+++ b/src/org/apache/james/mime4j/field/DefaultFieldParser.java
@@ -1,45 +1,45 @@
-/*

- *  Copyright 2006 the mime4j project

- *

- *  Licensed 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.field;

-

-public class DefaultFieldParser extends DelegatingFieldParser {

-    

-    public DefaultFieldParser() {

-        setFieldParser(Field.CONTENT_TRANSFER_ENCODING, new ContentTransferEncodingField.Parser());

-        setFieldParser(Field.CONTENT_TYPE, new ContentTypeField.Parser());

-        

-        final DateTimeField.Parser dateTimeParser = new DateTimeField.Parser();

-        setFieldParser(Field.DATE, dateTimeParser);

-        setFieldParser(Field.RESENT_DATE, dateTimeParser);

-        

-        final MailboxListField.Parser mailboxListParser = new MailboxListField.Parser();

-        setFieldParser(Field.FROM, mailboxListParser);

-        setFieldParser(Field.RESENT_FROM, mailboxListParser);

-        

-        final MailboxField.Parser mailboxParser = new MailboxField.Parser();

-        setFieldParser(Field.SENDER, mailboxParser);

-        setFieldParser(Field.RESENT_SENDER, mailboxParser);

-        

-        final AddressListField.Parser addressListParser = new AddressListField.Parser();

-        setFieldParser(Field.TO, addressListParser);

-        setFieldParser(Field.RESENT_TO, addressListParser);

-        setFieldParser(Field.CC, addressListParser);

-        setFieldParser(Field.RESENT_CC, addressListParser);

-        setFieldParser(Field.BCC, addressListParser);

-        setFieldParser(Field.RESENT_BCC, addressListParser);

-        setFieldParser(Field.REPLY_TO, addressListParser);

-    }

-}

+/*
+ *  Copyright 2006 the mime4j project
+ *
+ *  Licensed 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.field;
+
+public class DefaultFieldParser extends DelegatingFieldParser {
+    
+    public DefaultFieldParser() {
+        setFieldParser(Field.CONTENT_TRANSFER_ENCODING, new ContentTransferEncodingField.Parser());
+        setFieldParser(Field.CONTENT_TYPE, new ContentTypeField.Parser());
+        
+        final DateTimeField.Parser dateTimeParser = new DateTimeField.Parser();
+        setFieldParser(Field.DATE, dateTimeParser);
+        setFieldParser(Field.RESENT_DATE, dateTimeParser);
+        
+        final MailboxListField.Parser mailboxListParser = new MailboxListField.Parser();
+        setFieldParser(Field.FROM, mailboxListParser);
+        setFieldParser(Field.RESENT_FROM, mailboxListParser);
+        
+        final MailboxField.Parser mailboxParser = new MailboxField.Parser();
+        setFieldParser(Field.SENDER, mailboxParser);
+        setFieldParser(Field.RESENT_SENDER, mailboxParser);
+        
+        final AddressListField.Parser addressListParser = new AddressListField.Parser();
+        setFieldParser(Field.TO, addressListParser);
+        setFieldParser(Field.RESENT_TO, addressListParser);
+        setFieldParser(Field.CC, addressListParser);
+        setFieldParser(Field.RESENT_CC, addressListParser);
+        setFieldParser(Field.BCC, addressListParser);
+        setFieldParser(Field.RESENT_BCC, addressListParser);
+        setFieldParser(Field.REPLY_TO, addressListParser);
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/DelegatingFieldParser.java b/src/org/apache/james/mime4j/field/DelegatingFieldParser.java
index 2a2866f..32b69ec 100644
--- a/src/org/apache/james/mime4j/field/DelegatingFieldParser.java
+++ b/src/org/apache/james/mime4j/field/DelegatingFieldParser.java
@@ -1,47 +1,47 @@
-/*

- *  Copyright 2006 the mime4j project

- *

- *  Licensed 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.field;

-

-import java.util.HashMap;

-import java.util.Map;

-

-public class DelegatingFieldParser implements FieldParser {

-

-    private Map<String, FieldParser> parsers = new HashMap<String, FieldParser>();

-    private FieldParser defaultParser = new UnstructuredField.Parser();

-

-    /**

-     * Sets the parser used for the field named <code>name</code>.

-     * @param name the name of the field

-     * @param parser the parser for fields named <code>name</code>

-     */

-    public void setFieldParser(final String name, final FieldParser parser) {

-        parsers.put(name.toLowerCase(), parser);

-    }

-

-    public FieldParser getParser(final String name) {

-        final FieldParser field = parsers.get(name.toLowerCase());

-        if(field==null) {

-            return defaultParser;

-        }

-        return field;

-    }

-

-    public Field parse(final String name, final String body, final String raw) {

-        final FieldParser parser = getParser(name);

-        return parser.parse(name, body, raw);

-    }

-}

+/*
+ *  Copyright 2006 the mime4j project
+ *
+ *  Licensed 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.field;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DelegatingFieldParser implements FieldParser {
+
+    private Map<String, FieldParser> parsers = new HashMap<String, FieldParser>();
+    private FieldParser defaultParser = new UnstructuredField.Parser();
+
+    /**
+     * Sets the parser used for the field named <code>name</code>.
+     * @param name the name of the field
+     * @param parser the parser for fields named <code>name</code>
+     */
+    public void setFieldParser(final String name, final FieldParser parser) {
+        parsers.put(name.toLowerCase(), parser);
+    }
+
+    public FieldParser getParser(final String name) {
+        final FieldParser field = parsers.get(name.toLowerCase());
+        if(field==null) {
+            return defaultParser;
+        }
+        return field;
+    }
+
+    public Field parse(final String name, final String body, final String raw) {
+        final FieldParser parser = getParser(name);
+        return parser.parse(name, body, raw);
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/Field.java b/src/org/apache/james/mime4j/field/Field.java
index 7c2a20d..4dea5c5 100644
--- a/src/org/apache/james/mime4j/field/Field.java
+++ b/src/org/apache/james/mime4j/field/Field.java
@@ -1,192 +1,192 @@
-/****************************************************************

- * 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.field;

-

-import java.util.regex.Matcher;

-import java.util.regex.Pattern;

-

-/**

- * The base class of all field classes.

- *

- * 

- * @version $Id: Field.java,v 1.6 2004/10/25 07:26:46 ntherning Exp $

- */

-public abstract class Field {

-    public static final String SENDER = "Sender";

-    public static final String FROM = "From";

-    public static final String TO = "To";

-    public static final String CC = "Cc";

-    public static final String BCC = "Bcc";

-    public static final String REPLY_TO = "Reply-To";

-    public static final String RESENT_SENDER = "Resent-Sender";

-    public static final String RESENT_FROM = "Resent-From";

-    public static final String RESENT_TO = "Resent-To";

-    public static final String RESENT_CC = "Resent-Cc";

-    public static final String RESENT_BCC = "Resent-Bcc";

-

-    public static final String DATE = "Date";

-    public static final String RESENT_DATE = "Resent-Date";

-

-    public static final String SUBJECT = "Subject";

-    public static final String CONTENT_TYPE = "Content-Type";

-    public static final String CONTENT_TRANSFER_ENCODING = 

-                                        "Content-Transfer-Encoding";

-    

-    private static final String FIELD_NAME_PATTERN = 

-        "^([\\x21-\\x39\\x3b-\\x7e]+)[ \t]*:";

-    private static final Pattern fieldNamePattern = 

-        Pattern.compile(FIELD_NAME_PATTERN);

-        

-    private static final DefaultFieldParser parser = new DefaultFieldParser();

-    

-    private final String name;

-    private final String body;

-    private final String raw;

-    

-    protected Field(final String name, final String body, final String raw) {

-        this.name = name;

-        this.body = body;

-        this.raw = raw;

-    }

-    

-    /**

-     * Parses the given string and returns an instance of the 

-     * <code>Field</code> class. The type of the class returned depends on

-     * the field name:

-     * <table>

-     *      <tr>

-     *          <td><em>Field name</em></td><td><em>Class returned</em></td>

-     *          <td>Content-Type</td><td>org.apache.james.mime4j.field.ContentTypeField</td>

-     *          <td>other</td><td>org.apache.james.mime4j.field.UnstructuredField</td>

-     *      </tr>

-     * </table>

-     * 

-     * @param s the string to parse.

-     * @return a <code>Field</code> instance.

-     * @throws IllegalArgumentException on parse errors.

-     */

-    public static Field parse(final String raw) {

-        

-        /*

-         * Unfold the field.

-         */

-        final String unfolded = raw.replaceAll("\r|\n", "");

-        

-        /*

-         * Split into name and value.

-         */

-        final Matcher fieldMatcher = fieldNamePattern.matcher(unfolded);

-        if (!fieldMatcher.find()) {

-            throw new IllegalArgumentException("Invalid field in string");

-        }

-        final String name = fieldMatcher.group(1);

-        

-        String body = unfolded.substring(fieldMatcher.end());

-        if (body.length() > 0 && body.charAt(0) == ' ') {

-            body = body.substring(1);

-        }

-        

-        return parser.parse(name, body, raw);

-    }

-    

-    /**

-     * Gets the default parser used to parse fields.

-     * @return the default field parser

-     */

-    public static DefaultFieldParser getParser() {

-        return parser;

-    }

-    

-    /**

-     * Gets the name of the field (<code>Subject</code>, 

-     * <code>From</code>, etc).

-     * 

-     * @return the field name.

-     */

-    public String getName() {

-        return name;

-    }

-    

-    /**

-     * Gets the original raw field string.

-     * 

-     * @return the original raw field string.

-     */

-    public String getRaw() {

-        return raw;

-    }

-    

-    /**

-     * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field 

-     * body string.

-     * 

-     * @return the unfolded unparsed field body string.

-     */

-    public String getBody() {

-        return body;

-    }

-    

-    /**

-     * Determines if this is a <code>Content-Type</code> field.

-     * 

-     * @return <code>true</code> if this is a <code>Content-Type</code> field,

-     *         <code>false</code> otherwise.

-     */

-    public boolean isContentType() {

-        return CONTENT_TYPE.equalsIgnoreCase(name);

-    }

-    

-    /**

-     * Determines if this is a <code>Subject</code> field.

-     * 

-     * @return <code>true</code> if this is a <code>Subject</code> field,

-     *         <code>false</code> otherwise.

-     */

-    public boolean isSubject() {

-        return SUBJECT.equalsIgnoreCase(name);

-    }

-    

-    /**

-     * Determines if this is a <code>From</code> field.

-     * 

-     * @return <code>true</code> if this is a <code>From</code> field,

-     *         <code>false</code> otherwise.

-     */

-    public boolean isFrom() {

-        return FROM.equalsIgnoreCase(name);

-    }

-    

-    /**

-     * Determines if this is a <code>To</code> field.

-     * 

-     * @return <code>true</code> if this is a <code>To</code> field,

-     *         <code>false</code> otherwise.

-     */

-    public boolean isTo() {

-        return TO.equalsIgnoreCase(name);

-    }

-    

-    /**

-     * @see #getRaw()

-     */

-    public String toString() {

-        return raw;

-    }

-}

+/****************************************************************
+ * 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.field;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The base class of all field classes.
+ *
+ * 
+ * @version $Id: Field.java,v 1.6 2004/10/25 07:26:46 ntherning Exp $
+ */
+public abstract class Field {
+    public static final String SENDER = "Sender";
+    public static final String FROM = "From";
+    public static final String TO = "To";
+    public static final String CC = "Cc";
+    public static final String BCC = "Bcc";
+    public static final String REPLY_TO = "Reply-To";
+    public static final String RESENT_SENDER = "Resent-Sender";
+    public static final String RESENT_FROM = "Resent-From";
+    public static final String RESENT_TO = "Resent-To";
+    public static final String RESENT_CC = "Resent-Cc";
+    public static final String RESENT_BCC = "Resent-Bcc";
+
+    public static final String DATE = "Date";
+    public static final String RESENT_DATE = "Resent-Date";
+
+    public static final String SUBJECT = "Subject";
+    public static final String CONTENT_TYPE = "Content-Type";
+    public static final String CONTENT_TRANSFER_ENCODING = 
+                                        "Content-Transfer-Encoding";
+    
+    private static final String FIELD_NAME_PATTERN = 
+        "^([\\x21-\\x39\\x3b-\\x7e]+)[ \t]*:";
+    private static final Pattern fieldNamePattern = 
+        Pattern.compile(FIELD_NAME_PATTERN);
+        
+    private static final DefaultFieldParser parser = new DefaultFieldParser();
+    
+    private final String name;
+    private final String body;
+    private final String raw;
+    
+    protected Field(final String name, final String body, final String raw) {
+        this.name = name;
+        this.body = body;
+        this.raw = raw;
+    }
+    
+    /**
+     * Parses the given string and returns an instance of the 
+     * <code>Field</code> class. The type of the class returned depends on
+     * the field name:
+     * <table>
+     *      <tr>
+     *          <td><em>Field name</em></td><td><em>Class returned</em></td>
+     *          <td>Content-Type</td><td>org.apache.james.mime4j.field.ContentTypeField</td>
+     *          <td>other</td><td>org.apache.james.mime4j.field.UnstructuredField</td>
+     *      </tr>
+     * </table>
+     * 
+     * @param s the string to parse.
+     * @return a <code>Field</code> instance.
+     * @throws IllegalArgumentException on parse errors.
+     */
+    public static Field parse(final String raw) {
+        
+        /*
+         * Unfold the field.
+         */
+        final String unfolded = raw.replaceAll("\r|\n", "");
+        
+        /*
+         * Split into name and value.
+         */
+        final Matcher fieldMatcher = fieldNamePattern.matcher(unfolded);
+        if (!fieldMatcher.find()) {
+            throw new IllegalArgumentException("Invalid field in string");
+        }
+        final String name = fieldMatcher.group(1);
+        
+        String body = unfolded.substring(fieldMatcher.end());
+        if (body.length() > 0 && body.charAt(0) == ' ') {
+            body = body.substring(1);
+        }
+        
+        return parser.parse(name, body, raw);
+    }
+    
+    /**
+     * Gets the default parser used to parse fields.
+     * @return the default field parser
+     */
+    public static DefaultFieldParser getParser() {
+        return parser;
+    }
+    
+    /**
+     * Gets the name of the field (<code>Subject</code>, 
+     * <code>From</code>, etc).
+     * 
+     * @return the field name.
+     */
+    public String getName() {
+        return name;
+    }
+    
+    /**
+     * Gets the original raw field string.
+     * 
+     * @return the original raw field string.
+     */
+    public String getRaw() {
+        return raw;
+    }
+    
+    /**
+     * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field 
+     * body string.
+     * 
+     * @return the unfolded unparsed field body string.
+     */
+    public String getBody() {
+        return body;
+    }
+    
+    /**
+     * Determines if this is a <code>Content-Type</code> field.
+     * 
+     * @return <code>true</code> if this is a <code>Content-Type</code> field,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isContentType() {
+        return CONTENT_TYPE.equalsIgnoreCase(name);
+    }
+    
+    /**
+     * Determines if this is a <code>Subject</code> field.
+     * 
+     * @return <code>true</code> if this is a <code>Subject</code> field,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isSubject() {
+        return SUBJECT.equalsIgnoreCase(name);
+    }
+    
+    /**
+     * Determines if this is a <code>From</code> field.
+     * 
+     * @return <code>true</code> if this is a <code>From</code> field,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isFrom() {
+        return FROM.equalsIgnoreCase(name);
+    }
+    
+    /**
+     * Determines if this is a <code>To</code> field.
+     * 
+     * @return <code>true</code> if this is a <code>To</code> field,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isTo() {
+        return TO.equalsIgnoreCase(name);
+    }
+    
+    /**
+     * @see #getRaw()
+     */
+    public String toString() {
+        return raw;
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/FieldParser.java b/src/org/apache/james/mime4j/field/FieldParser.java
index 4f33c9e..78aaf13 100644
--- a/src/org/apache/james/mime4j/field/FieldParser.java
+++ b/src/org/apache/james/mime4j/field/FieldParser.java
@@ -1,21 +1,21 @@
-/*

- *  Copyright 2006 the mime4j project

- *

- *  Licensed 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.field;

-

-public interface FieldParser {

-    

-    Field parse(final String name, final String body, final String raw);

-}

+/*
+ *  Copyright 2006 the mime4j project
+ *
+ *  Licensed 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.field;
+
+public interface FieldParser {
+    
+    Field parse(final String name, final String body, final String raw);
+}
diff --git a/src/org/apache/james/mime4j/field/MailboxField.java b/src/org/apache/james/mime4j/field/MailboxField.java
index 9404fd6..f159800 100644
--- a/src/org/apache/james/mime4j/field/MailboxField.java
+++ b/src/org/apache/james/mime4j/field/MailboxField.java
@@ -1,70 +1,70 @@
-/****************************************************************

- * 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.field;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.field.address.AddressList;

-import org.apache.james.mime4j.field.address.Mailbox;

-import org.apache.james.mime4j.field.address.MailboxList;

-import org.apache.james.mime4j.field.address.parser.ParseException;

-

-public class MailboxField extends Field {

-    private final Mailbox mailbox;

-    private final ParseException parseException;

-

-    protected MailboxField(final String name, final String body, final String raw, final Mailbox mailbox, final ParseException parseException) {

-        super(name, body, raw);

-        this.mailbox = mailbox;

-        this.parseException = parseException;

-    }

-

-    public Mailbox getMailbox() {

-        return mailbox;

-    }

-

-    public ParseException getParseException() {

-        return parseException;

-    }

-    

-    public static class Parser implements FieldParser {

-        private static Log log = LogFactory.getLog(Parser.class);

-

-        public Field parse(final String name, final String body, final String raw) {

-            Mailbox mailbox = null;

-            ParseException parseException = null;

-            try {

-                MailboxList mailboxList = AddressList.parse(body).flatten();

-                if (mailboxList.size() > 0) {

-                    mailbox = mailboxList.get(0);

-                }

-            }

-            catch (ParseException e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = e;

-            }

-            return new MailboxField(name, body, raw, mailbox, parseException);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.Mailbox;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class MailboxField extends Field {
+    private final Mailbox mailbox;
+    private final ParseException parseException;
+
+    protected MailboxField(final String name, final String body, final String raw, final Mailbox mailbox, final ParseException parseException) {
+        super(name, body, raw);
+        this.mailbox = mailbox;
+        this.parseException = parseException;
+    }
+
+    public Mailbox getMailbox() {
+        return mailbox;
+    }
+
+    public ParseException getParseException() {
+        return parseException;
+    }
+    
+    public static class Parser implements FieldParser {
+        private static Log log = LogFactory.getLog(Parser.class);
+
+        public Field parse(final String name, final String body, final String raw) {
+            Mailbox mailbox = null;
+            ParseException parseException = null;
+            try {
+                MailboxList mailboxList = AddressList.parse(body).flatten();
+                if (mailboxList.size() > 0) {
+                    mailbox = mailboxList.get(0);
+                }
+            }
+            catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = e;
+            }
+            return new MailboxField(name, body, raw, mailbox, parseException);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/MailboxListField.java b/src/org/apache/james/mime4j/field/MailboxListField.java
index 6364fc8..23378d4 100644
--- a/src/org/apache/james/mime4j/field/MailboxListField.java
+++ b/src/org/apache/james/mime4j/field/MailboxListField.java
@@ -1,67 +1,67 @@
-/****************************************************************

- * 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.field;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.field.address.AddressList;

-import org.apache.james.mime4j.field.address.MailboxList;

-import org.apache.james.mime4j.field.address.parser.ParseException;

-

-public class MailboxListField extends Field {

-    

-    private MailboxList mailboxList;

-    private ParseException parseException;

-

-    protected MailboxListField(final String name, final String body, final String raw, final MailboxList mailboxList, final ParseException parseException) {

-        super(name, body, raw);

-        this.mailboxList = mailboxList;

-        this.parseException = parseException;

-    }

-

-    public MailboxList getMailboxList() {

-        return mailboxList;

-    }

-

-    public ParseException getParseException() {

-        return parseException;

-    }

-    

-    public static class Parser implements FieldParser {

-        private static Log log = LogFactory.getLog(Parser.class);

-

-        public Field parse(final String name, final String body, final String raw) {

-            MailboxList mailboxList = null;

-            ParseException parseException = null;

-            try {

-                mailboxList = AddressList.parse(body).flatten();

-            }

-            catch (ParseException e) {

-                if (log.isDebugEnabled()) {

-                    log.debug("Parsing value '" + body + "': "+ e.getMessage());

-                }

-                parseException = e;

-            }

-            return new MailboxListField(name, body, raw, mailboxList, parseException);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class MailboxListField extends Field {
+    
+    private MailboxList mailboxList;
+    private ParseException parseException;
+
+    protected MailboxListField(final String name, final String body, final String raw, final MailboxList mailboxList, final ParseException parseException) {
+        super(name, body, raw);
+        this.mailboxList = mailboxList;
+        this.parseException = parseException;
+    }
+
+    public MailboxList getMailboxList() {
+        return mailboxList;
+    }
+
+    public ParseException getParseException() {
+        return parseException;
+    }
+    
+    public static class Parser implements FieldParser {
+        private static Log log = LogFactory.getLog(Parser.class);
+
+        public Field parse(final String name, final String body, final String raw) {
+            MailboxList mailboxList = null;
+            ParseException parseException = null;
+            try {
+                mailboxList = AddressList.parse(body).flatten();
+            }
+            catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsing value '" + body + "': "+ e.getMessage());
+                }
+                parseException = e;
+            }
+            return new MailboxListField(name, body, raw, mailboxList, parseException);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/UnstructuredField.java b/src/org/apache/james/mime4j/field/UnstructuredField.java
index 5e2adf9..6084e44 100644
--- a/src/org/apache/james/mime4j/field/UnstructuredField.java
+++ b/src/org/apache/james/mime4j/field/UnstructuredField.java
@@ -1,49 +1,49 @@
-/****************************************************************

- * 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.field;

-

-import org.apache.james.mime4j.decoder.DecoderUtil;

-

-

-/**

- * Simple unstructured field such as <code>Subject</code>.

- *

- * 

- * @version $Id: UnstructuredField.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $

- */

-public class UnstructuredField extends Field {

-    private String value;

-    

-    protected UnstructuredField(String name, String body, String raw, String value) {

-        super(name, body, raw);

-        this.value = value;

-    }

-

-    public String getValue() {

-        return value;

-    }

-

-    public static class Parser implements FieldParser {

-        public Field parse(final String name, final String body, final String raw) {

-            final String value = DecoderUtil.decodeEncodedWords(body);

-            return new UnstructuredField(name, body, raw, value);

-        }

-    }

-}

+/****************************************************************
+ * 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.field;
+
+import org.apache.james.mime4j.decoder.DecoderUtil;
+
+
+/**
+ * Simple unstructured field such as <code>Subject</code>.
+ *
+ * 
+ * @version $Id: UnstructuredField.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $
+ */
+public class UnstructuredField extends Field {
+    private String value;
+    
+    protected UnstructuredField(String name, String body, String raw, String value) {
+        super(name, body, raw);
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public static class Parser implements FieldParser {
+        public Field parse(final String name, final String body, final String raw) {
+            final String value = DecoderUtil.decodeEncodedWords(body);
+            return new UnstructuredField(name, body, raw, value);
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/address/Address.java b/src/org/apache/james/mime4j/field/address/Address.java
index 2a5d61e3..3e24e91 100644
--- a/src/org/apache/james/mime4j/field/address/Address.java
+++ b/src/org/apache/james/mime4j/field/address/Address.java
@@ -1,52 +1,52 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-

-/**

- * The abstract base for classes that represent RFC2822 addresses.

- * This includes groups and mailboxes.

- *

- * Currently, no public methods are introduced on this class.

- *

- *

- */

-public abstract class Address {

-

-	/**

-	 * Adds any mailboxes represented by this address

-	 * into the given ArrayList. Note that this method

-	 * has default (package) access, so a doAddMailboxesTo

-	 * method is needed to allow the behavior to be

-	 * overridden by subclasses.

-	 */

-	final void addMailboxesTo(ArrayList<Address> results) {

-		doAddMailboxesTo(results);

-	}

-

-	/**

-	 * Adds any mailboxes represented by this address

-	 * into the given ArrayList. Must be overridden by

-	 * concrete subclasses.

-	 */

-	protected abstract void doAddMailboxesTo(ArrayList<Address> results);

-

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * The abstract base for classes that represent RFC2822 addresses.
+ * This includes groups and mailboxes.
+ *
+ * Currently, no public methods are introduced on this class.
+ *
+ *
+ */
+public abstract class Address {
+
+	/**
+	 * Adds any mailboxes represented by this address
+	 * into the given ArrayList. Note that this method
+	 * has default (package) access, so a doAddMailboxesTo
+	 * method is needed to allow the behavior to be
+	 * overridden by subclasses.
+	 */
+	final void addMailboxesTo(ArrayList<Address> results) {
+		doAddMailboxesTo(results);
+	}
+
+	/**
+	 * Adds any mailboxes represented by this address
+	 * into the given ArrayList. Must be overridden by
+	 * concrete subclasses.
+	 */
+	protected abstract void doAddMailboxesTo(ArrayList<Address> results);
+
+}
diff --git a/src/org/apache/james/mime4j/field/address/AddressList.java b/src/org/apache/james/mime4j/field/address/AddressList.java
index fa4d02c..1829e79 100644
--- a/src/org/apache/james/mime4j/field/address/AddressList.java
+++ b/src/org/apache/james/mime4j/field/address/AddressList.java
@@ -1,138 +1,138 @@
-/****************************************************************

- * 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.field.address;

-

-import org.apache.james.mime4j.field.address.parser.AddressListParser;

-import org.apache.james.mime4j.field.address.parser.ParseException;

-

-import java.io.StringReader;

-import java.util.ArrayList;

-

-/**

- * An immutable, random-access list of Address objects.

- *

- *

- */

-public class AddressList {

-

-	private ArrayList<Address> addresses;

-

-	/**

-	 * @param addresses An ArrayList that contains only Address objects.

-	 * @param dontCopy true iff it is not possible for the addresses ArrayList to be modified by someone else.

-	 */

-	public AddressList(ArrayList<Address> addresses, boolean dontCopy) {

-		if (addresses != null)

-			this.addresses = (dontCopy ? addresses : new ArrayList<Address>(addresses));

-		else

-			this.addresses = new ArrayList<Address>(0);

-	}

-

-	/**

-	 * The number of elements in this list.

-	 */

-	public int size() {

-		return addresses.size();

-	}

-

-	/**

-	 * Gets an address.

-	 */

-	public Address get(int index) {

-		if (0 > index || size() <= index)

-			throw new IndexOutOfBoundsException();

-		return addresses.get(index);

-	}

-

-	/**

-	 * Returns a flat list of all mailboxes represented

-	 * in this address list. Use this if you don't care

-	 * about grouping.

-	 */

-	public MailboxList flatten() {

-		// in the common case, all addresses are mailboxes

-		boolean groupDetected = false;

-		for (int i = 0; i < size(); i++) {

-			if (!(get(i) instanceof Mailbox)) {

-				groupDetected = true;

-				break;

-			}

-		}

-

-		if (!groupDetected)

-			return new MailboxList(addresses, true);

-

-		ArrayList<Address> results = new ArrayList<Address>();

-		for (int i = 0; i < size(); i++) {

-			Address addr = get(i);

-			addr.addMailboxesTo(results);

-		}

-

-		// copy-on-construct this time, because subclasses

-		// could have held onto a reference to the results

-		return new MailboxList(results, false);

-	}

-

-	/**

-	 * Dumps a representation of this address list to

-	 * stdout, for debugging purposes.

-	 */

-	public void print() {

-		for (int i = 0; i < size(); i++) {

-			Address addr = get(i);

-			System.out.println(addr.toString());

-		}

-	}

-

-	/**

-	 * Parse the address list string, such as the value

-	 * of a From, To, Cc, Bcc, Sender, or Reply-To

-	 * header.

-	 *

-	 * The string MUST be unfolded already.

-	 */

-	public static AddressList parse(String rawAddressList) throws ParseException {

-		AddressListParser parser = new AddressListParser(new StringReader(rawAddressList));

-		return Builder.getInstance().buildAddressList(parser.parse());

-	}

-

-	/**

-	 * Test console.

-	 */

-	public static void main(String[] args) throws Exception {

-		java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));

-		while (true) {

-			try {

-				System.out.print("> ");

-				String line = reader.readLine();

-				if (line.length() == 0 || line.toLowerCase().equals("exit") || line.toLowerCase().equals("quit")) {

-					System.out.println("Goodbye.");

-					return;

-				}

-				AddressList list = parse(line);

-				list.print();

-			}

-			catch(Exception e) {

-				e.printStackTrace();

-				Thread.sleep(300);

-			}

-		}

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+import org.apache.james.mime4j.field.address.parser.AddressListParser;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Address objects.
+ *
+ *
+ */
+public class AddressList {
+
+	private ArrayList<Address> addresses;
+
+	/**
+	 * @param addresses An ArrayList that contains only Address objects.
+	 * @param dontCopy true iff it is not possible for the addresses ArrayList to be modified by someone else.
+	 */
+	public AddressList(ArrayList<Address> addresses, boolean dontCopy) {
+		if (addresses != null)
+			this.addresses = (dontCopy ? addresses : new ArrayList<Address>(addresses));
+		else
+			this.addresses = new ArrayList<Address>(0);
+	}
+
+	/**
+	 * The number of elements in this list.
+	 */
+	public int size() {
+		return addresses.size();
+	}
+
+	/**
+	 * Gets an address.
+	 */
+	public Address get(int index) {
+		if (0 > index || size() <= index)
+			throw new IndexOutOfBoundsException();
+		return addresses.get(index);
+	}
+
+	/**
+	 * Returns a flat list of all mailboxes represented
+	 * in this address list. Use this if you don't care
+	 * about grouping.
+	 */
+	public MailboxList flatten() {
+		// in the common case, all addresses are mailboxes
+		boolean groupDetected = false;
+		for (int i = 0; i < size(); i++) {
+			if (!(get(i) instanceof Mailbox)) {
+				groupDetected = true;
+				break;
+			}
+		}
+
+		if (!groupDetected)
+			return new MailboxList(addresses, true);
+
+		ArrayList<Address> results = new ArrayList<Address>();
+		for (int i = 0; i < size(); i++) {
+			Address addr = get(i);
+			addr.addMailboxesTo(results);
+		}
+
+		// copy-on-construct this time, because subclasses
+		// could have held onto a reference to the results
+		return new MailboxList(results, false);
+	}
+
+	/**
+	 * Dumps a representation of this address list to
+	 * stdout, for debugging purposes.
+	 */
+	public void print() {
+		for (int i = 0; i < size(); i++) {
+			Address addr = get(i);
+			System.out.println(addr.toString());
+		}
+	}
+
+	/**
+	 * Parse the address list string, such as the value
+	 * of a From, To, Cc, Bcc, Sender, or Reply-To
+	 * header.
+	 *
+	 * The string MUST be unfolded already.
+	 */
+	public static AddressList parse(String rawAddressList) throws ParseException {
+		AddressListParser parser = new AddressListParser(new StringReader(rawAddressList));
+		return Builder.getInstance().buildAddressList(parser.parse());
+	}
+
+	/**
+	 * Test console.
+	 */
+	public static void main(String[] args) throws Exception {
+		java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
+		while (true) {
+			try {
+				System.out.print("> ");
+				String line = reader.readLine();
+				if (line.length() == 0 || line.toLowerCase().equals("exit") || line.toLowerCase().equals("quit")) {
+					System.out.println("Goodbye.");
+					return;
+				}
+				AddressList list = parse(line);
+				list.print();
+			}
+			catch(Exception e) {
+				e.printStackTrace();
+				Thread.sleep(300);
+			}
+		}
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/Builder.java b/src/org/apache/james/mime4j/field/address/Builder.java
index a2bd3f0..3bcd15b 100644
--- a/src/org/apache/james/mime4j/field/address/Builder.java
+++ b/src/org/apache/james/mime4j/field/address/Builder.java
@@ -1,243 +1,243 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-import java.util.Iterator;

-

-import org.apache.james.mime4j.decoder.DecoderUtil;

-import org.apache.james.mime4j.field.address.parser.ASTaddr_spec;

-import org.apache.james.mime4j.field.address.parser.ASTaddress;

-import org.apache.james.mime4j.field.address.parser.ASTaddress_list;

-import org.apache.james.mime4j.field.address.parser.ASTangle_addr;

-import org.apache.james.mime4j.field.address.parser.ASTdomain;

-import org.apache.james.mime4j.field.address.parser.ASTgroup_body;

-import org.apache.james.mime4j.field.address.parser.ASTlocal_part;

-import org.apache.james.mime4j.field.address.parser.ASTmailbox;

-import org.apache.james.mime4j.field.address.parser.ASTname_addr;

-import org.apache.james.mime4j.field.address.parser.ASTphrase;

-import org.apache.james.mime4j.field.address.parser.ASTroute;

-import org.apache.james.mime4j.field.address.parser.Node;

-import org.apache.james.mime4j.field.address.parser.SimpleNode;

-import org.apache.james.mime4j.field.address.parser.Token;

-

-/**

- * Transforms the JJTree-generated abstract syntax tree

- * into a graph of org.apache.james.mime4j.field.address objects.

- *

- *

- */

-class Builder {

-

-	private static Builder singleton = new Builder();

-

-	public static Builder getInstance() {

-		return singleton;

-	}

-

-

-

-	public AddressList buildAddressList(ASTaddress_list node) {

-		ArrayList<Address> list = new ArrayList<Address>();

-		for (int i = 0; i < node.jjtGetNumChildren(); i++) {

-			ASTaddress childNode = (ASTaddress) node.jjtGetChild(i);

-			Address address = buildAddress(childNode);

-			list.add(address);

-		}

-		return new AddressList(list, true);

-	}

-

-	private Address buildAddress(ASTaddress node) {

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		Node n = it.nextNode();

-		if (n instanceof ASTaddr_spec) {

-			return buildAddrSpec((ASTaddr_spec)n);

-		}

-		else if (n instanceof ASTangle_addr) {

-			return buildAngleAddr((ASTangle_addr)n);

-		}

-		else if (n instanceof ASTphrase) {

-			String name = buildString((ASTphrase)n, false);

-			Node n2 = it.nextNode();

-			if (n2 instanceof ASTgroup_body) {

-				return new Group(name, buildGroupBody((ASTgroup_body)n2));

-			}

-			else if (n2 instanceof ASTangle_addr) {

-                name = DecoderUtil.decodeEncodedWords(name);

-				return new NamedMailbox(name, buildAngleAddr((ASTangle_addr)n2));

-			}

-			else {

-				throw new IllegalStateException();

-			}

-		}

-		else {

-			throw new IllegalStateException();

-		}

-	}

-

-

-

-	private MailboxList buildGroupBody(ASTgroup_body node) {

-		ArrayList<Address> results = new ArrayList<Address>();

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		while (it.hasNext()) {

-			Node n = it.nextNode();

-			if (n instanceof ASTmailbox)

-				results.add(buildMailbox((ASTmailbox)n));

-			else

-				throw new IllegalStateException();

-		}

-		return new MailboxList(results, true);

-	}

-

-	private Mailbox buildMailbox(ASTmailbox node) {

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		Node n = it.nextNode();

-		if (n instanceof ASTaddr_spec) {

-			return buildAddrSpec((ASTaddr_spec)n);

-		}

-		else if (n instanceof ASTangle_addr) {

-			return buildAngleAddr((ASTangle_addr)n);

-		}

-		else if (n instanceof ASTname_addr) {

-			return buildNameAddr((ASTname_addr)n);

-		}

-		else {

-			throw new IllegalStateException();

-		}

-	}

-

-	private NamedMailbox buildNameAddr(ASTname_addr node) {

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		Node n = it.nextNode();

-		String name;

-		if (n instanceof ASTphrase) {

-			name = buildString((ASTphrase)n, false);

-		}

-		else {

-			throw new IllegalStateException();

-		}

-

-		n = it.nextNode();

-		if (n instanceof ASTangle_addr) {

-            name = DecoderUtil.decodeEncodedWords(name);

-			return new NamedMailbox(name, buildAngleAddr((ASTangle_addr) n));

-		}

-		else {

-			throw new IllegalStateException();

-		}

-	}

-

-	private Mailbox buildAngleAddr(ASTangle_addr node) {

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		DomainList route = null;

-		Node n = it.nextNode();

-		if (n instanceof ASTroute) {

-			route = buildRoute((ASTroute)n);

-			n = it.nextNode();

-		}

-		else if (n instanceof ASTaddr_spec)

-			; // do nothing

-		else

-			throw new IllegalStateException();

-

-		if (n instanceof ASTaddr_spec)

-			return buildAddrSpec(route, (ASTaddr_spec)n);

-		else

-			throw new IllegalStateException();

-	}

-

-	private DomainList buildRoute(ASTroute node) {

-		ArrayList<String> results = new ArrayList<String>(node.jjtGetNumChildren());

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		while (it.hasNext()) {

-			Node n = it.nextNode();

-			if (n instanceof ASTdomain)

-				results.add(buildString((ASTdomain)n, true));

-			else

-				throw new IllegalStateException();

-		}

-		return new DomainList(results, true);

-	}

-

-	private Mailbox buildAddrSpec(ASTaddr_spec node) {

-		return buildAddrSpec(null, node);

-	}

-	private Mailbox buildAddrSpec(DomainList route, ASTaddr_spec node) {

-		ChildNodeIterator it = new ChildNodeIterator(node);

-		String localPart = buildString((ASTlocal_part)it.nextNode(), true);

-		String domain = buildString((ASTdomain)it.nextNode(), true);

-		return new Mailbox(route, localPart, domain);

-	}

-

-

-	private String buildString(SimpleNode node, boolean stripSpaces) {

-		Token head = node.firstToken;

-		Token tail = node.lastToken;

-		StringBuffer out = new StringBuffer();

-

-		while (head != tail) {

-			out.append(head.image);

-			head = head.next;

-			if (!stripSpaces)

-				addSpecials(out, head.specialToken);

-		}

-		out.append(tail.image);

-

-		return out.toString();

-	}

-

-	private void addSpecials(StringBuffer out, Token specialToken) {

-		if (specialToken != null) {

-			addSpecials(out, specialToken.specialToken);

-			out.append(specialToken.image);

-		}

-	}

-

-	private static class ChildNodeIterator implements Iterator<Node> {

-

-		private SimpleNode simpleNode;

-		private int index;

-		private int len;

-

-		public ChildNodeIterator(SimpleNode simpleNode) {

-			this.simpleNode = simpleNode;

-			this.len = simpleNode.jjtGetNumChildren();

-			this.index = 0;

-		}

-

-		public void remove() {

-			throw new UnsupportedOperationException();

-		}

-

-		public boolean hasNext() {

-			return index < len;

-		}

-

-		public Node next() {

-			return nextNode();

-		}

-

-		public Node nextNode() {

-			return simpleNode.jjtGetChild(index++);

-		}

-

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.james.mime4j.decoder.DecoderUtil;
+import org.apache.james.mime4j.field.address.parser.ASTaddr_spec;
+import org.apache.james.mime4j.field.address.parser.ASTaddress;
+import org.apache.james.mime4j.field.address.parser.ASTaddress_list;
+import org.apache.james.mime4j.field.address.parser.ASTangle_addr;
+import org.apache.james.mime4j.field.address.parser.ASTdomain;
+import org.apache.james.mime4j.field.address.parser.ASTgroup_body;
+import org.apache.james.mime4j.field.address.parser.ASTlocal_part;
+import org.apache.james.mime4j.field.address.parser.ASTmailbox;
+import org.apache.james.mime4j.field.address.parser.ASTname_addr;
+import org.apache.james.mime4j.field.address.parser.ASTphrase;
+import org.apache.james.mime4j.field.address.parser.ASTroute;
+import org.apache.james.mime4j.field.address.parser.Node;
+import org.apache.james.mime4j.field.address.parser.SimpleNode;
+import org.apache.james.mime4j.field.address.parser.Token;
+
+/**
+ * Transforms the JJTree-generated abstract syntax tree
+ * into a graph of org.apache.james.mime4j.field.address objects.
+ *
+ *
+ */
+class Builder {
+
+	private static Builder singleton = new Builder();
+
+	public static Builder getInstance() {
+		return singleton;
+	}
+
+
+
+	public AddressList buildAddressList(ASTaddress_list node) {
+		ArrayList<Address> list = new ArrayList<Address>();
+		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
+			ASTaddress childNode = (ASTaddress) node.jjtGetChild(i);
+			Address address = buildAddress(childNode);
+			list.add(address);
+		}
+		return new AddressList(list, true);
+	}
+
+	private Address buildAddress(ASTaddress node) {
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		Node n = it.nextNode();
+		if (n instanceof ASTaddr_spec) {
+			return buildAddrSpec((ASTaddr_spec)n);
+		}
+		else if (n instanceof ASTangle_addr) {
+			return buildAngleAddr((ASTangle_addr)n);
+		}
+		else if (n instanceof ASTphrase) {
+			String name = buildString((ASTphrase)n, false);
+			Node n2 = it.nextNode();
+			if (n2 instanceof ASTgroup_body) {
+				return new Group(name, buildGroupBody((ASTgroup_body)n2));
+			}
+			else if (n2 instanceof ASTangle_addr) {
+                name = DecoderUtil.decodeEncodedWords(name);
+				return new NamedMailbox(name, buildAngleAddr((ASTangle_addr)n2));
+			}
+			else {
+				throw new IllegalStateException();
+			}
+		}
+		else {
+			throw new IllegalStateException();
+		}
+	}
+
+
+
+	private MailboxList buildGroupBody(ASTgroup_body node) {
+		ArrayList<Address> results = new ArrayList<Address>();
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		while (it.hasNext()) {
+			Node n = it.nextNode();
+			if (n instanceof ASTmailbox)
+				results.add(buildMailbox((ASTmailbox)n));
+			else
+				throw new IllegalStateException();
+		}
+		return new MailboxList(results, true);
+	}
+
+	private Mailbox buildMailbox(ASTmailbox node) {
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		Node n = it.nextNode();
+		if (n instanceof ASTaddr_spec) {
+			return buildAddrSpec((ASTaddr_spec)n);
+		}
+		else if (n instanceof ASTangle_addr) {
+			return buildAngleAddr((ASTangle_addr)n);
+		}
+		else if (n instanceof ASTname_addr) {
+			return buildNameAddr((ASTname_addr)n);
+		}
+		else {
+			throw new IllegalStateException();
+		}
+	}
+
+	private NamedMailbox buildNameAddr(ASTname_addr node) {
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		Node n = it.nextNode();
+		String name;
+		if (n instanceof ASTphrase) {
+			name = buildString((ASTphrase)n, false);
+		}
+		else {
+			throw new IllegalStateException();
+		}
+
+		n = it.nextNode();
+		if (n instanceof ASTangle_addr) {
+            name = DecoderUtil.decodeEncodedWords(name);
+			return new NamedMailbox(name, buildAngleAddr((ASTangle_addr) n));
+		}
+		else {
+			throw new IllegalStateException();
+		}
+	}
+
+	private Mailbox buildAngleAddr(ASTangle_addr node) {
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		DomainList route = null;
+		Node n = it.nextNode();
+		if (n instanceof ASTroute) {
+			route = buildRoute((ASTroute)n);
+			n = it.nextNode();
+		}
+		else if (n instanceof ASTaddr_spec)
+			; // do nothing
+		else
+			throw new IllegalStateException();
+
+		if (n instanceof ASTaddr_spec)
+			return buildAddrSpec(route, (ASTaddr_spec)n);
+		else
+			throw new IllegalStateException();
+	}
+
+	private DomainList buildRoute(ASTroute node) {
+		ArrayList<String> results = new ArrayList<String>(node.jjtGetNumChildren());
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		while (it.hasNext()) {
+			Node n = it.nextNode();
+			if (n instanceof ASTdomain)
+				results.add(buildString((ASTdomain)n, true));
+			else
+				throw new IllegalStateException();
+		}
+		return new DomainList(results, true);
+	}
+
+	private Mailbox buildAddrSpec(ASTaddr_spec node) {
+		return buildAddrSpec(null, node);
+	}
+	private Mailbox buildAddrSpec(DomainList route, ASTaddr_spec node) {
+		ChildNodeIterator it = new ChildNodeIterator(node);
+		String localPart = buildString((ASTlocal_part)it.nextNode(), true);
+		String domain = buildString((ASTdomain)it.nextNode(), true);
+		return new Mailbox(route, localPart, domain);
+	}
+
+
+	private String buildString(SimpleNode node, boolean stripSpaces) {
+		Token head = node.firstToken;
+		Token tail = node.lastToken;
+		StringBuffer out = new StringBuffer();
+
+		while (head != tail) {
+			out.append(head.image);
+			head = head.next;
+			if (!stripSpaces)
+				addSpecials(out, head.specialToken);
+		}
+		out.append(tail.image);
+
+		return out.toString();
+	}
+
+	private void addSpecials(StringBuffer out, Token specialToken) {
+		if (specialToken != null) {
+			addSpecials(out, specialToken.specialToken);
+			out.append(specialToken.image);
+		}
+	}
+
+	private static class ChildNodeIterator implements Iterator<Node> {
+
+		private SimpleNode simpleNode;
+		private int index;
+		private int len;
+
+		public ChildNodeIterator(SimpleNode simpleNode) {
+			this.simpleNode = simpleNode;
+			this.len = simpleNode.jjtGetNumChildren();
+			this.index = 0;
+		}
+
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean hasNext() {
+			return index < len;
+		}
+
+		public Node next() {
+			return nextNode();
+		}
+
+		public Node nextNode() {
+			return simpleNode.jjtGetChild(index++);
+		}
+
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/DomainList.java b/src/org/apache/james/mime4j/field/address/DomainList.java
index df5b4de..49b0f3b 100644
--- a/src/org/apache/james/mime4j/field/address/DomainList.java
+++ b/src/org/apache/james/mime4j/field/address/DomainList.java
@@ -1,76 +1,76 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-

-/**

- * An immutable, random-access list of Strings (that

- * are supposedly domain names or domain literals).

- *

- *

- */

-public class DomainList {

-	private ArrayList<String> domains;

-

-	/**

-	 * @param domains An ArrayList that contains only String objects.

-	 * @param dontCopy true iff it is not possible for the domains ArrayList to be modified by someone else.

-	 */

-	public DomainList(ArrayList<String> domains, boolean dontCopy) {

-		if (domains != null)

-			this.domains = (dontCopy ? domains : new ArrayList<String>(domains));

-		else

-			this.domains = new ArrayList<String>(0);

-	}

-

-	/**

-	 * The number of elements in this list.

-	 */

-	public int size() {

-		return domains.size();

-	}

-

-	/**

-	 * Gets the domain name or domain literal at the

-	 * specified index.

-	 * @throws IndexOutOfBoundsException If index is &lt; 0 or &gt;= size().

-	 */

-	public String get(int index) {

-		if (0 > index || size() <= index)

-			throw new IndexOutOfBoundsException();

-		return domains.get(index);

-	}

-

-	/**

-	 * Returns the list of domains formatted as a route

-	 * string (not including the trailing ':').

-	 */

-	public String toRouteString() {

-		StringBuffer out = new StringBuffer();

-		for (int i = 0; i < domains.size(); i++) {

-			out.append("@");

-			out.append(get(i));

-			if (i + 1 < domains.size())

-				out.append(",");

-		}

-		return out.toString();

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Strings (that
+ * are supposedly domain names or domain literals).
+ *
+ *
+ */
+public class DomainList {
+	private ArrayList<String> domains;
+
+	/**
+	 * @param domains An ArrayList that contains only String objects.
+	 * @param dontCopy true iff it is not possible for the domains ArrayList to be modified by someone else.
+	 */
+	public DomainList(ArrayList<String> domains, boolean dontCopy) {
+		if (domains != null)
+			this.domains = (dontCopy ? domains : new ArrayList<String>(domains));
+		else
+			this.domains = new ArrayList<String>(0);
+	}
+
+	/**
+	 * The number of elements in this list.
+	 */
+	public int size() {
+		return domains.size();
+	}
+
+	/**
+	 * Gets the domain name or domain literal at the
+	 * specified index.
+	 * @throws IndexOutOfBoundsException If index is &lt; 0 or &gt;= size().
+	 */
+	public String get(int index) {
+		if (0 > index || size() <= index)
+			throw new IndexOutOfBoundsException();
+		return domains.get(index);
+	}
+
+	/**
+	 * Returns the list of domains formatted as a route
+	 * string (not including the trailing ':').
+	 */
+	public String toRouteString() {
+		StringBuffer out = new StringBuffer();
+		for (int i = 0; i < domains.size(); i++) {
+			out.append("@");
+			out.append(get(i));
+			if (i + 1 < domains.size())
+				out.append(",");
+		}
+		return out.toString();
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/Group.java b/src/org/apache/james/mime4j/field/address/Group.java
index cca2b40..c0ab7f7 100644
--- a/src/org/apache/james/mime4j/field/address/Group.java
+++ b/src/org/apache/james/mime4j/field/address/Group.java
@@ -1,75 +1,75 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-

-/**

- * A named group of zero or more mailboxes.

- *

- *

- */

-public class Group extends Address {

-	private String name;

-	private MailboxList mailboxList;

-

-	/**

-	 * @param name The group name.

-	 * @param mailboxes The mailboxes in this group.

-	 */

-	public Group(String name, MailboxList mailboxes) {

-		this.name = name;

-		this.mailboxList = mailboxes;

-	}

-

-	/**

-	 * Returns the group name.

-	 */

-	public String getName() {

-		return name;

-	}

-

-	/**

-	 * Returns the mailboxes in this group.

-	 */

-	public MailboxList getMailboxes() {

-		return mailboxList;

-	}

-

-	@Override

-	public String toString() {

-		StringBuffer buf = new StringBuffer();

-		buf.append(name);

-		buf.append(":");

-		for (int i = 0; i < mailboxList.size(); i++) {

-			buf.append(mailboxList.get(i).toString());

-			if (i + 1 < mailboxList.size())

-				buf.append(",");

-		}

-		buf.append(";");

-		return buf.toString();

-	}

-

-	@Override

-	protected void doAddMailboxesTo(ArrayList<Address> results) {

-		for (int i = 0; i < mailboxList.size(); i++)

-			results.add(mailboxList.get(i));

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * A named group of zero or more mailboxes.
+ *
+ *
+ */
+public class Group extends Address {
+	private String name;
+	private MailboxList mailboxList;
+
+	/**
+	 * @param name The group name.
+	 * @param mailboxes The mailboxes in this group.
+	 */
+	public Group(String name, MailboxList mailboxes) {
+		this.name = name;
+		this.mailboxList = mailboxes;
+	}
+
+	/**
+	 * Returns the group name.
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Returns the mailboxes in this group.
+	 */
+	public MailboxList getMailboxes() {
+		return mailboxList;
+	}
+
+	@Override
+	public String toString() {
+		StringBuffer buf = new StringBuffer();
+		buf.append(name);
+		buf.append(":");
+		for (int i = 0; i < mailboxList.size(); i++) {
+			buf.append(mailboxList.get(i).toString());
+			if (i + 1 < mailboxList.size())
+				buf.append(",");
+		}
+		buf.append(";");
+		return buf.toString();
+	}
+
+	@Override
+	protected void doAddMailboxesTo(ArrayList<Address> results) {
+		for (int i = 0; i < mailboxList.size(); i++)
+			results.add(mailboxList.get(i));
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/Mailbox.java b/src/org/apache/james/mime4j/field/address/Mailbox.java
index c05a57f..25f2548 100644
--- a/src/org/apache/james/mime4j/field/address/Mailbox.java
+++ b/src/org/apache/james/mime4j/field/address/Mailbox.java
@@ -1,121 +1,121 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-

-/**

- * Represents a single e-mail address.

- *

- *

- */

-public class Mailbox extends Address {

-	private DomainList route;

-	private String localPart;

-	private String domain;

-

-	/**

-	 * Creates a mailbox without a route. Routes are obsolete.

-	 * @param localPart The part of the e-mail address to the left of the "@".

-	 * @param domain The part of the e-mail address to the right of the "@".

-	 */

-	public Mailbox(String localPart, String domain) {

-		this(null, localPart, domain);

-	}

-

-	/**

-	 * Creates a mailbox with a route. Routes are obsolete.

-	 * @param route The zero or more domains that make up the route. Can be null.

-	 * @param localPart The part of the e-mail address to the left of the "@".

-	 * @param domain The part of the e-mail address to the right of the "@".

-	 */

-	public Mailbox(DomainList route, String localPart, String domain) {

-		this.route = route;

-		this.localPart = localPart;

-		this.domain = domain;

-	}

-

-	/**

-	 * Returns the route list.

-	 */

-	public DomainList getRoute() {

-		return route;

-	}

-

-	/**

-	 * Returns the left part of the e-mail address

-	 * (before "@").

-	 */

-	public String getLocalPart() {

-		return localPart;

-	}

-

-	/**

-	 * Returns the right part of the e-mail address

-	 * (after "@").

-	 */

-	public String getDomain() {

-		return domain;

-	}

-

-	/**

-	 * Formats the address as a string, not including

-	 * the route.

-	 *

-	 * @see #getAddressString(boolean)

-	 */

-	public String getAddressString() {

-		return getAddressString(false);

-	}

-

-	/**

-	 * Note that this value may not be usable

-	 * for transport purposes, only display purposes.

-	 *

-	 * For example, if the unparsed address was

-	 *

-	 *   <"Joe Cheng"@joecheng.com>

-	 *

-	 * this method would return

-	 *

-	 *   <Joe Cheng@joecheng.com>

-	 *

-	 * which is not valid for transport; the local part

-	 * would need to be re-quoted.

-	 *

-	 * @param includeRoute true if the route should be included if it exists.

-	 */

-	public String getAddressString(boolean includeRoute) {

-		return "<" + (!includeRoute || route == null ? "" : route.toRouteString() + ":")

-			+ localPart

-			+ (domain == null ? "" : "@")

-			+ domain + ">";

-	}

-

-	@Override

-	protected final void doAddMailboxesTo(ArrayList<Address> results) {

-		results.add(this);

-	}

-

-	@Override

-	public String toString() {

-		return getAddressString();

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a single e-mail address.
+ *
+ *
+ */
+public class Mailbox extends Address {
+	private DomainList route;
+	private String localPart;
+	private String domain;
+
+	/**
+	 * Creates a mailbox without a route. Routes are obsolete.
+	 * @param localPart The part of the e-mail address to the left of the "@".
+	 * @param domain The part of the e-mail address to the right of the "@".
+	 */
+	public Mailbox(String localPart, String domain) {
+		this(null, localPart, domain);
+	}
+
+	/**
+	 * Creates a mailbox with a route. Routes are obsolete.
+	 * @param route The zero or more domains that make up the route. Can be null.
+	 * @param localPart The part of the e-mail address to the left of the "@".
+	 * @param domain The part of the e-mail address to the right of the "@".
+	 */
+	public Mailbox(DomainList route, String localPart, String domain) {
+		this.route = route;
+		this.localPart = localPart;
+		this.domain = domain;
+	}
+
+	/**
+	 * Returns the route list.
+	 */
+	public DomainList getRoute() {
+		return route;
+	}
+
+	/**
+	 * Returns the left part of the e-mail address
+	 * (before "@").
+	 */
+	public String getLocalPart() {
+		return localPart;
+	}
+
+	/**
+	 * Returns the right part of the e-mail address
+	 * (after "@").
+	 */
+	public String getDomain() {
+		return domain;
+	}
+
+	/**
+	 * Formats the address as a string, not including
+	 * the route.
+	 *
+	 * @see #getAddressString(boolean)
+	 */
+	public String getAddressString() {
+		return getAddressString(false);
+	}
+
+	/**
+	 * Note that this value may not be usable
+	 * for transport purposes, only display purposes.
+	 *
+	 * For example, if the unparsed address was
+	 *
+	 *   <"Joe Cheng"@joecheng.com>
+	 *
+	 * this method would return
+	 *
+	 *   <Joe Cheng@joecheng.com>
+	 *
+	 * which is not valid for transport; the local part
+	 * would need to be re-quoted.
+	 *
+	 * @param includeRoute true if the route should be included if it exists.
+	 */
+	public String getAddressString(boolean includeRoute) {
+		return "<" + (!includeRoute || route == null ? "" : route.toRouteString() + ":")
+			+ localPart
+			+ (domain == null ? "" : "@")
+			+ domain + ">";
+	}
+
+	@Override
+	protected final void doAddMailboxesTo(ArrayList<Address> results) {
+		results.add(this);
+	}
+
+	@Override
+	public String toString() {
+		return getAddressString();
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/MailboxList.java b/src/org/apache/james/mime4j/field/address/MailboxList.java
index 25337de..2c9efb3 100644
--- a/src/org/apache/james/mime4j/field/address/MailboxList.java
+++ b/src/org/apache/james/mime4j/field/address/MailboxList.java
@@ -1,71 +1,71 @@
-/****************************************************************

- * 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.field.address;

-

-import java.util.ArrayList;

-

-/**

- * An immutable, random-access list of Mailbox objects.

- *

- *

- */

-public class MailboxList {

-

-	private ArrayList<Address> mailboxes;

-

-	/**

-	 * @param mailboxes An ArrayList that contains only Mailbox objects.

-	 * @param dontCopy true iff it is not possible for the mailboxes ArrayList to be modified by someone else.

-	 */

-	public MailboxList(ArrayList<Address> mailboxes, boolean dontCopy) {

-		if (mailboxes != null)

-			this.mailboxes = (dontCopy ? mailboxes : new ArrayList<Address>(mailboxes));

-		else

-			this.mailboxes = new ArrayList<Address>(0);

-	}

-

-	/**

-	 * The number of elements in this list.

-	 */

-	public int size() {

-		return mailboxes.size();

-	}

-

-	/**

-	 * Gets an address.

-	 */

-	public Mailbox get(int index) {

-		if (0 > index || size() <= index)

-			throw new IndexOutOfBoundsException();

-		return (Mailbox)mailboxes.get(index);

-	}

-

-	/**

-	 * Dumps a representation of this mailbox list to

-	 * stdout, for debugging purposes.

-	 */

-	public void print() {

-		for (int i = 0; i < size(); i++) {

-			Mailbox mailbox = get(i);

-			System.out.println(mailbox.toString());

-		}

-	}

-

-}

+/****************************************************************
+ * 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.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Mailbox objects.
+ *
+ *
+ */
+public class MailboxList {
+
+	private ArrayList<Address> mailboxes;
+
+	/**
+	 * @param mailboxes An ArrayList that contains only Mailbox objects.
+	 * @param dontCopy true iff it is not possible for the mailboxes ArrayList to be modified by someone else.
+	 */
+	public MailboxList(ArrayList<Address> mailboxes, boolean dontCopy) {
+		if (mailboxes != null)
+			this.mailboxes = (dontCopy ? mailboxes : new ArrayList<Address>(mailboxes));
+		else
+			this.mailboxes = new ArrayList<Address>(0);
+	}
+
+	/**
+	 * The number of elements in this list.
+	 */
+	public int size() {
+		return mailboxes.size();
+	}
+
+	/**
+	 * Gets an address.
+	 */
+	public Mailbox get(int index) {
+		if (0 > index || size() <= index)
+			throw new IndexOutOfBoundsException();
+		return (Mailbox)mailboxes.get(index);
+	}
+
+	/**
+	 * Dumps a representation of this mailbox list to
+	 * stdout, for debugging purposes.
+	 */
+	public void print() {
+		for (int i = 0; i < size(); i++) {
+			Mailbox mailbox = get(i);
+			System.out.println(mailbox.toString());
+		}
+	}
+
+}
diff --git a/src/org/apache/james/mime4j/field/address/NamedMailbox.java b/src/org/apache/james/mime4j/field/address/NamedMailbox.java
index a54bd1d..4b83060 100644
--- a/src/org/apache/james/mime4j/field/address/NamedMailbox.java
+++ b/src/org/apache/james/mime4j/field/address/NamedMailbox.java
@@ -1,71 +1,71 @@
-/****************************************************************

- * 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.field.address;

-

-/**

- * A Mailbox that has a name/description.

- *

- *

- */

-public class NamedMailbox extends Mailbox {

-	private String name;

-

-	/**

-	 * @see Mailbox#Mailbox(String, String)

-	 */

-	public NamedMailbox(String name, String localPart, String domain) {

-		super(localPart, domain);

-		this.name = name;

-	}

-

-	/**

-	 * @see Mailbox#Mailbox(DomainList, String, String)

-	 */

-	public NamedMailbox(String name, DomainList route, String localPart, String domain) {

-		super(route, localPart, domain);

-		this.name = name;

-	}

-

-	/**

-	 * Creates a named mailbox based on an unnamed mailbox.

-	 */

-	public NamedMailbox(String name, Mailbox baseMailbox) {

-		super(baseMailbox.getRoute(), baseMailbox.getLocalPart(), baseMailbox.getDomain());

-		this.name = name;

-	}

-

-	/**

-	 * Returns the name of the mailbox.

-	 */

-	public String getName() {

-		return this.name;

-	}

-

-	/**

-	 * Same features (or problems) as Mailbox.getAddressString(boolean),

-	 * only more so.

-	 *

-	 * @see Mailbox#getAddressString(boolean)

-	 */

-	@Override

-	public String getAddressString(boolean includeRoute) {

-		return (name == null ? "" : name + " ") + super.getAddressString(includeRoute);

-	}

-}

+/****************************************************************
+ * 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.field.address;
+
+/**
+ * A Mailbox that has a name/description.
+ *
+ *
+ */
+public class NamedMailbox extends Mailbox {
+	private String name;
+
+	/**
+	 * @see Mailbox#Mailbox(String, String)
+	 */
+	public NamedMailbox(String name, String localPart, String domain) {
+		super(localPart, domain);
+		this.name = name;
+	}
+
+	/**
+	 * @see Mailbox#Mailbox(DomainList, String, String)
+	 */
+	public NamedMailbox(String name, DomainList route, String localPart, String domain) {
+		super(route, localPart, domain);
+		this.name = name;
+	}
+
+	/**
+	 * Creates a named mailbox based on an unnamed mailbox.
+	 */
+	public NamedMailbox(String name, Mailbox baseMailbox) {
+		super(baseMailbox.getRoute(), baseMailbox.getLocalPart(), baseMailbox.getDomain());
+		this.name = name;
+	}
+
+	/**
+	 * Returns the name of the mailbox.
+	 */
+	public String getName() {
+		return this.name;
+	}
+
+	/**
+	 * Same features (or problems) as Mailbox.getAddressString(boolean),
+	 * only more so.
+	 *
+	 * @see Mailbox#getAddressString(boolean)
+	 */
+	@Override
+	public String getAddressString(boolean includeRoute) {
+		return (name == null ? "" : name + " ") + super.getAddressString(includeRoute);
+	}
+}
diff --git a/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java b/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java
index dde2a08..8094df0 100644
--- a/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java
+++ b/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java
@@ -1,18 +1,18 @@
 /* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParser.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj b/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj
index 6859886..c14277b 100644
--- a/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj
+++ b/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj
@@ -1,124 +1,124 @@
 /*@bgen(jjtree) Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParser.jj */
-/*@egen*//****************************************************************

- * 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.                                           *

- ****************************************************************/

-

-

-/**

- * RFC2822 address list parser.

- *

- * Created 9/17/2004

- * by Joe Cheng <code@joecheng.com>

- */

-

-options {

-	STATIC=false;

-	LOOKAHEAD=1;                                                                                                                               

-	//DEBUG_PARSER=true;

-	//DEBUG_TOKEN_MANAGER=true;

-}

-

-PARSER_BEGIN(AddressListParser)

-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.field.address.parser;

-

+/*@egen*//****************************************************************
+ * 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.                                           *
+ ****************************************************************/
+
+
+/**
+ * RFC2822 address list parser.
+ *
+ * Created 9/17/2004
+ * by Joe Cheng <code@joecheng.com>
+ */
+
+options {
+	STATIC=false;
+	LOOKAHEAD=1;                                                                                                                               
+	//DEBUG_PARSER=true;
+	//DEBUG_TOKEN_MANAGER=true;
+}
+
+PARSER_BEGIN(AddressListParser)
+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
+
 public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/
   protected JJTAddressListParserState jjtree = new JJTAddressListParserState();
 
-/*@egen*/

-    public static void main(String args[]) throws ParseException {

-		while (true) {

-		    try {

-				AddressListParser parser = new AddressListParser(System.in);

-		    	parser.parseLine();

-		    	((SimpleNode)parser.jjtree.rootNode()).dump("> ");

-		    } catch (Exception x) {

-				x.printStackTrace();

-				return;

-		    }

-		}

-    }

-    

-    private static void log(String msg) {

-    	System.out.print(msg);

-    }

-    

-    public ASTaddress_list parse() throws ParseException {

-        try {

-    	    parseAll();

-    	    return (ASTaddress_list)jjtree.rootNode();

-    	} catch (TokenMgrError tme) {

-    	    throw new ParseException(tme.getMessage());

-    	}

-    }

-    

-    

-    void jjtreeOpenNodeScope(Node n) {

-    	((SimpleNode)n).firstToken = getToken(1);

-    }

-    

-    void jjtreeCloseNodeScope(Node n) {

-    	((SimpleNode)n).lastToken = getToken(0);

-    }

-}

-

-PARSER_END(AddressListParser)

-

-void parseLine()       :

-{}

-{

-	address_list() ["\r"] "\n"

-}

-

-void parseAll()       :

-{}

-{

-	address_list() <EOF>

-}

-

-void address_list() :

+/*@egen*/
+    public static void main(String args[]) throws ParseException {
+		while (true) {
+		    try {
+				AddressListParser parser = new AddressListParser(System.in);
+		    	parser.parseLine();
+		    	((SimpleNode)parser.jjtree.rootNode()).dump("> ");
+		    } catch (Exception x) {
+				x.printStackTrace();
+				return;
+		    }
+		}
+    }
+    
+    private static void log(String msg) {
+    	System.out.print(msg);
+    }
+    
+    public ASTaddress_list parse() throws ParseException {
+        try {
+    	    parseAll();
+    	    return (ASTaddress_list)jjtree.rootNode();
+    	} catch (TokenMgrError tme) {
+    	    throw new ParseException(tme.getMessage());
+    	}
+    }
+    
+    
+    void jjtreeOpenNodeScope(Node n) {
+    	((SimpleNode)n).firstToken = getToken(1);
+    }
+    
+    void jjtreeCloseNodeScope(Node n) {
+    	((SimpleNode)n).lastToken = getToken(0);
+    }
+}
+
+PARSER_END(AddressListParser)
+
+void parseLine()       :
+{}
+{
+	address_list() ["\r"] "\n"
+}
+
+void parseAll()       :
+{}
+{
+	address_list() <EOF>
+}
+
+void address_list() :
 {/*@bgen(jjtree) address_list */
   ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) address_list */
         try {
-/*@egen*/

-	[ address() ]

-	(

-		","

-		[ address() ]

+/*@egen*/
+	[ address() ]
+	(
+		","
+		[ address() ]
 	)*/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -140,22 +140,22 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void address() :

+/*@egen*/
+}
+
+void address() :
 {/*@bgen(jjtree) address */
   ASTaddress jjtn000 = new ASTaddress(JJTADDRESS);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) address */
         try {
-/*@egen*/

-	LOOKAHEAD(2147483647)

-	addr_spec()

-|	angle_addr()

+/*@egen*/
+	LOOKAHEAD(2147483647)
+	addr_spec()
+|	angle_addr()
 |	( phrase() (group_body() | angle_addr()) )/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -177,22 +177,22 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void mailbox() :

+/*@egen*/
+}
+
+void mailbox() :
 {/*@bgen(jjtree) mailbox */
   ASTmailbox jjtn000 = new ASTmailbox(JJTMAILBOX);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) mailbox */
         try {
-/*@egen*/

-	LOOKAHEAD(2147483647)

-	addr_spec()

-|	angle_addr()

+/*@egen*/
+	LOOKAHEAD(2147483647)
+	addr_spec()
+|	angle_addr()
 |	name_addr()/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -214,19 +214,19 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void name_addr() :

+/*@egen*/
+}
+
+void name_addr() :
 {/*@bgen(jjtree) name_addr */
   ASTname_addr jjtn000 = new ASTname_addr(JJTNAME_ADDR);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) name_addr */
         try {
-/*@egen*/

+/*@egen*/
 	phrase() angle_addr()/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -248,25 +248,25 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void group_body() :

+/*@egen*/
+}
+
+void group_body() :
 {/*@bgen(jjtree) group_body */
   ASTgroup_body jjtn000 = new ASTgroup_body(JJTGROUP_BODY);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) group_body */
         try {
-/*@egen*/

-	":"

-	[ mailbox() ]

-	(

-		","

-		[ mailbox() ]

-	)*

+/*@egen*/
+	":"
+	[ mailbox() ]
+	(
+		","
+		[ mailbox() ]
+	)*
 	";"/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -288,19 +288,19 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void angle_addr() :

+/*@egen*/
+}
+
+void angle_addr() :
 {/*@bgen(jjtree) angle_addr */
   ASTangle_addr jjtn000 = new ASTangle_addr(JJTANGLE_ADDR);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) angle_addr */
         try {
-/*@egen*/

+/*@egen*/
 	"<" [ route() ] addr_spec() ">"/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -322,19 +322,19 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void route() :

+/*@egen*/
+}
+
+void route() :
 {/*@bgen(jjtree) route */
   ASTroute jjtn000 = new ASTroute(JJTROUTE);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) route */
         try {
-/*@egen*/

+/*@egen*/
 	"@" domain() ( (",")* "@" domain() )* ":"/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -356,21 +356,21 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void phrase() :

+/*@egen*/
+}
+
+void phrase() :
 {/*@bgen(jjtree) phrase */
   ASTphrase jjtn000 = new ASTphrase(JJTPHRASE);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) phrase */
 try {
-/*@egen*/

-(	<DOTATOM>

-|	<QUOTEDSTRING>

+/*@egen*/
+(	<DOTATOM>
+|	<QUOTEDSTRING>
 )+/*@bgen(jjtree)*/
 } finally {
   if (jjtc000) {
@@ -378,19 +378,19 @@
     jjtreeCloseNodeScope(jjtn000);
   }
 }
-/*@egen*/

-}

-

-void addr_spec() :

+/*@egen*/
+}
+
+void addr_spec() :
 {/*@bgen(jjtree) addr_spec */
   ASTaddr_spec jjtn000 = new ASTaddr_spec(JJTADDR_SPEC);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/}

+/*@egen*/}
 {/*@bgen(jjtree) addr_spec */
         try {
-/*@egen*/

+/*@egen*/
 	( local_part() "@" domain() )/*@bgen(jjtree)*/
         } catch (Throwable jjte000) {
           if (jjtc000) {
@@ -412,26 +412,26 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void local_part() :

+/*@egen*/
+}
+
+void local_part() :
 {/*@bgen(jjtree) local_part */
   ASTlocal_part jjtn000 = new ASTlocal_part(JJTLOCAL_PART);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/ Token t; }

+/*@egen*/ Token t; }
 {/*@bgen(jjtree) local_part */
         try {
-/*@egen*/

-	( t=<DOTATOM> | t=<QUOTEDSTRING> )

-	(	[t="."]

-		{

-			if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)

-				throw new ParseException("Words in local part must be separated by '.'");

-		}

-		(	t=<DOTATOM> | t=<QUOTEDSTRING> )

+/*@egen*/
+	( t=<DOTATOM> | t=<QUOTEDSTRING> )
+	(	[t="."]
+		{
+			if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
+				throw new ParseException("Words in local part must be separated by '.'");
+		}
+		(	t=<DOTATOM> | t=<QUOTEDSTRING> )
 	)*/*@bgen(jjtree)*/
         } finally {
           if (jjtc000) {
@@ -439,28 +439,28 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-void domain() :

+/*@egen*/
+}
+
+void domain() :
 {/*@bgen(jjtree) domain */
   ASTdomain jjtn000 = new ASTdomain(JJTDOMAIN);
   boolean jjtc000 = true;
   jjtree.openNodeScope(jjtn000);
   jjtreeOpenNodeScope(jjtn000);
-/*@egen*/ Token t; }

+/*@egen*/ Token t; }
 {/*@bgen(jjtree) domain */
         try {
-/*@egen*/

-	(	t=<DOTATOM>

-		(	[t="."]

-			{

-				if (t.image.charAt(t.image.length() - 1) != '.')

-					throw new ParseException("Atoms in domain names must be separated by '.'");

-			}

-			t=<DOTATOM>

-		)*

-	)

+/*@egen*/
+	(	t=<DOTATOM>
+		(	[t="."]
+			{
+				if (t.image.charAt(t.image.length() - 1) != '.')
+					throw new ParseException("Atoms in domain names must be separated by '.'");
+			}
+			t=<DOTATOM>
+		)*
+	)
 |	<DOMAINLITERAL>/*@bgen(jjtree)*/
         } finally {
           if (jjtc000) {
@@ -468,128 +468,128 @@
             jjtreeCloseNodeScope(jjtn000);
           }
         }
-/*@egen*/

-}

-

-SPECIAL_TOKEN :

-{

- 	< WS: ( [" ", "\t"] )+ >

-}

-

-TOKEN :

-{

-	< #ALPHA: ["a" - "z", "A" - "Z"] >

-|	< #DIGIT: ["0" - "9"] >

-|	< #ATEXT: ( <ALPHA> | <DIGIT>

-			  | "!" | "#" | "$" | "%"

-			  | "&" | "'" | "*" | "+"

-			  | "-" | "/" | "=" | "?"

-			  | "^" | "_" | "`" | "{"

-			  | "|" | "}" | "~"

-			  )>

-|	< DOTATOM: <ATEXT> ( <ATEXT> | "." )* >

-}

-

-TOKEN_MGR_DECLS :

-{

-	// Keeps track of how many levels of comment nesting

-	// we've encountered.  This is only used when the 2nd

-	// level is reached, for example ((this)), not (this).

-	// This is because the outermost level must be treated

-	// specially anyway, because the outermost ")" has a 

-	// different token type than inner ")" instances.

-	static int commentNest;

-}

-

-MORE :

-{

-	// domain literal

-	"[" : INDOMAINLITERAL

-}

-

-<INDOMAINLITERAL>

-MORE :

-{

-	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }

-|	< ~["[", "]", "\\"] >

-}

-

-<INDOMAINLITERAL>

-TOKEN :

-{

-	< DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT

-}

-

-MORE :

-{

-	// starts a comment

-	"(" : INCOMMENT

-}

-

-<INCOMMENT>

-SKIP :

-{

-	// ends a comment

-	< COMMENT: ")" > : DEFAULT

-	// if this is ever changed to not be a SKIP, need

-	// to make sure matchedToken.token = token.toString()

-	// is called.

-}

-

-<INCOMMENT>

-MORE :

-{

-	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }

-|	"(" { commentNest = 1; } : NESTED_COMMENT

-|	< <ANY>>

-}

-

-<NESTED_COMMENT>

-MORE :

-{

-	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }

-|	"(" { ++commentNest; }

-|	")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }

-|	< <ANY>>

-}

-

-

-// QUOTED STRINGS

-

-MORE :

-{

-	"\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING

-}

-

-<INQUOTEDSTRING>

-MORE :

-{

-	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }

-|	< (~["\"", "\\"])+ >

-}

-

-<INQUOTEDSTRING>

-TOKEN :

-{

-	< QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT

-}

-

-// GLOBALS

-

-<*>

-TOKEN :

-{

-	< #QUOTEDPAIR: "\\" <ANY> >

-|	< #ANY: ~[] >

-}

-

-// ERROR!

-/*

-

-<*>

-TOKEN :

-{

-	< UNEXPECTED_CHAR: <ANY> >

-}

-

+/*@egen*/
+}
+
+SPECIAL_TOKEN :
+{
+ 	< WS: ( [" ", "\t"] )+ >
+}
+
+TOKEN :
+{
+	< #ALPHA: ["a" - "z", "A" - "Z"] >
+|	< #DIGIT: ["0" - "9"] >
+|	< #ATEXT: ( <ALPHA> | <DIGIT>
+			  | "!" | "#" | "$" | "%"
+			  | "&" | "'" | "*" | "+"
+			  | "-" | "/" | "=" | "?"
+			  | "^" | "_" | "`" | "{"
+			  | "|" | "}" | "~"
+			  )>
+|	< DOTATOM: <ATEXT> ( <ATEXT> | "." )* >
+}
+
+TOKEN_MGR_DECLS :
+{
+	// Keeps track of how many levels of comment nesting
+	// we've encountered.  This is only used when the 2nd
+	// level is reached, for example ((this)), not (this).
+	// This is because the outermost level must be treated
+	// specially anyway, because the outermost ")" has a 
+	// different token type than inner ")" instances.
+	static int commentNest;
+}
+
+MORE :
+{
+	// domain literal
+	"[" : INDOMAINLITERAL
+}
+
+<INDOMAINLITERAL>
+MORE :
+{
+	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+|	< ~["[", "]", "\\"] >
+}
+
+<INDOMAINLITERAL>
+TOKEN :
+{
+	< DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT
+}
+
+MORE :
+{
+	// starts a comment
+	"(" : INCOMMENT
+}
+
+<INCOMMENT>
+SKIP :
+{
+	// ends a comment
+	< COMMENT: ")" > : DEFAULT
+	// if this is ever changed to not be a SKIP, need
+	// to make sure matchedToken.token = token.toString()
+	// is called.
+}
+
+<INCOMMENT>
+MORE :
+{
+	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+|	"(" { commentNest = 1; } : NESTED_COMMENT
+|	< <ANY>>
+}
+
+<NESTED_COMMENT>
+MORE :
+{
+	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+|	"(" { ++commentNest; }
+|	")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }
+|	< <ANY>>
+}
+
+
+// QUOTED STRINGS
+
+MORE :
+{
+	"\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING
+}
+
+<INQUOTEDSTRING>
+MORE :
+{
+	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+|	< (~["\"", "\\"])+ >
+}
+
+<INQUOTEDSTRING>
+TOKEN :
+{
+	< QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT
+}
+
+// GLOBALS
+
+<*>
+TOKEN :
+{
+	< #QUOTEDPAIR: "\\" <ANY> >
+|	< #ANY: ~[] >
+}
+
+// ERROR!
+/*
+
+<*>
+TOKEN :
+{
+	< UNEXPECTED_CHAR: <ANY> >
+}
+
 */
\ No newline at end of file
diff --git a/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java b/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java
index a21ad35..006a082 100644
--- a/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java
+++ b/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java
@@ -1,18 +1,18 @@
 /* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserConstants.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java b/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java
index df8974a..d2dd88d 100644
--- a/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java
+++ b/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java
@@ -1,29 +1,29 @@
 /* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserTokenManager.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
 public class AddressListParserTokenManager implements AddressListParserConstants
 {
-        // Keeps track of how many levels of comment nesting

-        // we've encountered.  This is only used when the 2nd

-        // level is reached, for example ((this)), not (this).

-        // This is because the outermost level must be treated

-        // specially anyway, because the outermost ")" has a 

-        // different token type than inner ")" instances.

+        // Keeps track of how many levels of comment nesting
+        // we've encountered.  This is only used when the 2nd
+        // level is reached, for example ((this)), not (this).
+        // This is because the outermost level must be treated
+        // specially anyway, because the outermost ")" has a 
+        // different token type than inner ")" instances.
         static int commentNest;
   public  java.io.PrintStream debugStream = System.out;
   public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
diff --git a/src/org/apache/james/mime4j/field/address/parser/BaseNode.java b/src/org/apache/james/mime4j/field/address/parser/BaseNode.java
index 42fe3db..7809746 100644
--- a/src/org/apache/james/mime4j/field/address/parser/BaseNode.java
+++ b/src/org/apache/james/mime4j/field/address/parser/BaseNode.java
@@ -1,30 +1,30 @@
-/****************************************************************

- * 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.field.address.parser;

-

-import org.apache.james.mime4j.field.address.parser.Node;

-import org.apache.james.mime4j.field.address.parser.Token;

-

-public abstract class BaseNode implements Node {

-  

-  public Token firstToken;

-  public Token lastToken;

-

+/****************************************************************
+ * 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.field.address.parser;
+
+import org.apache.james.mime4j.field.address.parser.Node;
+import org.apache.james.mime4j.field.address.parser.Token;
+
+public abstract class BaseNode implements Node {
+  
+  public Token firstToken;
+  public Token lastToken;
+
 }
\ No newline at end of file
diff --git a/src/org/apache/james/mime4j/field/address/parser/ParseException.java b/src/org/apache/james/mime4j/field/address/parser/ParseException.java
index 939c6cf..e20146f 100644
--- a/src/org/apache/james/mime4j/field/address/parser/ParseException.java
+++ b/src/org/apache/james/mime4j/field/address/parser/ParseException.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java b/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java
index fb3f70c..c9ba0b4 100644
--- a/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java
+++ b/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/address/parser/Token.java b/src/org/apache/james/mime4j/field/address/parser/Token.java
index 0228aac..2382e8e 100644
--- a/src/org/apache/james/mime4j/field/address/parser/Token.java
+++ b/src/org/apache/james/mime4j/field/address/parser/Token.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java b/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java
index c06a44c..0299c85 100644
--- a/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java
+++ b/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.address.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java
index 9d4a0f5..cacf3af 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. ContentTypeParser.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java
index 8a76328..d933d80 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. ContentTypeParserConstants.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java
index 05d940d..25b7aba 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java
@@ -1,30 +1,30 @@
 /* Generated By:JavaCC: Do not edit this line. ContentTypeParserTokenManager.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 import java.util.ArrayList;
 
 public class ContentTypeParserTokenManager implements ContentTypeParserConstants
 {
-        // Keeps track of how many levels of comment nesting

-        // we've encountered.  This is only used when the 2nd

-        // level is reached, for example ((this)), not (this).

-        // This is because the outermost level must be treated

-        // specially anyway, because the outermost ")" has a

-        // different token type than inner ")" instances.

+        // Keeps track of how many levels of comment nesting
+        // we've encountered.  This is only used when the 2nd
+        // level is reached, for example ((this)), not (this).
+        // This is because the outermost level must be treated
+        // specially anyway, because the outermost ")" has a
+        // different token type than inner ")" instances.
         static int commentNest;
   public  java.io.PrintStream debugStream = System.out;
   public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java b/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java
index 2c5dc82..d9b69b2 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java b/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java
index 2f54316..ae035b7 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/Token.java b/src/org/apache/james/mime4j/field/contenttype/parser/Token.java
index 5bef6cf..34e65ee 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/Token.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/Token.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java b/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java
index 4a490ef..ea5a782 100644
--- a/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java
+++ b/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.contenttype.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/DateTime.java b/src/org/apache/james/mime4j/field/datetime/DateTime.java
index bf00ca7..506ff54 100644
--- a/src/org/apache/james/mime4j/field/datetime/DateTime.java
+++ b/src/org/apache/james/mime4j/field/datetime/DateTime.java
@@ -1,127 +1,127 @@
-/****************************************************************

- * 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.field.datetime;

-

-import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;

-import org.apache.james.mime4j.field.datetime.parser.ParseException;

-import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;

-

-import java.util.Date;

-import java.util.Calendar;

-import java.util.TimeZone;

-import java.util.GregorianCalendar;

-import java.io.StringReader;

-

-public class DateTime {

-    private final Date date;

-    private final int year;

-    private final int month;

-    private final int day;

-    private final int hour;

-    private final int minute;

-    private final int second;

-    private final int timeZone;

-

-    public DateTime(String yearString, int month, int day, int hour, int minute, int second, int timeZone) {

-        this.year = convertToYear(yearString);

-        this.date = convertToDate(year, month, day, hour, minute, second, timeZone);

-        this.month = month;

-        this.day = day;

-        this.hour = hour;

-        this.minute = minute;

-        this.second = second;

-        this.timeZone = timeZone;

-    }

-

-    private int convertToYear(String yearString) {

-        int year = Integer.parseInt(yearString);

-        switch (yearString.length()) {

-            case 1:

-            case 2:

-                if (year >= 0 && year < 50)

-                    return 2000 + year;

-                else

-                    return 1900 + year;

-            case 3:

-                return 1900 + year;

-            default:

-                return year;

-        }

-    }

-

-    public static Date convertToDate(int year, int month, int day, int hour, int minute, int second, int timeZone) {

-        Calendar c = new GregorianCalendar(TimeZone.getTimeZone("GMT+0"));

-        c.set(year, month - 1, day, hour, minute, second);

-        c.set(Calendar.MILLISECOND, 0);

-

-        if (timeZone != Integer.MIN_VALUE) {

-            int minutes = ((timeZone / 100) * 60) + timeZone % 100;

-            c.add(Calendar.MINUTE, -1 * minutes);

-        }

-

-        return c.getTime();

-    }

-

-    public Date getDate() {

-        return date;

-    }

-

-    public int getYear() {

-        return year;

-    }

-

-    public int getMonth() {

-        return month;

-    }

-

-    public int getDay() {

-        return day;

-    }

-

-    public int getHour() {

-        return hour;

-    }

-

-    public int getMinute() {

-        return minute;

-    }

-

-    public int getSecond() {

-        return second;

-    }

-

-    public int getTimeZone() {

-        return timeZone;

-    }

-

-    public void print() {

-        System.out.println(getYear() + " " + getMonth() + " " + getDay() + "; " + getHour() + " " + getMinute() + " " + getSecond() + " " + getTimeZone());

-    }

-

-

-    public static DateTime parse(String dateString) throws ParseException {

-        try {

-            return new DateTimeParser(new StringReader(dateString)).parseAll();

-        }

-        catch (TokenMgrError err) {

-            throw new ParseException(err.getMessage());

-        }

-    }

-}

+/****************************************************************
+ * 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.field.datetime;
+
+import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
+import org.apache.james.mime4j.field.datetime.parser.ParseException;
+import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;
+
+import java.util.Date;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.GregorianCalendar;
+import java.io.StringReader;
+
+public class DateTime {
+    private final Date date;
+    private final int year;
+    private final int month;
+    private final int day;
+    private final int hour;
+    private final int minute;
+    private final int second;
+    private final int timeZone;
+
+    public DateTime(String yearString, int month, int day, int hour, int minute, int second, int timeZone) {
+        this.year = convertToYear(yearString);
+        this.date = convertToDate(year, month, day, hour, minute, second, timeZone);
+        this.month = month;
+        this.day = day;
+        this.hour = hour;
+        this.minute = minute;
+        this.second = second;
+        this.timeZone = timeZone;
+    }
+
+    private int convertToYear(String yearString) {
+        int year = Integer.parseInt(yearString);
+        switch (yearString.length()) {
+            case 1:
+            case 2:
+                if (year >= 0 && year < 50)
+                    return 2000 + year;
+                else
+                    return 1900 + year;
+            case 3:
+                return 1900 + year;
+            default:
+                return year;
+        }
+    }
+
+    public static Date convertToDate(int year, int month, int day, int hour, int minute, int second, int timeZone) {
+        Calendar c = new GregorianCalendar(TimeZone.getTimeZone("GMT+0"));
+        c.set(year, month - 1, day, hour, minute, second);
+        c.set(Calendar.MILLISECOND, 0);
+
+        if (timeZone != Integer.MIN_VALUE) {
+            int minutes = ((timeZone / 100) * 60) + timeZone % 100;
+            c.add(Calendar.MINUTE, -1 * minutes);
+        }
+
+        return c.getTime();
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public int getYear() {
+        return year;
+    }
+
+    public int getMonth() {
+        return month;
+    }
+
+    public int getDay() {
+        return day;
+    }
+
+    public int getHour() {
+        return hour;
+    }
+
+    public int getMinute() {
+        return minute;
+    }
+
+    public int getSecond() {
+        return second;
+    }
+
+    public int getTimeZone() {
+        return timeZone;
+    }
+
+    public void print() {
+        System.out.println(getYear() + " " + getMonth() + " " + getDay() + "; " + getHour() + " " + getMinute() + " " + getSecond() + " " + getTimeZone());
+    }
+
+
+    public static DateTime parse(String dateString) throws ParseException {
+        try {
+            return new DateTimeParser(new StringReader(dateString)).parseAll();
+        }
+        catch (TokenMgrError err) {
+            throw new ParseException(err.getMessage());
+        }
+    }
+}
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java
index 04cb952..43edebb 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. DateTimeParser.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java
index 17389d8..2c203db 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. DateTimeParserConstants.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java
index e75998c..4b2d2fd 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. DateTimeParserTokenManager.java */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 import org.apache.james.mime4j.field.datetime.DateTime;
@@ -20,12 +20,12 @@
 
 public class DateTimeParserTokenManager implements DateTimeParserConstants
 {
-        // Keeps track of how many levels of comment nesting

-        // we've encountered.  This is only used when the 2nd

-        // level is reached, for example ((this)), not (this).

-        // This is because the outermost level must be treated

-        // specially anyway, because the outermost ")" has a

-        // different token type than inner ")" instances.

+        // Keeps track of how many levels of comment nesting
+        // we've encountered.  This is only used when the 2nd
+        // level is reached, for example ((this)), not (this).
+        // This is because the outermost level must be treated
+        // specially anyway, because the outermost ")" has a
+        // different token type than inner ")" instances.
         static int commentNest;
   public  java.io.PrintStream debugStream = System.out;
   public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java b/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java
index 4186991..13b3ff0 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java b/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java
index 73c9ece..2724529 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/Token.java b/src/org/apache/james/mime4j/field/datetime/parser/Token.java
index 52d101e..0927a09 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/Token.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/Token.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java b/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java
index 9732550..e7043c1 100644
--- a/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java
+++ b/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java
@@ -1,18 +1,18 @@
 /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
-/*

- *  Copyright 2004 the mime4j project

- *

- *  Licensed 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.

+/*
+ *  Copyright 2004 the mime4j project
+ *
+ *  Licensed 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.field.datetime.parser;
 
diff --git a/src/org/apache/james/mime4j/message/AbstractBody.java b/src/org/apache/james/mime4j/message/AbstractBody.java
index d2647ec..190e882 100644
--- a/src/org/apache/james/mime4j/message/AbstractBody.java
+++ b/src/org/apache/james/mime4j/message/AbstractBody.java
@@ -1,47 +1,47 @@
-/****************************************************************

- * 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.message;

-

-

-/**

- * Abstract <code>Body</code> implementation providing the parent

- * functionality required by bodies.

- *

- * 

- * @version $Id: AbstractBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-public abstract class AbstractBody implements Body {

-    private Entity parent = null;

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#getParent()

-     */

-    public Entity getParent() {

-        return parent;

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)

-     */

-    public void setParent(Entity parent) {

-        this.parent = parent;

-    }

-

-}

+/****************************************************************
+ * 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.message;
+
+
+/**
+ * Abstract <code>Body</code> implementation providing the parent
+ * functionality required by bodies.
+ *
+ * 
+ * @version $Id: AbstractBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public abstract class AbstractBody implements Body {
+    private Entity parent = null;
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#getParent()
+     */
+    public Entity getParent() {
+        return parent;
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)
+     */
+    public void setParent(Entity parent) {
+        this.parent = parent;
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/message/BinaryBody.java b/src/org/apache/james/mime4j/message/BinaryBody.java
index bfc992a..fc05a7d 100644
--- a/src/org/apache/james/mime4j/message/BinaryBody.java
+++ b/src/org/apache/james/mime4j/message/BinaryBody.java
@@ -1,42 +1,42 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-

-/**

- * Interface implemented by bodies containing binary data.

- *

- * 

- * @version $Id: BinaryBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public interface BinaryBody extends Body {

-    

-    /**

-     * Gets a <code>InputStream</code> which reads the bytes of the

-     * body.

-     * 

-     * @return the stream.

-     * @throws IOException on I/O errors.

-     */

-    InputStream getInputStream() throws IOException;

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Interface implemented by bodies containing binary data.
+ *
+ * 
+ * @version $Id: BinaryBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public interface BinaryBody extends Body {
+    
+    /**
+     * Gets a <code>InputStream</code> which reads the bytes of the
+     * body.
+     * 
+     * @return the stream.
+     * @throws IOException on I/O errors.
+     */
+    InputStream getInputStream() throws IOException;
+}
diff --git a/src/org/apache/james/mime4j/message/Body.java b/src/org/apache/james/mime4j/message/Body.java
index 54b8948..5d9b381 100644
--- a/src/org/apache/james/mime4j/message/Body.java
+++ b/src/org/apache/james/mime4j/message/Body.java
@@ -1,54 +1,54 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-/**

- * Encapsulates the body of an entity (see RFC 2045).

- *

- * 

- * @version $Id: Body.java,v 1.4 2004/10/04 15:36:43 ntherning Exp $

- */

-public interface Body {

-

-    /**

-     * Gets the parent of this body.

-     * 

-     * @return the parent.

-     */

-    Entity getParent();

-    

-    /**

-     * Sets the parent of this body.

-     * 

-     * @param parent the parent.

-     */

-    void setParent(Entity parent);

-    

-    /**

-     * Writes this body to the given stream in MIME message format.

-     * 

-     * @param out the stream to write to.

-     * @throws IOException on I/O errors.

-     */

-    void writeTo(OutputStream out) throws IOException;

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Encapsulates the body of an entity (see RFC 2045).
+ *
+ * 
+ * @version $Id: Body.java,v 1.4 2004/10/04 15:36:43 ntherning Exp $
+ */
+public interface Body {
+
+    /**
+     * Gets the parent of this body.
+     * 
+     * @return the parent.
+     */
+    Entity getParent();
+    
+    /**
+     * Sets the parent of this body.
+     * 
+     * @param parent the parent.
+     */
+    void setParent(Entity parent);
+    
+    /**
+     * Writes this body to the given stream in MIME message format.
+     * 
+     * @param out the stream to write to.
+     * @throws IOException on I/O errors.
+     */
+    void writeTo(OutputStream out) throws IOException;
+}
diff --git a/src/org/apache/james/mime4j/message/BodyPart.java b/src/org/apache/james/mime4j/message/BodyPart.java
index 474030d..7640183 100644
--- a/src/org/apache/james/mime4j/message/BodyPart.java
+++ b/src/org/apache/james/mime4j/message/BodyPart.java
@@ -1,42 +1,42 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-

-/**

- * Represents a MIME body part  (see RFC 2045).

- *

- * 

- * @version $Id: BodyPart.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public class BodyPart extends Entity {

-

-    /**

-     * 

-     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-        getHeader().writeTo(out);

-        getBody().writeTo(out);

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * Represents a MIME body part  (see RFC 2045).
+ *
+ * 
+ * @version $Id: BodyPart.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class BodyPart extends Entity {
+
+    /**
+     * 
+     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+        getHeader().writeTo(out);
+        getBody().writeTo(out);
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/Entity.java b/src/org/apache/james/mime4j/message/Entity.java
index 96f2a48..06039f3 100644
--- a/src/org/apache/james/mime4j/message/Entity.java
+++ b/src/org/apache/james/mime4j/message/Entity.java
@@ -1,170 +1,170 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.OutputStream;

-

-import org.apache.james.mime4j.field.ContentTransferEncodingField;

-import org.apache.james.mime4j.field.ContentTypeField;

-import org.apache.james.mime4j.field.Field;

-

-/**

- * MIME entity. An entity has a header and a body (see RFC 2045).

- *

- * 

- * @version $Id: Entity.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public abstract class Entity {

-    private Header header = null;

-    private Body body = null;

-    private Entity parent = null;

-

-    /**

-     * Gets the parent entity of this entity.

-     * Returns <code>null</code> if this is the root entity.

-     * 

-     * @return the parent or <code>null</code>.

-     */

-    public Entity getParent() {

-        return parent;

-    }

-    

-    /**

-     * Sets the parent entity of this entity.

-     * 

-     * @param parent the parent entity or <code>null</code> if

-     *        this will be the root entity.

-     */

-    public void setParent(Entity parent) {

-        this.parent = parent;

-    }

-    

-    /**

-     * Gets the entity header.

-     * 

-     * @return the header.

-     */

-    public Header getHeader() {

-        return header;

-    }

-    

-    /**

-     * Sets the entity header.

-     * 

-     * @param header the header.

-     */

-    public void setHeader(Header header) {

-        this.header = header;

-    }

-    

-    /**

-     * Gets the body of this entity.

-     * 

-     * @return the body,

-     */

-    public Body getBody() {

-        return body;

-    }

-

-    /**

-     * Sets the body of this entity.

-     * 

-     * @param body the body.

-     */

-    public void setBody(Body body) {

-        this.body = body;

-        body.setParent(this);

-    }

-    

-    /**

-     * Determines the MIME type of this <code>Entity</code>. The MIME type

-     * is derived by looking at the parent's Content-Type field if no

-     * Content-Type field is set for this <code>Entity</code>.

-     * 

-     * @return the MIME type.

-     */

-    public String getMimeType() {

-        ContentTypeField child = 

-            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE);

-        ContentTypeField parent = getParent() != null 

-            ? (ContentTypeField) getParent().getHeader().

-                                                getField(Field.CONTENT_TYPE)

-            : null;

-        

-        return ContentTypeField.getMimeType(child, parent);

-    }

-    

-    /**

-     * Determines the MIME character set encoding of this <code>Entity</code>.

-     * 

-     * @return the MIME character set encoding.

-     */

-    public String getCharset() {

-        return ContentTypeField.getCharset( 

-            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE));

-    }

-    

-    /**

-     * Determines the transfer encoding of this <code>Entity</code>.

-     * 

-     * @return the transfer encoding.

-     */

-    public String getContentTransferEncoding() {

-        ContentTransferEncodingField f = (ContentTransferEncodingField) 

-                        getHeader().getField(Field.CONTENT_TRANSFER_ENCODING);

-        

-        return ContentTransferEncodingField.getEncoding(f);

-    }

-    

-    /**

-     * Determines if the MIME type of this <code>Entity</code> matches the

-     * given one. MIME types are case-insensitive.

-     * 

-     * @param type the MIME type to match against.

-     * @return <code>true</code> on match, <code>false</code> otherwise.

-     */

-    public boolean isMimeType(String type) {

-        return getMimeType().equalsIgnoreCase(type);

-    }

-    

-    /**

-     * Determines if the MIME type of this <code>Entity</code> is

-     * <code>multipart/*</code>. Since multipart-entities must have

-     * a boundary parameter in the <code>Content-Type</code> field this

-     * method returns <code>false</code> if no boundary exists.

-     * 

-     * @return <code>true</code> on match, <code>false</code> otherwise.

-     */

-    public boolean isMultipart() {

-        ContentTypeField f = 

-            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE);

-        return f != null && f.getBoundary() != null 

-            && getMimeType().startsWith(ContentTypeField.TYPE_MULTIPART_PREFIX);

-    }

-    

-    /**

-     * Write the content to the given outputstream

-     * 

-     * @param out the outputstream to write to

-     * @throws IOException 

-     */

-    public abstract void writeTo(OutputStream out) throws IOException;

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.james.mime4j.field.ContentTransferEncodingField;
+import org.apache.james.mime4j.field.ContentTypeField;
+import org.apache.james.mime4j.field.Field;
+
+/**
+ * MIME entity. An entity has a header and a body (see RFC 2045).
+ *
+ * 
+ * @version $Id: Entity.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public abstract class Entity {
+    private Header header = null;
+    private Body body = null;
+    private Entity parent = null;
+
+    /**
+     * Gets the parent entity of this entity.
+     * Returns <code>null</code> if this is the root entity.
+     * 
+     * @return the parent or <code>null</code>.
+     */
+    public Entity getParent() {
+        return parent;
+    }
+    
+    /**
+     * Sets the parent entity of this entity.
+     * 
+     * @param parent the parent entity or <code>null</code> if
+     *        this will be the root entity.
+     */
+    public void setParent(Entity parent) {
+        this.parent = parent;
+    }
+    
+    /**
+     * Gets the entity header.
+     * 
+     * @return the header.
+     */
+    public Header getHeader() {
+        return header;
+    }
+    
+    /**
+     * Sets the entity header.
+     * 
+     * @param header the header.
+     */
+    public void setHeader(Header header) {
+        this.header = header;
+    }
+    
+    /**
+     * Gets the body of this entity.
+     * 
+     * @return the body,
+     */
+    public Body getBody() {
+        return body;
+    }
+
+    /**
+     * Sets the body of this entity.
+     * 
+     * @param body the body.
+     */
+    public void setBody(Body body) {
+        this.body = body;
+        body.setParent(this);
+    }
+    
+    /**
+     * Determines the MIME type of this <code>Entity</code>. The MIME type
+     * is derived by looking at the parent's Content-Type field if no
+     * Content-Type field is set for this <code>Entity</code>.
+     * 
+     * @return the MIME type.
+     */
+    public String getMimeType() {
+        ContentTypeField child = 
+            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE);
+        ContentTypeField parent = getParent() != null 
+            ? (ContentTypeField) getParent().getHeader().
+                                                getField(Field.CONTENT_TYPE)
+            : null;
+        
+        return ContentTypeField.getMimeType(child, parent);
+    }
+    
+    /**
+     * Determines the MIME character set encoding of this <code>Entity</code>.
+     * 
+     * @return the MIME character set encoding.
+     */
+    public String getCharset() {
+        return ContentTypeField.getCharset( 
+            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE));
+    }
+    
+    /**
+     * Determines the transfer encoding of this <code>Entity</code>.
+     * 
+     * @return the transfer encoding.
+     */
+    public String getContentTransferEncoding() {
+        ContentTransferEncodingField f = (ContentTransferEncodingField) 
+                        getHeader().getField(Field.CONTENT_TRANSFER_ENCODING);
+        
+        return ContentTransferEncodingField.getEncoding(f);
+    }
+    
+    /**
+     * Determines if the MIME type of this <code>Entity</code> matches the
+     * given one. MIME types are case-insensitive.
+     * 
+     * @param type the MIME type to match against.
+     * @return <code>true</code> on match, <code>false</code> otherwise.
+     */
+    public boolean isMimeType(String type) {
+        return getMimeType().equalsIgnoreCase(type);
+    }
+    
+    /**
+     * Determines if the MIME type of this <code>Entity</code> is
+     * <code>multipart/*</code>. Since multipart-entities must have
+     * a boundary parameter in the <code>Content-Type</code> field this
+     * method returns <code>false</code> if no boundary exists.
+     * 
+     * @return <code>true</code> on match, <code>false</code> otherwise.
+     */
+    public boolean isMultipart() {
+        ContentTypeField f = 
+            (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE);
+        return f != null && f.getBoundary() != null 
+            && getMimeType().startsWith(ContentTypeField.TYPE_MULTIPART_PREFIX);
+    }
+    
+    /**
+     * Write the content to the given outputstream
+     * 
+     * @param out the outputstream to write to
+     * @throws IOException 
+     */
+    public abstract void writeTo(OutputStream out) throws IOException;
+}
diff --git a/src/org/apache/james/mime4j/message/Header.java b/src/org/apache/james/mime4j/message/Header.java
index e9216a8..4607ab6 100644
--- a/src/org/apache/james/mime4j/message/Header.java
+++ b/src/org/apache/james/mime4j/message/Header.java
@@ -1,158 +1,158 @@
-/****************************************************************

- * 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.message;

-

-import java.io.BufferedWriter;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.Iterator;

-import java.util.LinkedList;

-import java.util.List;

-

-import org.apache.james.mime4j.AbstractContentHandler;

-import org.apache.james.mime4j.MimeStreamParser;

-import org.apache.james.mime4j.field.ContentTypeField;

-import org.apache.james.mime4j.field.Field;

-import org.apache.james.mime4j.util.CharsetUtil;

-

-

-/**

- * The header of an entity (see RFC 2045).

- *

- *

- * @version $Id: Header.java,v 1.3 2004/10/04 15:36:44 ntherning Exp $

- */

-public class Header {

-    private List<Field> fields = new LinkedList<Field>();

-    private HashMap<String, List<Field>> fieldMap = new HashMap<String, List<Field>>();

-

-    /**

-     * Creates a new empty <code>Header</code>.

-     */

-    public Header() {

-    }

-

-    /**

-     * Creates a new <code>Header</code> from the specified stream.

-     *

-     * @param is the stream to read the header from.

-     */

-    public Header(InputStream is) throws IOException {

-        final MimeStreamParser parser = new MimeStreamParser();

-        parser.setContentHandler(new AbstractContentHandler() {

-            @Override

-            public void endHeader() {

-                parser.stop();

-            }

-            @Override

-            public void field(String fieldData) {

-                addField(Field.parse(fieldData));

-            }

-        });

-        parser.parse(is);

-    }

-

-    /**

-     * Adds a field to the end of the list of fields.

-     *

-     * @param field the field to add.

-     */

-    public void addField(Field field) {

-        List<Field> values = fieldMap.get(field.getName().toLowerCase());

-        if (values == null) {

-            values = new LinkedList<Field>();

-            fieldMap.put(field.getName().toLowerCase(), values);

-        }

-        values.add(field);

-        fields.add(field);

-    }

-

-    /**

-     * Gets the fields of this header. The returned list will not be

-     * modifiable.

-     *

-     * @return the list of <code>Field</code> objects.

-     */

-    public List<Field> getFields() {

-        return Collections.unmodifiableList(fields);

-    }

-

-    /**

-     * Gets a <code>Field</code> given a field name. If there are multiple

-     * such fields defined in this header the first one will be returned.

-     *

-     * @param name the field name (e.g. From, Subject).

-     * @return the field or <code>null</code> if none found.

-     */

-    public Field getField(String name) {

-        List<Field> l = fieldMap.get(name.toLowerCase());

-        if (l != null && !l.isEmpty()) {

-            return l.get(0);

-        }

-        return null;

-    }

-

-    /**

-     * Gets all <code>Field</code>s having the specified field name.

-     *

-     * @param name the field name (e.g. From, Subject).

-     * @return the list of fields.

-     */

-    public List<Field> getFields(String name) {

-        List<Field> l = fieldMap.get(name.toLowerCase());

-        return Collections.unmodifiableList(l);

-    }

-

-    /**

-     * Return Header Object as String representation. Each headerline is

-     * seperated by "\r\n"

-     *

-     * @return headers

-     */

-    @Override

-    public String toString() {

-        StringBuffer str = new StringBuffer();

-        for (Iterator<Field> it = fields.iterator(); it.hasNext();) {

-            str.append(it.next().toString());

-            str.append("\r\n");

-        }

-        return str.toString();

-    }

-

-

-    /**

-     * Write the Header to the given OutputStream

-     *

-     * @param out the OutputStream to write to

-     * @throws IOException

-     */

-    public void writeTo(OutputStream out) throws IOException {

-        String charString = ((ContentTypeField) getField(Field.CONTENT_TYPE)).getCharset();

-

-        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(charString)),8192);

-        writer.write(toString()+ "\r\n");

-        writer.flush();

-    }

-

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.james.mime4j.AbstractContentHandler;
+import org.apache.james.mime4j.MimeStreamParser;
+import org.apache.james.mime4j.field.ContentTypeField;
+import org.apache.james.mime4j.field.Field;
+import org.apache.james.mime4j.util.CharsetUtil;
+
+
+/**
+ * The header of an entity (see RFC 2045).
+ *
+ *
+ * @version $Id: Header.java,v 1.3 2004/10/04 15:36:44 ntherning Exp $
+ */
+public class Header {
+    private List<Field> fields = new LinkedList<Field>();
+    private HashMap<String, List<Field>> fieldMap = new HashMap<String, List<Field>>();
+
+    /**
+     * Creates a new empty <code>Header</code>.
+     */
+    public Header() {
+    }
+
+    /**
+     * Creates a new <code>Header</code> from the specified stream.
+     *
+     * @param is the stream to read the header from.
+     */
+    public Header(InputStream is) throws IOException {
+        final MimeStreamParser parser = new MimeStreamParser();
+        parser.setContentHandler(new AbstractContentHandler() {
+            @Override
+            public void endHeader() {
+                parser.stop();
+            }
+            @Override
+            public void field(String fieldData) {
+                addField(Field.parse(fieldData));
+            }
+        });
+        parser.parse(is);
+    }
+
+    /**
+     * Adds a field to the end of the list of fields.
+     *
+     * @param field the field to add.
+     */
+    public void addField(Field field) {
+        List<Field> values = fieldMap.get(field.getName().toLowerCase());
+        if (values == null) {
+            values = new LinkedList<Field>();
+            fieldMap.put(field.getName().toLowerCase(), values);
+        }
+        values.add(field);
+        fields.add(field);
+    }
+
+    /**
+     * Gets the fields of this header. The returned list will not be
+     * modifiable.
+     *
+     * @return the list of <code>Field</code> objects.
+     */
+    public List<Field> getFields() {
+        return Collections.unmodifiableList(fields);
+    }
+
+    /**
+     * Gets a <code>Field</code> given a field name. If there are multiple
+     * such fields defined in this header the first one will be returned.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @return the field or <code>null</code> if none found.
+     */
+    public Field getField(String name) {
+        List<Field> l = fieldMap.get(name.toLowerCase());
+        if (l != null && !l.isEmpty()) {
+            return l.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * Gets all <code>Field</code>s having the specified field name.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @return the list of fields.
+     */
+    public List<Field> getFields(String name) {
+        List<Field> l = fieldMap.get(name.toLowerCase());
+        return Collections.unmodifiableList(l);
+    }
+
+    /**
+     * Return Header Object as String representation. Each headerline is
+     * seperated by "\r\n"
+     *
+     * @return headers
+     */
+    @Override
+    public String toString() {
+        StringBuffer str = new StringBuffer();
+        for (Iterator<Field> it = fields.iterator(); it.hasNext();) {
+            str.append(it.next().toString());
+            str.append("\r\n");
+        }
+        return str.toString();
+    }
+
+
+    /**
+     * Write the Header to the given OutputStream
+     *
+     * @param out the OutputStream to write to
+     * @throws IOException
+     */
+    public void writeTo(OutputStream out) throws IOException {
+        String charString = ((ContentTypeField) getField(Field.CONTENT_TYPE)).getCharset();
+
+        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(charString)),8192);
+        writer.write(toString()+ "\r\n");
+        writer.flush();
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/message/MemoryBinaryBody.java b/src/org/apache/james/mime4j/message/MemoryBinaryBody.java
index a44db4d..28bccc1 100644
--- a/src/org/apache/james/mime4j/message/MemoryBinaryBody.java
+++ b/src/org/apache/james/mime4j/message/MemoryBinaryBody.java
@@ -1,92 +1,92 @@
-/****************************************************************

- * 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.message;

-

-import java.io.ByteArrayInputStream;

-import java.io.ByteArrayOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-import org.apache.commons.io.IOUtils;

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.util.TempPath;

-import org.apache.james.mime4j.util.TempStorage;

-

-

-/**

- * Binary body backed by a {@link org.apache.james.mime4j.util.TempFile}.

- *

- * 

- * @version $Id: TempFileBinaryBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-class MemoryBinaryBody extends AbstractBody implements BinaryBody {

-    private static Log log = LogFactory.getLog(MemoryBinaryBody.class);

-    

-    private Entity parent = null;

-    private byte[] tempFile = null;

-

-    /**

-     * Use the given InputStream to build the TemporyFileBinaryBody

-     * 

-     * @param is the InputStream to use as source

-     * @throws IOException

-     */

-    public MemoryBinaryBody(InputStream is) throws IOException {

-        

-        TempPath tempPath = TempStorage.getInstance().getRootTempPath();

-        

-        ByteArrayOutputStream out = new ByteArrayOutputStream();

-        IOUtils.copy(is, out);

-        out.close();

-        tempFile = out.toByteArray();

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.AbstractBody#getParent()

-     */

-    public Entity getParent() {

-        return parent;

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.AbstractBody#setParent(org.apache.james.mime4j.message.Entity)

-     */

-    public void setParent(Entity parent) {

-        this.parent = parent;

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.BinaryBody#getInputStream()

-     */

-    public InputStream getInputStream() throws IOException {

-        return new ByteArrayInputStream(tempFile);

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-	IOUtils.copy(getInputStream(),out);

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.TempPath;
+import org.apache.james.mime4j.util.TempStorage;
+
+
+/**
+ * Binary body backed by a {@link org.apache.james.mime4j.util.TempFile}.
+ *
+ * 
+ * @version $Id: TempFileBinaryBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+class MemoryBinaryBody extends AbstractBody implements BinaryBody {
+    private static Log log = LogFactory.getLog(MemoryBinaryBody.class);
+    
+    private Entity parent = null;
+    private byte[] tempFile = null;
+
+    /**
+     * Use the given InputStream to build the TemporyFileBinaryBody
+     * 
+     * @param is the InputStream to use as source
+     * @throws IOException
+     */
+    public MemoryBinaryBody(InputStream is) throws IOException {
+        
+        TempPath tempPath = TempStorage.getInstance().getRootTempPath();
+        
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        IOUtils.copy(is, out);
+        out.close();
+        tempFile = out.toByteArray();
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.AbstractBody#getParent()
+     */
+    public Entity getParent() {
+        return parent;
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.AbstractBody#setParent(org.apache.james.mime4j.message.Entity)
+     */
+    public void setParent(Entity parent) {
+        this.parent = parent;
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.BinaryBody#getInputStream()
+     */
+    public InputStream getInputStream() throws IOException {
+        return new ByteArrayInputStream(tempFile);
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+	IOUtils.copy(getInputStream(),out);
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/MemoryTextBody.java b/src/org/apache/james/mime4j/message/MemoryTextBody.java
index 71af663..6f3ef9176 100644
--- a/src/org/apache/james/mime4j/message/MemoryTextBody.java
+++ b/src/org/apache/james/mime4j/message/MemoryTextBody.java
@@ -1,118 +1,118 @@
-/****************************************************************

- * 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.message;

-

-import java.io.ByteArrayInputStream;

-import java.io.ByteArrayOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.InputStreamReader;

-import java.io.OutputStream;

-import java.io.Reader;

-import java.io.UnsupportedEncodingException;

-

-import org.apache.commons.io.IOUtils;

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.util.CharsetUtil;

-import org.apache.james.mime4j.util.TempPath;

-import org.apache.james.mime4j.util.TempStorage;

-

-

-/**

- * Text body backed by a {@link org.apache.james.mime4j.util.TempFile}.

- *

- * 

- * @version $Id: TempFileTextBody.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $

- */

-class MemoryTextBody extends AbstractBody implements TextBody {

-    private static Log log = LogFactory.getLog(MemoryTextBody.class);

-    

-    private String mimeCharset = null;

-    private byte[] tempFile = null;

-

-    public MemoryTextBody(InputStream is) throws IOException {

-        this(is, null);

-    }

-    

-    public MemoryTextBody(InputStream is, String mimeCharset) 

-            throws IOException {

-        

-        this.mimeCharset = mimeCharset;

-        

-        TempPath tempPath = TempStorage.getInstance().getRootTempPath();

-        

-        ByteArrayOutputStream out = new ByteArrayOutputStream();

-        IOUtils.copy(is, out);

-        out.close();

-        tempFile = out.toByteArray();

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.TextBody#getReader()

-     */

-    public Reader getReader() throws UnsupportedEncodingException, IOException {

-        String javaCharset = null;

-        if (mimeCharset != null) {

-            javaCharset = CharsetUtil.toJavaCharset(mimeCharset);

-        }

-        

-        if (javaCharset == null) {

-            javaCharset = "ISO-8859-1";

-            

-            if (log.isWarnEnabled()) {

-                if (mimeCharset == null) {

-                    log.warn("No MIME charset specified. Using " + javaCharset

-                            + " instead.");

-                } else {

-                    log.warn("MIME charset '" + mimeCharset + "' has no "

-                            + "corresponding Java charset. Using " + javaCharset

-                            + " instead.");

-                }

-            }

-        }

-        /*

-            if (log.isWarnEnabled()) {

-                if (mimeCharset == null) {

-                    log.warn("No MIME charset specified. Using the "

-                           + "platform's default charset.");

-                } else {

-                    log.warn("MIME charset '" + mimeCharset + "' has no "

-                            + "corresponding Java charset. Using the "

-                            + "platform's default charset.");

-                }

-            }

-            

-            return new InputStreamReader(tempFile.getInputStream());

-        }*/

-        

-        return new InputStreamReader(new ByteArrayInputStream(tempFile), javaCharset);

-    }

-    

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-	IOUtils.copy(new ByteArrayInputStream(tempFile), out);	

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.io.IOUtils;
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.TempPath;
+import org.apache.james.mime4j.util.TempStorage;
+
+
+/**
+ * Text body backed by a {@link org.apache.james.mime4j.util.TempFile}.
+ *
+ * 
+ * @version $Id: TempFileTextBody.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $
+ */
+class MemoryTextBody extends AbstractBody implements TextBody {
+    private static Log log = LogFactory.getLog(MemoryTextBody.class);
+    
+    private String mimeCharset = null;
+    private byte[] tempFile = null;
+
+    public MemoryTextBody(InputStream is) throws IOException {
+        this(is, null);
+    }
+    
+    public MemoryTextBody(InputStream is, String mimeCharset) 
+            throws IOException {
+        
+        this.mimeCharset = mimeCharset;
+        
+        TempPath tempPath = TempStorage.getInstance().getRootTempPath();
+        
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        IOUtils.copy(is, out);
+        out.close();
+        tempFile = out.toByteArray();
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.TextBody#getReader()
+     */
+    public Reader getReader() throws UnsupportedEncodingException, IOException {
+        String javaCharset = null;
+        if (mimeCharset != null) {
+            javaCharset = CharsetUtil.toJavaCharset(mimeCharset);
+        }
+        
+        if (javaCharset == null) {
+            javaCharset = "ISO-8859-1";
+            
+            if (log.isWarnEnabled()) {
+                if (mimeCharset == null) {
+                    log.warn("No MIME charset specified. Using " + javaCharset
+                            + " instead.");
+                } else {
+                    log.warn("MIME charset '" + mimeCharset + "' has no "
+                            + "corresponding Java charset. Using " + javaCharset
+                            + " instead.");
+                }
+            }
+        }
+        /*
+            if (log.isWarnEnabled()) {
+                if (mimeCharset == null) {
+                    log.warn("No MIME charset specified. Using the "
+                           + "platform's default charset.");
+                } else {
+                    log.warn("MIME charset '" + mimeCharset + "' has no "
+                            + "corresponding Java charset. Using the "
+                            + "platform's default charset.");
+                }
+            }
+            
+            return new InputStreamReader(tempFile.getInputStream());
+        }*/
+        
+        return new InputStreamReader(new ByteArrayInputStream(tempFile), javaCharset);
+    }
+    
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+	IOUtils.copy(new ByteArrayInputStream(tempFile), out);	
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/Message.java b/src/org/apache/james/mime4j/message/Message.java
index 5143071..dad9afd 100644
--- a/src/org/apache/james/mime4j/message/Message.java
+++ b/src/org/apache/james/mime4j/message/Message.java
@@ -1,257 +1,257 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.util.Stack;

-

-import org.apache.james.mime4j.BodyDescriptor;

-import org.apache.james.mime4j.ContentHandler;

-import org.apache.james.mime4j.MimeStreamParser;

-import org.apache.james.mime4j.decoder.Base64InputStream;

-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;

-import org.apache.james.mime4j.field.Field;

-import org.apache.james.mime4j.field.UnstructuredField;

-

-

-/**

- * Represents a MIME message. The following code parses a stream into a

- * <code>Message</code> object.

- *

- * <pre>

- *      Message msg = new Message(new BufferedInputStream(

- *                                      new FileInputStream("mime.msg")));

- * </pre>

- *

- *

- *

- * @version $Id: Message.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public class Message extends Entity implements Body {

-

-    /**

-     * Creates a new empty <code>Message</code>.

-     */

-    public Message() {

-    }

-

-    /**

-     * Parses the specified MIME message stream into a <code>Message</code>

-     * instance.

-     *

-     * @param is the stream to parse.

-     * @throws IOException on I/O errors.

-     */

-    public Message(InputStream is) throws IOException {

-        MimeStreamParser parser = new MimeStreamParser();

-        parser.setContentHandler(new MessageBuilder());

-        parser.parse(is);

-    }

-

-

-    /**

-     * Gets the <code>Subject</code> field.

-     *

-     * @return the <code>Subject</code> field or <code>null</code> if it

-     *         doesn't exist.

-     */

-    public UnstructuredField getSubject() {

-        return (UnstructuredField) getHeader().getField(Field.SUBJECT);

-    }

-

-    /**

-     *

-     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)

-     */

-    @Override

-    public void writeTo(OutputStream out) throws IOException {

-        getHeader().writeTo(out);

-

-        Body body = getBody();

-        if (body instanceof Multipart) {

-            Multipart mp = (Multipart) body;

-            mp.writeTo(out);

-        } else {

-            body.writeTo(out);

-        }

-    }

-

-

-    private class MessageBuilder implements ContentHandler {

-        private Stack<Object> stack = new Stack<Object>();

-

-        public MessageBuilder() {

-        }

-

-        private void expect(Class c) {

-            if (!c.isInstance(stack.peek())) {

-                throw new IllegalStateException("Internal stack error: "

-                        + "Expected '" + c.getName() + "' found '"

-                        + stack.peek().getClass().getName() + "'");

-            }

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#startMessage()

-         */

-        public void startMessage() {

-            if (stack.isEmpty()) {

-                stack.push(Message.this);

-            } else {

-                expect(Entity.class);

-                Message m = new Message();

-                ((Entity) stack.peek()).setBody(m);

-                stack.push(m);

-            }

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#endMessage()

-         */

-        public void endMessage() {

-            expect(Message.class);

-            stack.pop();

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#startHeader()

-         */

-        public void startHeader() {

-            stack.push(new Header());

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)

-         */

-        public void field(String fieldData) {

-            expect(Header.class);

-            ((Header) stack.peek()).addField(Field.parse(fieldData));

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#endHeader()

-         */

-        public void endHeader() {

-            expect(Header.class);

-            Header h = (Header) stack.pop();

-            expect(Entity.class);

-            ((Entity) stack.peek()).setHeader(h);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)

-         */

-        public void startMultipart(BodyDescriptor bd) {

-            expect(Entity.class);

-

-            Entity e = (Entity) stack.peek();

-            Multipart multiPart = new Multipart();

-            e.setBody(multiPart);

-            stack.push(multiPart);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)

-         */

-        public void body(BodyDescriptor bd, InputStream is) throws IOException {

-            expect(Entity.class);

-

-            String enc = bd.getTransferEncoding();

-            if ("base64".equals(enc)) {

-                is = new Base64InputStream(is);

-            } else if ("quoted-printable".equals(enc)) {

-                is = new QuotedPrintableInputStream(is);

-            }

-

-            Body body = null;

-            if (bd.getMimeType().startsWith("text/")) {

-                body = new MemoryTextBody(is, bd.getCharset());

-            } else {

-                body = new MemoryBinaryBody(is);

-            }

-

-            ((Entity) stack.peek()).setBody(body);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#endMultipart()

-         */

-        public void endMultipart() {

-            stack.pop();

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#startBodyPart()

-         */

-        public void startBodyPart() {

-            expect(Multipart.class);

-

-            BodyPart bodyPart = new BodyPart();

-            ((Multipart) stack.peek()).addBodyPart(bodyPart);

-            stack.push(bodyPart);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#endBodyPart()

-         */

-        public void endBodyPart() {

-            expect(BodyPart.class);

-            stack.pop();

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)

-         */

-        public void epilogue(InputStream is) throws IOException {

-            expect(Multipart.class);

-            StringBuffer sb = new StringBuffer();

-            int b;

-            while ((b = is.read()) != -1) {

-                sb.append((char) b);

-            }

-            ((Multipart) stack.peek()).setEpilogue(sb.toString());

-        }

-

-        /**

-         * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)

-         */

-        public void preamble(InputStream is) throws IOException {

-            expect(Multipart.class);

-            StringBuffer sb = new StringBuffer();

-            int b;

-            while ((b = is.read()) != -1) {

-                sb.append((char) b);

-            }

-            ((Multipart) stack.peek()).setPreamble(sb.toString());

-        }

-

-        /**

-         * TODO: Implement me

-         *

-         * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)

-         */

-        public void raw(InputStream is) throws IOException {

-            throw new UnsupportedOperationException("Not supported");

-        }

-

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Stack;
+
+import org.apache.james.mime4j.BodyDescriptor;
+import org.apache.james.mime4j.ContentHandler;
+import org.apache.james.mime4j.MimeStreamParser;
+import org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+import org.apache.james.mime4j.field.Field;
+import org.apache.james.mime4j.field.UnstructuredField;
+
+
+/**
+ * Represents a MIME message. The following code parses a stream into a
+ * <code>Message</code> object.
+ *
+ * <pre>
+ *      Message msg = new Message(new BufferedInputStream(
+ *                                      new FileInputStream("mime.msg")));
+ * </pre>
+ *
+ *
+ *
+ * @version $Id: Message.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class Message extends Entity implements Body {
+
+    /**
+     * Creates a new empty <code>Message</code>.
+     */
+    public Message() {
+    }
+
+    /**
+     * Parses the specified MIME message stream into a <code>Message</code>
+     * instance.
+     *
+     * @param is the stream to parse.
+     * @throws IOException on I/O errors.
+     */
+    public Message(InputStream is) throws IOException {
+        MimeStreamParser parser = new MimeStreamParser();
+        parser.setContentHandler(new MessageBuilder());
+        parser.parse(is);
+    }
+
+
+    /**
+     * Gets the <code>Subject</code> field.
+     *
+     * @return the <code>Subject</code> field or <code>null</code> if it
+     *         doesn't exist.
+     */
+    public UnstructuredField getSubject() {
+        return (UnstructuredField) getHeader().getField(Field.SUBJECT);
+    }
+
+    /**
+     *
+     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)
+     */
+    @Override
+    public void writeTo(OutputStream out) throws IOException {
+        getHeader().writeTo(out);
+
+        Body body = getBody();
+        if (body instanceof Multipart) {
+            Multipart mp = (Multipart) body;
+            mp.writeTo(out);
+        } else {
+            body.writeTo(out);
+        }
+    }
+
+
+    private class MessageBuilder implements ContentHandler {
+        private Stack<Object> stack = new Stack<Object>();
+
+        public MessageBuilder() {
+        }
+
+        private void expect(Class c) {
+            if (!c.isInstance(stack.peek())) {
+                throw new IllegalStateException("Internal stack error: "
+                        + "Expected '" + c.getName() + "' found '"
+                        + stack.peek().getClass().getName() + "'");
+            }
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#startMessage()
+         */
+        public void startMessage() {
+            if (stack.isEmpty()) {
+                stack.push(Message.this);
+            } else {
+                expect(Entity.class);
+                Message m = new Message();
+                ((Entity) stack.peek()).setBody(m);
+                stack.push(m);
+            }
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#endMessage()
+         */
+        public void endMessage() {
+            expect(Message.class);
+            stack.pop();
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#startHeader()
+         */
+        public void startHeader() {
+            stack.push(new Header());
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
+         */
+        public void field(String fieldData) {
+            expect(Header.class);
+            ((Header) stack.peek()).addField(Field.parse(fieldData));
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#endHeader()
+         */
+        public void endHeader() {
+            expect(Header.class);
+            Header h = (Header) stack.pop();
+            expect(Entity.class);
+            ((Entity) stack.peek()).setHeader(h);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
+         */
+        public void startMultipart(BodyDescriptor bd) {
+            expect(Entity.class);
+
+            Entity e = (Entity) stack.peek();
+            Multipart multiPart = new Multipart();
+            e.setBody(multiPart);
+            stack.push(multiPart);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
+         */
+        public void body(BodyDescriptor bd, InputStream is) throws IOException {
+            expect(Entity.class);
+
+            String enc = bd.getTransferEncoding();
+            if ("base64".equals(enc)) {
+                is = new Base64InputStream(is);
+            } else if ("quoted-printable".equals(enc)) {
+                is = new QuotedPrintableInputStream(is);
+            }
+
+            Body body = null;
+            if (bd.getMimeType().startsWith("text/")) {
+                body = new MemoryTextBody(is, bd.getCharset());
+            } else {
+                body = new MemoryBinaryBody(is);
+            }
+
+            ((Entity) stack.peek()).setBody(body);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#endMultipart()
+         */
+        public void endMultipart() {
+            stack.pop();
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#startBodyPart()
+         */
+        public void startBodyPart() {
+            expect(Multipart.class);
+
+            BodyPart bodyPart = new BodyPart();
+            ((Multipart) stack.peek()).addBodyPart(bodyPart);
+            stack.push(bodyPart);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#endBodyPart()
+         */
+        public void endBodyPart() {
+            expect(BodyPart.class);
+            stack.pop();
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
+         */
+        public void epilogue(InputStream is) throws IOException {
+            expect(Multipart.class);
+            StringBuffer sb = new StringBuffer();
+            int b;
+            while ((b = is.read()) != -1) {
+                sb.append((char) b);
+            }
+            ((Multipart) stack.peek()).setEpilogue(sb.toString());
+        }
+
+        /**
+         * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
+         */
+        public void preamble(InputStream is) throws IOException {
+            expect(Multipart.class);
+            StringBuffer sb = new StringBuffer();
+            int b;
+            while ((b = is.read()) != -1) {
+                sb.append((char) b);
+            }
+            ((Multipart) stack.peek()).setPreamble(sb.toString());
+        }
+
+        /**
+         * TODO: Implement me
+         *
+         * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
+         */
+        public void raw(InputStream is) throws IOException {
+            throw new UnsupportedOperationException("Not supported");
+        }
+
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/Multipart.java b/src/org/apache/james/mime4j/message/Multipart.java
index d834000..2951e5a 100644
--- a/src/org/apache/james/mime4j/message/Multipart.java
+++ b/src/org/apache/james/mime4j/message/Multipart.java
@@ -1,203 +1,203 @@
-/****************************************************************

- * 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.message;

-

-import java.io.BufferedWriter;

-import java.io.IOException;

-import java.io.OutputStream;

-import java.io.OutputStreamWriter;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.LinkedList;

-import java.util.List;

-

-import org.apache.james.mime4j.field.ContentTypeField;

-import org.apache.james.mime4j.field.Field;

-import org.apache.james.mime4j.util.CharsetUtil;

-

-/**

- * Represents a MIME multipart body (see RFC 2045).A multipart body has a

- * ordered list of body parts. The multipart body also has a preamble and

- * epilogue. The preamble consists of whatever characters appear before the

- * first body part while the epilogue consists of whatever characters come

- * after the last body part.

- *

- *

- * @version $Id: Multipart.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public class Multipart implements Body {

-    private String preamble = "";

-    private String epilogue = "";

-    private List<BodyPart> bodyParts = new LinkedList<BodyPart>();

-    private Entity parent = null;

-    private String subType = "alternative";

-

-    /**

-     * Creates a new empty <code>Multipart</code> instance.

-     */

-    public Multipart() {

-    }

-

-    /**

-     * Gets the multipart sub-type. E.g. <code>alternative</code> (the default)

-     * or <code>parallel</code>. See RFC 2045 for common sub-types and their

-     * meaning.

-     *

-     * @return the multipart sub-type.

-     */

-    public String getSubType() {

-        return subType;

-    }

-

-    /**

-     * Sets the multipart sub-type. E.g. <code>alternative</code>

-     * or <code>parallel</code>. See RFC 2045 for common sub-types and their

-     * meaning.

-     *

-     * @param subType the sub-type.

-     */

-    public void setSubType(String subType) {

-        this.subType = subType;

-    }

-

-    /**

-     * @see org.apache.james.mime4j.message.Body#getParent()

-     */

-    public Entity getParent() {

-        return parent;

-    }

-

-    /**

-     * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)

-     */

-    public void setParent(Entity parent) {

-        this.parent = parent;

-        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {

-            it.next().setParent(parent);

-        }

-    }

-

-    /**

-     * Gets the epilogue.

-     *

-     * @return the epilogue.

-     */

-    public String getEpilogue() {

-        return epilogue;

-    }

-

-    /**

-     * Sets the epilogue.

-     *

-     * @param epilogue the epilogue.

-     */

-    public void setEpilogue(String epilogue) {

-        this.epilogue = epilogue;

-    }

-

-    /**

-     * Gets the list of body parts. The list is immutable.

-     *

-     * @return the list of <code>BodyPart</code> objects.

-     */

-    public List<BodyPart> getBodyParts() {

-        return Collections.unmodifiableList(bodyParts);

-    }

-

-    /**

-     * Sets the list of body parts.

-     *

-     * @param bodyParts the new list of <code>BodyPart</code> objects.

-     */

-    public void setBodyParts(List<BodyPart> bodyParts) {

-        this.bodyParts = bodyParts;

-        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {

-            it.next().setParent(parent);

-        }

-    }

-

-    /**

-     * Adds a body part to the end of the list of body parts.

-     *

-     * @param bodyPart the body part.

-     */

-    public void addBodyPart(BodyPart bodyPart) {

-        bodyParts.add(bodyPart);

-        bodyPart.setParent(parent);

-    }

-

-    /**

-     * Gets the preamble.

-     *

-     * @return the preamble.

-     */

-    public String getPreamble() {

-        return preamble;

-    }

-

-    /**

-     * Sets the preamble.

-     *

-     * @param preamble the preamble.

-     */

-    public void setPreamble(String preamble) {

-        this.preamble = preamble;

-    }

-

-    /**

-     *

-     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-        String boundary = getBoundary();

-        List<BodyPart> bodyParts = getBodyParts();

-

-        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(getCharset())),8192);

-

-        writer.write(getPreamble() + "\r\n");

-

-        for (int i = 0; i < bodyParts.size(); i++) {

-            writer.write(boundary + "\r\n");

-            bodyParts.get(i).writeTo(out);

-        }

-

-        writer.write(getEpilogue() + "\r\n");

-        writer.write(boundary + "--" + "\r\n");

-

-    }

-

-    /**

-     * Return the boundory of the parent Entity

-     *

-     * @return boundery

-     */

-    private String getBoundary() {

-        Entity e = getParent();

-        ContentTypeField cField = (ContentTypeField) e.getHeader().getField(

-                Field.CONTENT_TYPE);

-        return cField.getBoundary();

-    }

-

-    private String getCharset() {

-        Entity e = getParent();

-        String charString = ((ContentTypeField) e.getHeader().getField(Field.CONTENT_TYPE)).getCharset();

-        return charString;

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.james.mime4j.field.ContentTypeField;
+import org.apache.james.mime4j.field.Field;
+import org.apache.james.mime4j.util.CharsetUtil;
+
+/**
+ * Represents a MIME multipart body (see RFC 2045).A multipart body has a
+ * ordered list of body parts. The multipart body also has a preamble and
+ * epilogue. The preamble consists of whatever characters appear before the
+ * first body part while the epilogue consists of whatever characters come
+ * after the last body part.
+ *
+ *
+ * @version $Id: Multipart.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class Multipart implements Body {
+    private String preamble = "";
+    private String epilogue = "";
+    private List<BodyPart> bodyParts = new LinkedList<BodyPart>();
+    private Entity parent = null;
+    private String subType = "alternative";
+
+    /**
+     * Creates a new empty <code>Multipart</code> instance.
+     */
+    public Multipart() {
+    }
+
+    /**
+     * Gets the multipart sub-type. E.g. <code>alternative</code> (the default)
+     * or <code>parallel</code>. See RFC 2045 for common sub-types and their
+     * meaning.
+     *
+     * @return the multipart sub-type.
+     */
+    public String getSubType() {
+        return subType;
+    }
+
+    /**
+     * Sets the multipart sub-type. E.g. <code>alternative</code>
+     * or <code>parallel</code>. See RFC 2045 for common sub-types and their
+     * meaning.
+     *
+     * @param subType the sub-type.
+     */
+    public void setSubType(String subType) {
+        this.subType = subType;
+    }
+
+    /**
+     * @see org.apache.james.mime4j.message.Body#getParent()
+     */
+    public Entity getParent() {
+        return parent;
+    }
+
+    /**
+     * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)
+     */
+    public void setParent(Entity parent) {
+        this.parent = parent;
+        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {
+            it.next().setParent(parent);
+        }
+    }
+
+    /**
+     * Gets the epilogue.
+     *
+     * @return the epilogue.
+     */
+    public String getEpilogue() {
+        return epilogue;
+    }
+
+    /**
+     * Sets the epilogue.
+     *
+     * @param epilogue the epilogue.
+     */
+    public void setEpilogue(String epilogue) {
+        this.epilogue = epilogue;
+    }
+
+    /**
+     * Gets the list of body parts. The list is immutable.
+     *
+     * @return the list of <code>BodyPart</code> objects.
+     */
+    public List<BodyPart> getBodyParts() {
+        return Collections.unmodifiableList(bodyParts);
+    }
+
+    /**
+     * Sets the list of body parts.
+     *
+     * @param bodyParts the new list of <code>BodyPart</code> objects.
+     */
+    public void setBodyParts(List<BodyPart> bodyParts) {
+        this.bodyParts = bodyParts;
+        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {
+            it.next().setParent(parent);
+        }
+    }
+
+    /**
+     * Adds a body part to the end of the list of body parts.
+     *
+     * @param bodyPart the body part.
+     */
+    public void addBodyPart(BodyPart bodyPart) {
+        bodyParts.add(bodyPart);
+        bodyPart.setParent(parent);
+    }
+
+    /**
+     * Gets the preamble.
+     *
+     * @return the preamble.
+     */
+    public String getPreamble() {
+        return preamble;
+    }
+
+    /**
+     * Sets the preamble.
+     *
+     * @param preamble the preamble.
+     */
+    public void setPreamble(String preamble) {
+        this.preamble = preamble;
+    }
+
+    /**
+     *
+     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+        String boundary = getBoundary();
+        List<BodyPart> bodyParts = getBodyParts();
+
+        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(getCharset())),8192);
+
+        writer.write(getPreamble() + "\r\n");
+
+        for (int i = 0; i < bodyParts.size(); i++) {
+            writer.write(boundary + "\r\n");
+            bodyParts.get(i).writeTo(out);
+        }
+
+        writer.write(getEpilogue() + "\r\n");
+        writer.write(boundary + "--" + "\r\n");
+
+    }
+
+    /**
+     * Return the boundory of the parent Entity
+     *
+     * @return boundery
+     */
+    private String getBoundary() {
+        Entity e = getParent();
+        ContentTypeField cField = (ContentTypeField) e.getHeader().getField(
+                Field.CONTENT_TYPE);
+        return cField.getBoundary();
+    }
+
+    private String getCharset() {
+        Entity e = getParent();
+        String charString = ((ContentTypeField) e.getHeader().getField(Field.CONTENT_TYPE)).getCharset();
+        return charString;
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/TempFileBinaryBody.java b/src/org/apache/james/mime4j/message/TempFileBinaryBody.java
index 9db1482..7f9a400 100644
--- a/src/org/apache/james/mime4j/message/TempFileBinaryBody.java
+++ b/src/org/apache/james/mime4j/message/TempFileBinaryBody.java
@@ -1,91 +1,91 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-import org.apache.commons.io.IOUtils;

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.util.TempFile;

-import org.apache.james.mime4j.util.TempPath;

-import org.apache.james.mime4j.util.TempStorage;

-

-

-/**

- * Binary body backed by a {@link org.apache.james.mime4j.util.TempFile}.

- *

- * 

- * @version $Id: TempFileBinaryBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-class TempFileBinaryBody extends AbstractBody implements BinaryBody {

-    private static Log log = LogFactory.getLog(TempFileBinaryBody.class);

-    

-    private Entity parent = null;

-    private TempFile tempFile = null;

-

-    /**

-     * Use the given InputStream to build the TemporyFileBinaryBody

-     * 

-     * @param is the InputStream to use as source

-     * @throws IOException

-     */

-    public TempFileBinaryBody(InputStream is) throws IOException {

-        

-        TempPath tempPath = TempStorage.getInstance().getRootTempPath();

-        tempFile = tempPath.createTempFile("attachment", ".bin");

-        

-        OutputStream out = tempFile.getOutputStream();

-        IOUtils.copy(is, out);

-        out.close();

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.AbstractBody#getParent()

-     */

-    public Entity getParent() {

-        return parent;

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.AbstractBody#setParent(org.apache.james.mime4j.message.Entity)

-     */

-    public void setParent(Entity parent) {

-        this.parent = parent;

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.BinaryBody#getInputStream()

-     */

-    public InputStream getInputStream() throws IOException {

-        return tempFile.getInputStream();

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-	IOUtils.copy(getInputStream(),out);

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.TempFile;
+import org.apache.james.mime4j.util.TempPath;
+import org.apache.james.mime4j.util.TempStorage;
+
+
+/**
+ * Binary body backed by a {@link org.apache.james.mime4j.util.TempFile}.
+ *
+ * 
+ * @version $Id: TempFileBinaryBody.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+class TempFileBinaryBody extends AbstractBody implements BinaryBody {
+    private static Log log = LogFactory.getLog(TempFileBinaryBody.class);
+    
+    private Entity parent = null;
+    private TempFile tempFile = null;
+
+    /**
+     * Use the given InputStream to build the TemporyFileBinaryBody
+     * 
+     * @param is the InputStream to use as source
+     * @throws IOException
+     */
+    public TempFileBinaryBody(InputStream is) throws IOException {
+        
+        TempPath tempPath = TempStorage.getInstance().getRootTempPath();
+        tempFile = tempPath.createTempFile("attachment", ".bin");
+        
+        OutputStream out = tempFile.getOutputStream();
+        IOUtils.copy(is, out);
+        out.close();
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.AbstractBody#getParent()
+     */
+    public Entity getParent() {
+        return parent;
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.AbstractBody#setParent(org.apache.james.mime4j.message.Entity)
+     */
+    public void setParent(Entity parent) {
+        this.parent = parent;
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.BinaryBody#getInputStream()
+     */
+    public InputStream getInputStream() throws IOException {
+        return tempFile.getInputStream();
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+	IOUtils.copy(getInputStream(),out);
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/TempFileTextBody.java b/src/org/apache/james/mime4j/message/TempFileTextBody.java
index b74e15d..3230d34 100644
--- a/src/org/apache/james/mime4j/message/TempFileTextBody.java
+++ b/src/org/apache/james/mime4j/message/TempFileTextBody.java
@@ -1,117 +1,117 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.InputStreamReader;

-import java.io.OutputStream;

-import java.io.Reader;

-import java.io.UnsupportedEncodingException;

-

-import org.apache.commons.io.IOUtils;

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-import org.apache.james.mime4j.util.CharsetUtil;

-import org.apache.james.mime4j.util.TempFile;

-import org.apache.james.mime4j.util.TempPath;

-import org.apache.james.mime4j.util.TempStorage;

-

-

-/**

- * Text body backed by a {@link org.apache.james.mime4j.util.TempFile}.

- *

- * 

- * @version $Id: TempFileTextBody.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $

- */

-class TempFileTextBody extends AbstractBody implements TextBody {

-    private static Log log = LogFactory.getLog(TempFileTextBody.class);

-    

-    private String mimeCharset = null;

-    private TempFile tempFile = null;

-

-    public TempFileTextBody(InputStream is) throws IOException {

-        this(is, null);

-    }

-    

-    public TempFileTextBody(InputStream is, String mimeCharset) 

-            throws IOException {

-        

-        this.mimeCharset = mimeCharset;

-        

-        TempPath tempPath = TempStorage.getInstance().getRootTempPath();

-        tempFile = tempPath.createTempFile("attachment", ".txt");

-        

-        OutputStream out = tempFile.getOutputStream();

-        IOUtils.copy(is, out);

-        out.close();

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.message.TextBody#getReader()

-     */

-    public Reader getReader() throws UnsupportedEncodingException, IOException {

-        String javaCharset = null;

-        if (mimeCharset != null) {

-            javaCharset = CharsetUtil.toJavaCharset(mimeCharset);

-        }

-        

-        if (javaCharset == null) {

-            javaCharset = "ISO-8859-1";

-            

-            if (log.isWarnEnabled()) {

-                if (mimeCharset == null) {

-                    log.warn("No MIME charset specified. Using " + javaCharset

-                            + " instead.");

-                } else {

-                    log.warn("MIME charset '" + mimeCharset + "' has no "

-                            + "corresponding Java charset. Using " + javaCharset

-                            + " instead.");

-                }

-            }

-        }

-        /*

-            if (log.isWarnEnabled()) {

-                if (mimeCharset == null) {

-                    log.warn("No MIME charset specified. Using the "

-                           + "platform's default charset.");

-                } else {

-                    log.warn("MIME charset '" + mimeCharset + "' has no "

-                            + "corresponding Java charset. Using the "

-                            + "platform's default charset.");

-                }

-            }

-            

-            return new InputStreamReader(tempFile.getInputStream());

-        }*/

-        

-        return new InputStreamReader(tempFile.getInputStream(), javaCharset);

-    }

-    

-    

-    /**

-     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)

-     */

-    public void writeTo(OutputStream out) throws IOException {

-	IOUtils.copy(tempFile.getInputStream(), out);	

-    }

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.io.IOUtils;
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.TempFile;
+import org.apache.james.mime4j.util.TempPath;
+import org.apache.james.mime4j.util.TempStorage;
+
+
+/**
+ * Text body backed by a {@link org.apache.james.mime4j.util.TempFile}.
+ *
+ * 
+ * @version $Id: TempFileTextBody.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $
+ */
+class TempFileTextBody extends AbstractBody implements TextBody {
+    private static Log log = LogFactory.getLog(TempFileTextBody.class);
+    
+    private String mimeCharset = null;
+    private TempFile tempFile = null;
+
+    public TempFileTextBody(InputStream is) throws IOException {
+        this(is, null);
+    }
+    
+    public TempFileTextBody(InputStream is, String mimeCharset) 
+            throws IOException {
+        
+        this.mimeCharset = mimeCharset;
+        
+        TempPath tempPath = TempStorage.getInstance().getRootTempPath();
+        tempFile = tempPath.createTempFile("attachment", ".txt");
+        
+        OutputStream out = tempFile.getOutputStream();
+        IOUtils.copy(is, out);
+        out.close();
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.message.TextBody#getReader()
+     */
+    public Reader getReader() throws UnsupportedEncodingException, IOException {
+        String javaCharset = null;
+        if (mimeCharset != null) {
+            javaCharset = CharsetUtil.toJavaCharset(mimeCharset);
+        }
+        
+        if (javaCharset == null) {
+            javaCharset = "ISO-8859-1";
+            
+            if (log.isWarnEnabled()) {
+                if (mimeCharset == null) {
+                    log.warn("No MIME charset specified. Using " + javaCharset
+                            + " instead.");
+                } else {
+                    log.warn("MIME charset '" + mimeCharset + "' has no "
+                            + "corresponding Java charset. Using " + javaCharset
+                            + " instead.");
+                }
+            }
+        }
+        /*
+            if (log.isWarnEnabled()) {
+                if (mimeCharset == null) {
+                    log.warn("No MIME charset specified. Using the "
+                           + "platform's default charset.");
+                } else {
+                    log.warn("MIME charset '" + mimeCharset + "' has no "
+                            + "corresponding Java charset. Using the "
+                            + "platform's default charset.");
+                }
+            }
+            
+            return new InputStreamReader(tempFile.getInputStream());
+        }*/
+        
+        return new InputStreamReader(tempFile.getInputStream(), javaCharset);
+    }
+    
+    
+    /**
+     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+	IOUtils.copy(tempFile.getInputStream(), out);	
+    }
+}
diff --git a/src/org/apache/james/mime4j/message/TextBody.java b/src/org/apache/james/mime4j/message/TextBody.java
index 4fe7144..cbf24c2 100644
--- a/src/org/apache/james/mime4j/message/TextBody.java
+++ b/src/org/apache/james/mime4j/message/TextBody.java
@@ -1,42 +1,42 @@
-/****************************************************************

- * 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.message;

-

-import java.io.IOException;

-import java.io.Reader;

-

-

-/**

- * Encapsulates the contents of a <code>text/*</code> entity body.

- *

- * 

- * @version $Id: TextBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public interface TextBody extends Body {

-    

-    /**

-     * Gets a <code>Reader</code> which may be used to read out the contents

-     * of this body.

-     * 

-     * @return the <code>Reader</code>.

-     * @throws IOException on I/O errors.

-     */

-    Reader getReader() throws IOException;

-}

+/****************************************************************
+ * 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.message;
+
+import java.io.IOException;
+import java.io.Reader;
+
+
+/**
+ * Encapsulates the contents of a <code>text/*</code> entity body.
+ *
+ * 
+ * @version $Id: TextBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public interface TextBody extends Body {
+    
+    /**
+     * Gets a <code>Reader</code> which may be used to read out the contents
+     * of this body.
+     * 
+     * @return the <code>Reader</code>.
+     * @throws IOException on I/O errors.
+     */
+    Reader getReader() throws IOException;
+}
diff --git a/src/org/apache/james/mime4j/util/CharsetUtil.java b/src/org/apache/james/mime4j/util/CharsetUtil.java
index 30a3413..ea92334 100644
--- a/src/org/apache/james/mime4j/util/CharsetUtil.java
+++ b/src/org/apache/james/mime4j/util/CharsetUtil.java
@@ -1,1249 +1,1249 @@
-/****************************************************************

- * 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.UnsupportedEncodingException;

-import java.nio.charset.IllegalCharsetNameException;

-import java.nio.charset.UnsupportedCharsetException;

-import java.util.HashMap;

-import java.util.Locale;

-import java.util.TreeSet;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-

-/**

- * Utility class for working with character sets. It is somewhat similar to

- * the Java 1.4 <code>java.nio.charset.Charset</code> class but knows many

- * more aliases and is compatible with Java 1.3. It will use a simple detection

- * mechanism to detect what character sets the current VM supports. This will

- * be a sub-set of the character sets listed in the

- * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">

- * Java 1.5 (J2SE5.0) Supported Encodings</a> document.

- * <p>

- * The <a href="http://www.iana.org/assignments/character-sets">

- * IANA Character Sets</a> document has been used to determine the preferred

- * MIME character set names and to get a list of known aliases.

- * <p>

- * This is a complete list of the character sets known to this class:

- * <table>

- *     <tr>

- *         <td>Canonical (Java) name</td>

- *         <td>MIME preferred</td>

- *         <td>Aliases</td>

- *     </tr>

- *     <tr>

- *         <td>ASCII</td>

- *         <td>US-ASCII</td>

- *         <td>ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ISO646-US us IBM367 cp367 csASCII ascii7 646 iso_646.irv:1983 </td>

- *     </tr>

- *     <tr>

- *         <td>Big5</td>

- *         <td>Big5</td>

- *         <td>csBig5 CN-Big5 BIG-FIVE BIGFIVE </td>

- *     </tr>

- *     <tr>

- *         <td>Big5_HKSCS</td>

- *         <td>Big5-HKSCS</td>

- *         <td>big5hkscs </td>

- *     </tr>

- *     <tr>

- *         <td>Big5_Solaris</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp037</td>

- *         <td>IBM037</td>

- *         <td>ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1006</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1025</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1026</td>

- *         <td>IBM1026</td>

- *         <td>csIBM1026 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1046</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1047</td>

- *         <td>IBM1047</td>

- *         <td>IBM-1047 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1097</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1098</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1112</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1122</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1123</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1124</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1140</td>

- *         <td>IBM01140</td>

- *         <td>CCSID01140 CP01140 ebcdic-us-37+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1141</td>

- *         <td>IBM01141</td>

- *         <td>CCSID01141 CP01141 ebcdic-de-273+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1142</td>

- *         <td>IBM01142</td>

- *         <td>CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1143</td>

- *         <td>IBM01143</td>

- *         <td>CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1144</td>

- *         <td>IBM01144</td>

- *         <td>CCSID01144 CP01144 ebcdic-it-280+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1145</td>

- *         <td>IBM01145</td>

- *         <td>CCSID01145 CP01145 ebcdic-es-284+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1146</td>

- *         <td>IBM01146</td>

- *         <td>CCSID01146 CP01146 ebcdic-gb-285+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1147</td>

- *         <td>IBM01147</td>

- *         <td>CCSID01147 CP01147 ebcdic-fr-297+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1148</td>

- *         <td>IBM01148</td>

- *         <td>CCSID01148 CP01148 ebcdic-international-500+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1149</td>

- *         <td>IBM01149</td>

- *         <td>CCSID01149 CP01149 ebcdic-is-871+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp1250</td>

- *         <td>windows-1250</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1251</td>

- *         <td>windows-1251</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1252</td>

- *         <td>windows-1252</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1253</td>

- *         <td>windows-1253</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1254</td>

- *         <td>windows-1254</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1255</td>

- *         <td>windows-1255</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1256</td>

- *         <td>windows-1256</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1257</td>

- *         <td>windows-1257</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1258</td>

- *         <td>windows-1258</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1381</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp1383</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp273</td>

- *         <td>IBM273</td>

- *         <td>csIBM273 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp277</td>

- *         <td>IBM277</td>

- *         <td>EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp278</td>

- *         <td>IBM278</td>

- *         <td>CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp280</td>

- *         <td>IBM280</td>

- *         <td>ebcdic-cp-it csIBM280 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp284</td>

- *         <td>IBM284</td>

- *         <td>ebcdic-cp-es csIBM284 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp285</td>

- *         <td>IBM285</td>

- *         <td>ebcdic-cp-gb csIBM285 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp297</td>

- *         <td>IBM297</td>

- *         <td>ebcdic-cp-fr csIBM297 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp33722</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp420</td>

- *         <td>IBM420</td>

- *         <td>ebcdic-cp-ar1 csIBM420 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp424</td>

- *         <td>IBM424</td>

- *         <td>ebcdic-cp-he csIBM424 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp437</td>

- *         <td>IBM437</td>

- *         <td>437 csPC8CodePage437 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp500</td>

- *         <td>IBM500</td>

- *         <td>ebcdic-cp-be ebcdic-cp-ch csIBM500 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp737</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp775</td>

- *         <td>IBM775</td>

- *         <td>csPC775Baltic </td>

- *     </tr>

- *     <tr>

- *         <td>Cp838</td>

- *         <td>IBM-Thai</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp850</td>

- *         <td>IBM850</td>

- *         <td>850 csPC850Multilingual </td>

- *     </tr>

- *     <tr>

- *         <td>Cp852</td>

- *         <td>IBM852</td>

- *         <td>852 csPCp852 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp855</td>

- *         <td>IBM855</td>

- *         <td>855 csIBM855 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp856</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp857</td>

- *         <td>IBM857</td>

- *         <td>857 csIBM857 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp858</td>

- *         <td>IBM00858</td>

- *         <td>CCSID00858 CP00858 PC-Multilingual-850+euro </td>

- *     </tr>

- *     <tr>

- *         <td>Cp860</td>

- *         <td>IBM860</td>

- *         <td>860 csIBM860 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp861</td>

- *         <td>IBM861</td>

- *         <td>861 cp-is csIBM861 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp862</td>

- *         <td>IBM862</td>

- *         <td>862 csPC862LatinHebrew </td>

- *     </tr>

- *     <tr>

- *         <td>Cp863</td>

- *         <td>IBM863</td>

- *         <td>863 csIBM863 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp864</td>

- *         <td>IBM864</td>

- *         <td>cp864 csIBM864 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp865</td>

- *         <td>IBM865</td>

- *         <td>865 csIBM865 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp866</td>

- *         <td>IBM866</td>

- *         <td>866 csIBM866 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp868</td>

- *         <td>IBM868</td>

- *         <td>cp-ar csIBM868 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp869</td>

- *         <td>IBM869</td>

- *         <td>cp-gr csIBM869 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp870</td>

- *         <td>IBM870</td>

- *         <td>ebcdic-cp-roece ebcdic-cp-yu csIBM870 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp871</td>

- *         <td>IBM871</td>

- *         <td>ebcdic-cp-is csIBM871 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp875</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp918</td>

- *         <td>IBM918</td>

- *         <td>ebcdic-cp-ar2 csIBM918 </td>

- *     </tr>

- *     <tr>

- *         <td>Cp921</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp922</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp930</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp933</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp935</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp937</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp939</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp942</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp942C</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp943</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp943C</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp948</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp949</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp949C</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp950</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp964</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>Cp970</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>EUC_CN</td>

- *         <td>GB2312</td>

- *         <td>x-EUC-CN csGB2312 euccn euc-cn gb2312-80 gb2312-1980 CN-GB CN-GB-ISOIR165 </td>

- *     </tr>

- *     <tr>

- *         <td>EUC_JP</td>

- *         <td>EUC-JP</td>

- *         <td>csEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese eucjis x-eucjp eucjp x-euc-jp </td>

- *     </tr>

- *     <tr>

- *         <td>EUC_JP_LINUX</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>EUC_JP_Solaris</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>EUC_KR</td>

- *         <td>EUC-KR</td>

- *         <td>csEUCKR ksc5601 5601 ksc5601_1987 ksc_5601 ksc5601-1987 ks_c_5601-1987 euckr </td>

- *     </tr>

- *     <tr>

- *         <td>EUC_TW</td>

- *         <td>EUC-TW</td>

- *         <td>x-EUC-TW cns11643 euctw </td>

- *     </tr>

- *     <tr>

- *         <td>GB18030</td>

- *         <td>GB18030</td>

- *         <td>gb18030-2000 </td>

- *     </tr>

- *     <tr>

- *         <td>GBK</td>

- *         <td>windows-936</td>

- *         <td>CP936 MS936 ms_936 x-mswin-936 </td>

- *     </tr>

- *     <tr>

- *         <td>ISCII91</td>

- *         <td>?</td>

- *         <td>x-ISCII91 iscii </td>

- *     </tr>

- *     <tr>

- *         <td>ISO2022CN</td>

- *         <td>ISO-2022-CN</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>ISO2022JP</td>

- *         <td>ISO-2022-JP</td>

- *         <td>csISO2022JP JIS jis_encoding csjisencoding </td>

- *     </tr>

- *     <tr>

- *         <td>ISO2022KR</td>

- *         <td>ISO-2022-KR</td>

- *         <td>csISO2022KR </td>

- *     </tr>

- *     <tr>

- *         <td>ISO2022_CN_CNS</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>ISO2022_CN_GB</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_1</td>

- *         <td>ISO-8859-1</td>

- *         <td>ISO_8859-1:1987 iso-ir-100 ISO_8859-1 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 819 IBM-819 ISO8859-1 ISO_8859_1 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_13</td>

- *         <td>ISO-8859-13</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_15</td>

- *         <td>ISO-8859-15</td>

- *         <td>ISO_8859-15 Latin-9 8859_15 csISOlatin9 IBM923 cp923 923 L9 IBM-923 ISO8859-15 LATIN9 LATIN0 csISOlatin0 ISO8859_15_FDIS </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_2</td>

- *         <td>ISO-8859-2</td>

- *         <td>ISO_8859-2:1987 iso-ir-101 ISO_8859-2 latin2 l2 csISOLatin2 8859_2 iso8859_2 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_3</td>

- *         <td>ISO-8859-3</td>

- *         <td>ISO_8859-3:1988 iso-ir-109 ISO_8859-3 latin3 l3 csISOLatin3 8859_3 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_4</td>

- *         <td>ISO-8859-4</td>

- *         <td>ISO_8859-4:1988 iso-ir-110 ISO_8859-4 latin4 l4 csISOLatin4 8859_4 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_5</td>

- *         <td>ISO-8859-5</td>

- *         <td>ISO_8859-5:1988 iso-ir-144 ISO_8859-5 cyrillic csISOLatinCyrillic 8859_5 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_6</td>

- *         <td>ISO-8859-6</td>

- *         <td>ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ECMA-114 ASMO-708 arabic csISOLatinArabic 8859_6 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_7</td>

- *         <td>ISO-8859-7</td>

- *         <td>ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 sun_eu_greek </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_8</td>

- *         <td>ISO-8859-8</td>

- *         <td>ISO_8859-8:1988 iso-ir-138 ISO_8859-8 hebrew csISOLatinHebrew 8859_8 </td>

- *     </tr>

- *     <tr>

- *         <td>ISO8859_9</td>

- *         <td>ISO-8859-9</td>

- *         <td>ISO_8859-9:1989 iso-ir-148 ISO_8859-9 latin5 l5 csISOLatin5 8859_9 </td>

- *     </tr>

- *     <tr>

- *         <td>JISAutoDetect</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>JIS_C6626-1983</td>

- *         <td>JIS_C6626-1983</td>

- *         <td>x-JIS0208 JIS0208 csISO87JISX0208 x0208 JIS_X0208-1983 iso-ir-87 </td>

- *     </tr>

- *     <tr>

- *         <td>JIS_X0201</td>

- *         <td>JIS_X0201</td>

- *         <td>X0201 JIS0201 csHalfWidthKatakana </td>

- *     </tr>

- *     <tr>

- *         <td>JIS_X0212-1990</td>

- *         <td>JIS_X0212-1990</td>

- *         <td>iso-ir-159 x0212 JIS0212 csISO159JISX02121990 </td>

- *     </tr>

- *     <tr>

- *         <td>KOI8_R</td>

- *         <td>KOI8-R</td>

- *         <td>csKOI8R koi8 </td>

- *     </tr>

- *     <tr>

- *         <td>MS874</td>

- *         <td>windows-874</td>

- *         <td>cp874 </td>

- *     </tr>

- *     <tr>

- *         <td>MS932</td>

- *         <td>Windows-31J</td>

- *         <td>windows-932 csWindows31J x-ms-cp932 </td>

- *     </tr>

- *     <tr>

- *         <td>MS949</td>

- *         <td>windows-949</td>

- *         <td>windows949 ms_949 x-windows-949 </td>

- *     </tr>

- *     <tr>

- *         <td>MS950</td>

- *         <td>windows-950</td>

- *         <td>x-windows-950 </td>

- *     </tr>

- *     <tr>

- *         <td>MS950_HKSCS</td>

- *         <td></td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacArabic</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacCentralEurope</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacCroatian</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacCyrillic</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacDingbat</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacGreek</td>

- *         <td>MacGreek</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacHebrew</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacIceland</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacRoman</td>

- *         <td>MacRoman</td>

- *         <td>Macintosh MAC csMacintosh </td>

- *     </tr>

- *     <tr>

- *         <td>MacRomania</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacSymbol</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacThai</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacTurkish</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>MacUkraine</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>SJIS</td>

- *         <td>Shift_JIS</td>

- *         <td>MS_Kanji csShiftJIS shift-jis x-sjis pck </td>

- *     </tr>

- *     <tr>

- *         <td>TIS620</td>

- *         <td>TIS-620</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>UTF-16</td>

- *         <td>UTF-16</td>

- *         <td>UTF_16 </td>

- *     </tr>

- *     <tr>

- *         <td>UTF8</td>

- *         <td>UTF-8</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>UnicodeBig</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>UnicodeBigUnmarked</td>

- *         <td>UTF-16BE</td>

- *         <td>X-UTF-16BE UTF_16BE ISO-10646-UCS-2 </td>

- *     </tr>

- *     <tr>

- *         <td>UnicodeLittle</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- *     <tr>

- *         <td>UnicodeLittleUnmarked</td>

- *         <td>UTF-16LE</td>

- *         <td>UTF_16LE X-UTF-16LE </td>

- *     </tr>

- *     <tr>

- *         <td>x-Johab</td>

- *         <td>johab</td>

- *         <td>johab cp1361 ms1361 ksc5601-1992 ksc5601_1992 </td>

- *     </tr>

- *     <tr>

- *         <td>x-iso-8859-11</td>

- *         <td>?</td>

- *         <td></td>

- *     </tr>

- * </table>

- *

- *

- * @version $Id: CharsetUtil.java,v 1.1 2004/10/25 07:26:46 ntherning Exp $

- */

-public class CharsetUtil {

-    private static Log log = LogFactory.getLog(CharsetUtil.class);

-

-    private static class Charset implements Comparable<Charset> {

-        private String canonical = null;

-        private String mime = null;

-        private String[] aliases = null;

-

-        private Charset(String canonical, String mime, String[] aliases) {

-            this.canonical = canonical;

-            this.mime = mime;

-            this.aliases = aliases;

-        }

-

-        public int compareTo(Charset c) {

-            return this.canonical.compareTo(c.canonical);

-        }

-    }

-

-    private static Charset[] JAVA_CHARSETS = {

-        new Charset("ISO8859_1", "ISO-8859-1",

-                    new String[] {"ISO_8859-1:1987", "iso-ir-100", "ISO_8859-1",

-                                  "latin1", "l1", "IBM819", "CP819",

-                                  "csISOLatin1", "8859_1", "819", "IBM-819",

-                                  "ISO8859-1", "ISO_8859_1"}),

-        new Charset("ISO8859_2", "ISO-8859-2",

-                    new String[] {"ISO_8859-2:1987", "iso-ir-101", "ISO_8859-2",

-                                  "latin2", "l2", "csISOLatin2", "8859_2",

-                                  "iso8859_2"}),

-        new Charset("ISO8859_3", "ISO-8859-3", new String[] {"ISO_8859-3:1988", "iso-ir-109", "ISO_8859-3", "latin3", "l3", "csISOLatin3", "8859_3"}),

-        new Charset("ISO8859_4", "ISO-8859-4",

-                    new String[] {"ISO_8859-4:1988", "iso-ir-110", "ISO_8859-4",

-                                  "latin4", "l4", "csISOLatin4", "8859_4"}),

-        new Charset("ISO8859_5", "ISO-8859-5",

-                    new String[] {"ISO_8859-5:1988", "iso-ir-144", "ISO_8859-5",

-                                  "cyrillic", "csISOLatinCyrillic", "8859_5"}),

-        new Charset("ISO8859_6", "ISO-8859-6", new String[] {"ISO_8859-6:1987", "iso-ir-127", "ISO_8859-6", "ECMA-114", "ASMO-708", "arabic", "csISOLatinArabic", "8859_6"}),

-        new Charset("ISO8859_7", "ISO-8859-7",

-                    new String[] {"ISO_8859-7:1987", "iso-ir-126", "ISO_8859-7",

-                                  "ELOT_928", "ECMA-118", "greek", "greek8",

-                                  "csISOLatinGreek", "8859_7", "sun_eu_greek"}),

-        new Charset("ISO8859_8", "ISO-8859-8", new String[] {"ISO_8859-8:1988", "iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew", "8859_8"}),

-        new Charset("ISO8859_9", "ISO-8859-9",

-                    new String[] {"ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9",

-                                  "latin5", "l5", "csISOLatin5", "8859_9"}),

-

-        new Charset("ISO8859_13", "ISO-8859-13", new String[] {}),

-        new Charset("ISO8859_15", "ISO-8859-15",

-                    new String[] {"ISO_8859-15", "Latin-9", "8859_15",

-                                  "csISOlatin9", "IBM923", "cp923", "923", "L9",

-                                  "IBM-923", "ISO8859-15", "LATIN9", "LATIN0",

-                                  "csISOlatin0", "ISO8859_15_FDIS"}),

-        new Charset("KOI8_R", "KOI8-R", new String[] {"csKOI8R", "koi8"}),

-        new Charset("ASCII", "US-ASCII",

-                    new String[] {"ANSI_X3.4-1968", "iso-ir-6",

-                                  "ANSI_X3.4-1986", "ISO_646.irv:1991",

-                                  "ISO646-US", "us", "IBM367", "cp367",

-                                  "csASCII", "ascii7", "646", "iso_646.irv:1983"}),

-        new Charset("UTF8", "UTF-8", new String[] {}),

-        new Charset("UTF-16", "UTF-16", new String[] {"UTF_16"}),

-        new Charset("UnicodeBigUnmarked", "UTF-16BE", new String[] {"X-UTF-16BE", "UTF_16BE", "ISO-10646-UCS-2"}),

-        new Charset("UnicodeLittleUnmarked", "UTF-16LE", new String[] {"UTF_16LE", "X-UTF-16LE"}),

-        new Charset("Big5", "Big5", new String[] {"csBig5", "CN-Big5", "BIG-FIVE", "BIGFIVE"}),

-        new Charset("Big5_HKSCS", "Big5-HKSCS", new String[] {"big5hkscs"}),

-        new Charset("EUC_JP", "EUC-JP",

-                    new String[] {"csEUCPkdFmtJapanese",

-                              "Extended_UNIX_Code_Packed_Format_for_Japanese",

-                              "eucjis", "x-eucjp", "eucjp", "x-euc-jp"}),

-        new Charset("EUC_KR", "EUC-KR",

-                    new String[] {"csEUCKR", "ksc5601", "5601", "ksc5601_1987",

-                                  "ksc_5601", "ksc5601-1987", "ks_c_5601-1987",

-                                  "euckr"}),

-        new Charset("GB18030", "GB18030", new String[] {"gb18030-2000"}),

-        new Charset("EUC_CN", "GB2312", new String[] {"x-EUC-CN", "csGB2312", "euccn", "euc-cn", "gb2312-80", "gb2312-1980", "CN-GB", "CN-GB-ISOIR165"}),

-        new Charset("GBK", "windows-936", new String[] {"CP936", "MS936", "ms_936", "x-mswin-936"}),

-

-        new Charset("Cp037", "IBM037", new String[] {"ebcdic-cp-us", "ebcdic-cp-ca", "ebcdic-cp-wt", "ebcdic-cp-nl", "csIBM037"}),

-        new Charset("Cp273", "IBM273", new String[] {"csIBM273"}),

-        new Charset("Cp277", "IBM277", new String[] {"EBCDIC-CP-DK", "EBCDIC-CP-NO", "csIBM277"}),

-        new Charset("Cp278", "IBM278", new String[] {"CP278", "ebcdic-cp-fi", "ebcdic-cp-se", "csIBM278"}),

-        new Charset("Cp280", "IBM280", new String[] {"ebcdic-cp-it", "csIBM280"}),

-        new Charset("Cp284", "IBM284", new String[] {"ebcdic-cp-es", "csIBM284"}),

-        new Charset("Cp285", "IBM285", new String[] {"ebcdic-cp-gb", "csIBM285"}),

-        new Charset("Cp297", "IBM297", new String[] {"ebcdic-cp-fr", "csIBM297"}),

-        new Charset("Cp420", "IBM420", new String[] {"ebcdic-cp-ar1", "csIBM420"}),

-        new Charset("Cp424", "IBM424", new String[] {"ebcdic-cp-he", "csIBM424"}),

-        new Charset("Cp437", "IBM437", new String[] {"437", "csPC8CodePage437"}),

-        new Charset("Cp500", "IBM500", new String[] {"ebcdic-cp-be", "ebcdic-cp-ch", "csIBM500"}),

-        new Charset("Cp775", "IBM775", new String[] {"csPC775Baltic"}),

-        new Charset("Cp838", "IBM-Thai", new String[] {}),

-        new Charset("Cp850", "IBM850", new String[] {"850", "csPC850Multilingual"}),

-        new Charset("Cp852", "IBM852", new String[] {"852", "csPCp852"}),

-        new Charset("Cp855", "IBM855", new String[] {"855", "csIBM855"}),

-        new Charset("Cp857", "IBM857", new String[] {"857", "csIBM857"}),

-        new Charset("Cp858", "IBM00858",

-                new String[] {"CCSID00858", "CP00858",

-                              "PC-Multilingual-850+euro"}),

-        new Charset("Cp860", "IBM860", new String[] {"860", "csIBM860"}),

-        new Charset("Cp861", "IBM861", new String[] {"861", "cp-is", "csIBM861"}),

-        new Charset("Cp862", "IBM862", new String[] {"862", "csPC862LatinHebrew"}),

-        new Charset("Cp863", "IBM863", new String[] {"863", "csIBM863"}),

-        new Charset("Cp864", "IBM864", new String[] {"cp864", "csIBM864"}),

-        new Charset("Cp865", "IBM865", new String[] {"865", "csIBM865"}),

-        new Charset("Cp866", "IBM866", new String[] {"866", "csIBM866"}),

-        new Charset("Cp868", "IBM868", new String[] {"cp-ar", "csIBM868"}),

-        new Charset("Cp869", "IBM869", new String[] {"cp-gr", "csIBM869"}),

-        new Charset("Cp870", "IBM870", new String[] {"ebcdic-cp-roece", "ebcdic-cp-yu", "csIBM870"}),

-        new Charset("Cp871", "IBM871", new String[] {"ebcdic-cp-is", "csIBM871"}),

-        new Charset("Cp918", "IBM918", new String[] {"ebcdic-cp-ar2", "csIBM918"}),

-        new Charset("Cp1026", "IBM1026", new String[] {"csIBM1026"}),

-        new Charset("Cp1047", "IBM1047", new String[] {"IBM-1047"}),

-        new Charset("Cp1140", "IBM01140",

-                    new String[] {"CCSID01140", "CP01140",

-                                  "ebcdic-us-37+euro"}),

-        new Charset("Cp1141", "IBM01141",

-                    new String[] {"CCSID01141", "CP01141",

-                                  "ebcdic-de-273+euro"}),

-        new Charset("Cp1142", "IBM01142", new String[] {"CCSID01142", "CP01142", "ebcdic-dk-277+euro", "ebcdic-no-277+euro"}),

-        new Charset("Cp1143", "IBM01143", new String[] {"CCSID01143", "CP01143", "ebcdic-fi-278+euro", "ebcdic-se-278+euro"}),

-        new Charset("Cp1144", "IBM01144", new String[] {"CCSID01144", "CP01144", "ebcdic-it-280+euro"}),

-        new Charset("Cp1145", "IBM01145", new String[] {"CCSID01145", "CP01145", "ebcdic-es-284+euro"}),

-        new Charset("Cp1146", "IBM01146", new String[] {"CCSID01146", "CP01146", "ebcdic-gb-285+euro"}),

-        new Charset("Cp1147", "IBM01147", new String[] {"CCSID01147", "CP01147", "ebcdic-fr-297+euro"}),

-        new Charset("Cp1148", "IBM01148", new String[] {"CCSID01148", "CP01148", "ebcdic-international-500+euro"}),

-        new Charset("Cp1149", "IBM01149", new String[] {"CCSID01149", "CP01149", "ebcdic-is-871+euro"}),

-        new Charset("Cp1250", "windows-1250", new String[] {}),

-        new Charset("Cp1251", "windows-1251", new String[] {}),

-        new Charset("Cp1252", "windows-1252", new String[] {}),

-        new Charset("Cp1253", "windows-1253", new String[] {}),

-        new Charset("Cp1254", "windows-1254", new String[] {}),

-        new Charset("Cp1255", "windows-1255", new String[] {}),

-        new Charset("Cp1256", "windows-1256", new String[] {}),

-        new Charset("Cp1257", "windows-1257", new String[] {}),

-        new Charset("Cp1258", "windows-1258", new String[] {}),

-        new Charset("ISO2022CN", "ISO-2022-CN", new String[] {}),

-        new Charset("ISO2022JP", "ISO-2022-JP", new String[] {"csISO2022JP", "JIS", "jis_encoding", "csjisencoding"}),

-        new Charset("ISO2022KR", "ISO-2022-KR", new String[] {"csISO2022KR"}),

-        new Charset("JIS_X0201", "JIS_X0201", new String[] {"X0201", "JIS0201", "csHalfWidthKatakana"}),

-        new Charset("JIS_X0212-1990", "JIS_X0212-1990", new String[] {"iso-ir-159", "x0212", "JIS0212", "csISO159JISX02121990"}),

-        new Charset("JIS_C6626-1983", "JIS_C6626-1983", new String[] {"x-JIS0208", "JIS0208", "csISO87JISX0208", "x0208", "JIS_X0208-1983", "iso-ir-87"}),

-        new Charset("SJIS", "Shift_JIS", new String[] {"MS_Kanji", "csShiftJIS", "shift-jis", "x-sjis", "pck"}),

-        new Charset("TIS620", "TIS-620", new String[] {}),

-        new Charset("MS932", "Windows-31J", new String[] {"windows-932", "csWindows31J", "x-ms-cp932"}),

-        new Charset("EUC_TW", "EUC-TW", new String[] {"x-EUC-TW", "cns11643", "euctw"}),

-        new Charset("x-Johab", "johab", new String[] {"johab", "cp1361", "ms1361", "ksc5601-1992", "ksc5601_1992"}),

-        new Charset("MS950_HKSCS", "", new String[] {}),

-        new Charset("MS874", "windows-874", new String[] {"cp874"}),

-        new Charset("MS949", "windows-949", new String[] {"windows949", "ms_949", "x-windows-949"}),

-        new Charset("MS950", "windows-950", new String[] {"x-windows-950"}),

-

-        new Charset("Cp737", null, new String[] {}),

-        new Charset("Cp856", null, new String[] {}),

-        new Charset("Cp875", null, new String[] {}),

-        new Charset("Cp921", null, new String[] {}),

-        new Charset("Cp922", null, new String[] {}),

-        new Charset("Cp930", null, new String[] {}),

-        new Charset("Cp933", null, new String[] {}),

-        new Charset("Cp935", null, new String[] {}),

-        new Charset("Cp937", null, new String[] {}),

-        new Charset("Cp939", null, new String[] {}),

-        new Charset("Cp942", null, new String[] {}),

-        new Charset("Cp942C", null, new String[] {}),

-        new Charset("Cp943", null, new String[] {}),

-        new Charset("Cp943C", null, new String[] {}),

-        new Charset("Cp948", null, new String[] {}),

-        new Charset("Cp949", null, new String[] {}),

-        new Charset("Cp949C", null, new String[] {}),

-        new Charset("Cp950", null, new String[] {}),

-        new Charset("Cp964", null, new String[] {}),

-        new Charset("Cp970", null, new String[] {}),

-        new Charset("Cp1006", null, new String[] {}),

-        new Charset("Cp1025", null, new String[] {}),

-        new Charset("Cp1046", null, new String[] {}),

-        new Charset("Cp1097", null, new String[] {}),

-        new Charset("Cp1098", null, new String[] {}),

-        new Charset("Cp1112", null, new String[] {}),

-        new Charset("Cp1122", null, new String[] {}),

-        new Charset("Cp1123", null, new String[] {}),

-        new Charset("Cp1124", null, new String[] {}),

-        new Charset("Cp1381", null, new String[] {}),

-        new Charset("Cp1383", null, new String[] {}),

-        new Charset("Cp33722", null, new String[] {}),

-        new Charset("Big5_Solaris", null, new String[] {}),

-        new Charset("EUC_JP_LINUX", null, new String[] {}),

-        new Charset("EUC_JP_Solaris", null, new String[] {}),

-        new Charset("ISCII91", null, new String[] {"x-ISCII91", "iscii"}),

-        new Charset("ISO2022_CN_CNS", null, new String[] {}),

-        new Charset("ISO2022_CN_GB", null, new String[] {}),

-        new Charset("x-iso-8859-11", null, new String[] {}),

-        new Charset("JISAutoDetect", null, new String[] {}),

-        new Charset("MacArabic", null, new String[] {}),

-        new Charset("MacCentralEurope", null, new String[] {}),

-        new Charset("MacCroatian", null, new String[] {}),

-        new Charset("MacCyrillic", null, new String[] {}),

-        new Charset("MacDingbat", null, new String[] {}),

-        new Charset("MacGreek", "MacGreek", new String[] {}),

-        new Charset("MacHebrew", null, new String[] {}),

-        new Charset("MacIceland", null, new String[] {}),

-        new Charset("MacRoman", "MacRoman", new String[] {"Macintosh", "MAC", "csMacintosh"}),

-        new Charset("MacRomania", null, new String[] {}),

-        new Charset("MacSymbol", null, new String[] {}),

-        new Charset("MacThai", null, new String[] {}),

-        new Charset("MacTurkish", null, new String[] {}),

-        new Charset("MacUkraine", null, new String[] {}),

-        new Charset("UnicodeBig", null, new String[] {}),

-        new Charset("UnicodeLittle", null, new String[] {})

-    };

-

-    /**

-     * Contains the canonical names of character sets which can be used to

-     * decode bytes into Java chars.

-     */

-    private static TreeSet<String> decodingSupported = null;

-

-    /**

-     * Contains the canonical names of character sets which can be used to

-     * encode Java chars into bytes.

-     */

-    private static TreeSet<String> encodingSupported = null;

-

-    /**

-     * Maps character set names to Charset objects. All possible names of

-     * a charset will be mapped to the Charset.

-     */

-    private static HashMap<String, Charset> charsetMap = null;

-

-    static {

-        decodingSupported = new TreeSet<String>();

-        encodingSupported = new TreeSet<String>();

-        byte[] dummy = new byte[] {'d', 'u', 'm', 'm', 'y'};

-        for (int i = 0; i < JAVA_CHARSETS.length; i++) {

-            try {

-                String s = new String(dummy, JAVA_CHARSETS[i].canonical);

-                decodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));

-            } catch (UnsupportedOperationException e) {

-            } catch (UnsupportedEncodingException e) {

-            }

-            try {

-                "dummy".getBytes(JAVA_CHARSETS[i].canonical);

-                encodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));

-            } catch (UnsupportedOperationException e) {

-            } catch (UnsupportedEncodingException e) {

-            }

-        }

-

-        charsetMap = new HashMap<String, Charset>();

-        for (int i = 0; i < JAVA_CHARSETS.length; i++) {

-            Charset c = JAVA_CHARSETS[i];

-            charsetMap.put(c.canonical.toLowerCase(Locale.US), c);

-            if (c.mime != null) {

-                charsetMap.put(c.mime.toLowerCase(Locale.US), c);

-            }

-            if (c.aliases != null) {

-                for (int j = 0; j < c.aliases.length; j++) {

-                    charsetMap.put(c.aliases[j].toLowerCase(Locale.US), c);

-                }

-            }

-        }

-

-        if (log.isDebugEnabled()) {

-            log.debug("Character sets which support decoding: "

-                        + decodingSupported);

-            log.debug("Character sets which support encoding: "

-                        + encodingSupported);

-        }

-    }

-

-    /**

-     * ANDROID:  THE FOLLOWING SET OF STATIC STRINGS ARE COPIED FROM A NEWER VERSION OF MIME4J

-     */

-

-    /** carriage return - line feed sequence */

-    public static final String CRLF = "\r\n";

-

-    /** US-ASCII CR, carriage return (13) */

-    public static final int CR = '\r';

-

-    /** US-ASCII LF, line feed (10) */

-    public static final int LF = '\n';

-

-    /** US-ASCII SP, space (32) */

-    public static final int SP = ' ';

-

-    /** US-ASCII HT, horizontal-tab (9)*/

-    public static final int HT = '\t';

-

-    public static final java.nio.charset.Charset US_ASCII = java.nio.charset.Charset

-            .forName("US-ASCII");

-

-    public static final java.nio.charset.Charset ISO_8859_1 = java.nio.charset.Charset

-            .forName("ISO-8859-1");

-

-    public static final java.nio.charset.Charset UTF_8 = java.nio.charset.Charset

-            .forName("UTF-8");

-

-    /**

-     * Returns <code>true</code> if the specified character is a whitespace

-     * character (CR, LF, SP or HT).

-     *

-     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J

-     *

-     * @param ch

-     *            character to test.

-     * @return <code>true</code> if the specified character is a whitespace

-     *         character, <code>false</code> otherwise.

-     */

-    public static boolean isWhitespace(char ch) {

-        return ch == SP || ch == HT || ch == CR || ch == LF;

-    }

-

-    /**

-     * Returns <code>true</code> if the specified string consists entirely of

-     * whitespace characters.

-     *

-     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J

-     *

-     * @param s

-     *            string to test.

-     * @return <code>true</code> if the specified string consists entirely of

-     *         whitespace characters, <code>false</code> otherwise.

-     */

-    public static boolean isWhitespace(final String s) {

-        if (s == null) {

-            throw new IllegalArgumentException("String may not be null");

-        }

-        final int len = s.length();

-        for (int i = 0; i < len; i++) {

-            if (!isWhitespace(s.charAt(i))) {

-                return false;

-            }

-        }

-        return true;

-    }

-

-    /**

-     * Determines if the VM supports encoding (chars to bytes) the

-     * specified character set. NOTE: the given character set name may

-     * not be known to the VM even if this method returns <code>true</code>.

-     * Use {@link #toJavaCharset(String)} to get the canonical Java character

-     * set name.

-     *

-     * @param charsetName the characters set name.

-     * @return <code>true</code> if encoding is supported, <code>false</code>

-     *         otherwise.

-     */

-    public static boolean isEncodingSupported(String charsetName) {

-        return encodingSupported.contains(charsetName.toLowerCase(Locale.US));

-    }

-

-    /**

-     * Determines if the VM supports decoding (bytes to chars) the

-     * specified character set. NOTE: the given character set name may

-     * not be known to the VM even if this method returns <code>true</code>.

-     * Use {@link #toJavaCharset(String)} to get the canonical Java character

-     * set name.

-     *

-     * @param charsetName the characters set name.

-     * @return <code>true</code> if decoding is supported, <code>false</code>

-     *         otherwise.

-     */

-    public static boolean isDecodingSupported(String charsetName) {

-        return decodingSupported.contains(charsetName.toLowerCase(Locale.US));

-    }

-

-    /**

-     * Gets the preferred MIME character set name for the specified

-     * character set or <code>null</code> if not known.

-     *

-     * @param charsetName the character set name to look for.

-     * @return the MIME preferred name or <code>null</code> if not known.

-     */

-    public static String toMimeCharset(String charsetName) {

-        Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));

-        if (c != null) {

-            return c.mime;

-        }

-        return null;

-    }

-

-    /**

-     * Gets the canonical Java character set name for the specified

-     * character set or <code>null</code> if not known. This should be

-     * called before doing any conversions using the Java API. NOTE:

-     * you must use {@link #isEncodingSupported(String)} or

-     * {@link #isDecodingSupported(String)} to make sure the returned

-     * Java character set is supported by the current VM.

-     *

-     * @param charsetName the character set name to look for.

-     * @return the canonical Java name or <code>null</code> if not known.

-     */

-    public static String toJavaCharset(String charsetName) {

-        Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));

-        if (c != null) {

-            return c.canonical;

-        }

-        return null;

-    }

-

-    public static java.nio.charset.Charset getCharset(String charsetName) {

-        String defaultCharset = "ISO-8859-1";

-

-        // Use the default chareset if given charset is null

-        if(charsetName == null) charsetName = defaultCharset;

-

-        try {

-            return java.nio.charset.Charset.forName(charsetName);

-        } catch (IllegalCharsetNameException e) {

-            log.info("Illegal charset " + charsetName + ", fallback to " +

-                    defaultCharset + ": " + e);

-            // Use default charset on exception

-            return java.nio.charset.Charset.forName(defaultCharset);

-        } catch (UnsupportedCharsetException ex) {

-            log.info("Unsupported charset " + charsetName + ", fallback to " +

-                    defaultCharset + ": " + ex);

-            // Use default charset on exception

-            return java.nio.charset.Charset.forName(defaultCharset);

-        }

-

-    }

-    /*

-     * Uncomment the code below and run the main method to regenerate the

-     * Javadoc table above when the known charsets change.

-     */

-

-    /*

-    private static String dumpHtmlTable() {

-        LinkedList l = new LinkedList(Arrays.asList(JAVA_CHARSETS));

-        Collections.sort(l);

-        StringBuffer sb = new StringBuffer();

-        sb.append(" * <table>\n");

-        sb.append(" *     <tr>\n");

-        sb.append(" *         <td>Canonical (Java) name</td>\n");

-        sb.append(" *         <td>MIME preferred</td>\n");

-        sb.append(" *         <td>Aliases</td>\n");

-        sb.append(" *     </tr>\n");

-

-        for (Iterator it = l.iterator(); it.hasNext();) {

-            Charset c = (Charset) it.next();

-            sb.append(" *     <tr>\n");

-            sb.append(" *         <td>" + c.canonical + "</td>\n");

-            sb.append(" *         <td>" + (c.mime == null ? "?" : c.mime)+ "</td>\n");

-            sb.append(" *         <td>");

-            for (int i = 0; c.aliases != null && i < c.aliases.length; i++) {

-                sb.append(c.aliases[i] + " ");

-            }

-            sb.append("</td>\n");

-            sb.append(" *     </tr>\n");

-        }

-        sb.append(" * </table>\n");

-        return sb.toString();

-    }

-

-    public static void main(String[] args) {

-        System.out.println(dumpHtmlTable());

-    }*/

-}

+/****************************************************************
+ * 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.UnsupportedEncodingException;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.TreeSet;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * Utility class for working with character sets. It is somewhat similar to
+ * the Java 1.4 <code>java.nio.charset.Charset</code> class but knows many
+ * more aliases and is compatible with Java 1.3. It will use a simple detection
+ * mechanism to detect what character sets the current VM supports. This will
+ * be a sub-set of the character sets listed in the
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">
+ * Java 1.5 (J2SE5.0) Supported Encodings</a> document.
+ * <p>
+ * The <a href="http://www.iana.org/assignments/character-sets">
+ * IANA Character Sets</a> document has been used to determine the preferred
+ * MIME character set names and to get a list of known aliases.
+ * <p>
+ * This is a complete list of the character sets known to this class:
+ * <table>
+ *     <tr>
+ *         <td>Canonical (Java) name</td>
+ *         <td>MIME preferred</td>
+ *         <td>Aliases</td>
+ *     </tr>
+ *     <tr>
+ *         <td>ASCII</td>
+ *         <td>US-ASCII</td>
+ *         <td>ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ISO646-US us IBM367 cp367 csASCII ascii7 646 iso_646.irv:1983 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Big5</td>
+ *         <td>Big5</td>
+ *         <td>csBig5 CN-Big5 BIG-FIVE BIGFIVE </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Big5_HKSCS</td>
+ *         <td>Big5-HKSCS</td>
+ *         <td>big5hkscs </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Big5_Solaris</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp037</td>
+ *         <td>IBM037</td>
+ *         <td>ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1006</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1025</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1026</td>
+ *         <td>IBM1026</td>
+ *         <td>csIBM1026 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1046</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1047</td>
+ *         <td>IBM1047</td>
+ *         <td>IBM-1047 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1097</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1098</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1112</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1122</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1123</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1124</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1140</td>
+ *         <td>IBM01140</td>
+ *         <td>CCSID01140 CP01140 ebcdic-us-37+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1141</td>
+ *         <td>IBM01141</td>
+ *         <td>CCSID01141 CP01141 ebcdic-de-273+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1142</td>
+ *         <td>IBM01142</td>
+ *         <td>CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1143</td>
+ *         <td>IBM01143</td>
+ *         <td>CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1144</td>
+ *         <td>IBM01144</td>
+ *         <td>CCSID01144 CP01144 ebcdic-it-280+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1145</td>
+ *         <td>IBM01145</td>
+ *         <td>CCSID01145 CP01145 ebcdic-es-284+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1146</td>
+ *         <td>IBM01146</td>
+ *         <td>CCSID01146 CP01146 ebcdic-gb-285+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1147</td>
+ *         <td>IBM01147</td>
+ *         <td>CCSID01147 CP01147 ebcdic-fr-297+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1148</td>
+ *         <td>IBM01148</td>
+ *         <td>CCSID01148 CP01148 ebcdic-international-500+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1149</td>
+ *         <td>IBM01149</td>
+ *         <td>CCSID01149 CP01149 ebcdic-is-871+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1250</td>
+ *         <td>windows-1250</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1251</td>
+ *         <td>windows-1251</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1252</td>
+ *         <td>windows-1252</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1253</td>
+ *         <td>windows-1253</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1254</td>
+ *         <td>windows-1254</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1255</td>
+ *         <td>windows-1255</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1256</td>
+ *         <td>windows-1256</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1257</td>
+ *         <td>windows-1257</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1258</td>
+ *         <td>windows-1258</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1381</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp1383</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp273</td>
+ *         <td>IBM273</td>
+ *         <td>csIBM273 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp277</td>
+ *         <td>IBM277</td>
+ *         <td>EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp278</td>
+ *         <td>IBM278</td>
+ *         <td>CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp280</td>
+ *         <td>IBM280</td>
+ *         <td>ebcdic-cp-it csIBM280 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp284</td>
+ *         <td>IBM284</td>
+ *         <td>ebcdic-cp-es csIBM284 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp285</td>
+ *         <td>IBM285</td>
+ *         <td>ebcdic-cp-gb csIBM285 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp297</td>
+ *         <td>IBM297</td>
+ *         <td>ebcdic-cp-fr csIBM297 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp33722</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp420</td>
+ *         <td>IBM420</td>
+ *         <td>ebcdic-cp-ar1 csIBM420 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp424</td>
+ *         <td>IBM424</td>
+ *         <td>ebcdic-cp-he csIBM424 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp437</td>
+ *         <td>IBM437</td>
+ *         <td>437 csPC8CodePage437 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp500</td>
+ *         <td>IBM500</td>
+ *         <td>ebcdic-cp-be ebcdic-cp-ch csIBM500 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp737</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp775</td>
+ *         <td>IBM775</td>
+ *         <td>csPC775Baltic </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp838</td>
+ *         <td>IBM-Thai</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp850</td>
+ *         <td>IBM850</td>
+ *         <td>850 csPC850Multilingual </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp852</td>
+ *         <td>IBM852</td>
+ *         <td>852 csPCp852 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp855</td>
+ *         <td>IBM855</td>
+ *         <td>855 csIBM855 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp856</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp857</td>
+ *         <td>IBM857</td>
+ *         <td>857 csIBM857 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp858</td>
+ *         <td>IBM00858</td>
+ *         <td>CCSID00858 CP00858 PC-Multilingual-850+euro </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp860</td>
+ *         <td>IBM860</td>
+ *         <td>860 csIBM860 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp861</td>
+ *         <td>IBM861</td>
+ *         <td>861 cp-is csIBM861 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp862</td>
+ *         <td>IBM862</td>
+ *         <td>862 csPC862LatinHebrew </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp863</td>
+ *         <td>IBM863</td>
+ *         <td>863 csIBM863 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp864</td>
+ *         <td>IBM864</td>
+ *         <td>cp864 csIBM864 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp865</td>
+ *         <td>IBM865</td>
+ *         <td>865 csIBM865 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp866</td>
+ *         <td>IBM866</td>
+ *         <td>866 csIBM866 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp868</td>
+ *         <td>IBM868</td>
+ *         <td>cp-ar csIBM868 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp869</td>
+ *         <td>IBM869</td>
+ *         <td>cp-gr csIBM869 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp870</td>
+ *         <td>IBM870</td>
+ *         <td>ebcdic-cp-roece ebcdic-cp-yu csIBM870 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp871</td>
+ *         <td>IBM871</td>
+ *         <td>ebcdic-cp-is csIBM871 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp875</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp918</td>
+ *         <td>IBM918</td>
+ *         <td>ebcdic-cp-ar2 csIBM918 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp921</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp922</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp930</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp933</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp935</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp937</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp939</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp942</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp942C</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp943</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp943C</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp948</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp949</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp949C</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp950</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp964</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>Cp970</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_CN</td>
+ *         <td>GB2312</td>
+ *         <td>x-EUC-CN csGB2312 euccn euc-cn gb2312-80 gb2312-1980 CN-GB CN-GB-ISOIR165 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_JP</td>
+ *         <td>EUC-JP</td>
+ *         <td>csEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese eucjis x-eucjp eucjp x-euc-jp </td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_JP_LINUX</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_JP_Solaris</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_KR</td>
+ *         <td>EUC-KR</td>
+ *         <td>csEUCKR ksc5601 5601 ksc5601_1987 ksc_5601 ksc5601-1987 ks_c_5601-1987 euckr </td>
+ *     </tr>
+ *     <tr>
+ *         <td>EUC_TW</td>
+ *         <td>EUC-TW</td>
+ *         <td>x-EUC-TW cns11643 euctw </td>
+ *     </tr>
+ *     <tr>
+ *         <td>GB18030</td>
+ *         <td>GB18030</td>
+ *         <td>gb18030-2000 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>GBK</td>
+ *         <td>windows-936</td>
+ *         <td>CP936 MS936 ms_936 x-mswin-936 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISCII91</td>
+ *         <td>?</td>
+ *         <td>x-ISCII91 iscii </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO2022CN</td>
+ *         <td>ISO-2022-CN</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO2022JP</td>
+ *         <td>ISO-2022-JP</td>
+ *         <td>csISO2022JP JIS jis_encoding csjisencoding </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO2022KR</td>
+ *         <td>ISO-2022-KR</td>
+ *         <td>csISO2022KR </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO2022_CN_CNS</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO2022_CN_GB</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_1</td>
+ *         <td>ISO-8859-1</td>
+ *         <td>ISO_8859-1:1987 iso-ir-100 ISO_8859-1 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 819 IBM-819 ISO8859-1 ISO_8859_1 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_13</td>
+ *         <td>ISO-8859-13</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_15</td>
+ *         <td>ISO-8859-15</td>
+ *         <td>ISO_8859-15 Latin-9 8859_15 csISOlatin9 IBM923 cp923 923 L9 IBM-923 ISO8859-15 LATIN9 LATIN0 csISOlatin0 ISO8859_15_FDIS </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_2</td>
+ *         <td>ISO-8859-2</td>
+ *         <td>ISO_8859-2:1987 iso-ir-101 ISO_8859-2 latin2 l2 csISOLatin2 8859_2 iso8859_2 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_3</td>
+ *         <td>ISO-8859-3</td>
+ *         <td>ISO_8859-3:1988 iso-ir-109 ISO_8859-3 latin3 l3 csISOLatin3 8859_3 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_4</td>
+ *         <td>ISO-8859-4</td>
+ *         <td>ISO_8859-4:1988 iso-ir-110 ISO_8859-4 latin4 l4 csISOLatin4 8859_4 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_5</td>
+ *         <td>ISO-8859-5</td>
+ *         <td>ISO_8859-5:1988 iso-ir-144 ISO_8859-5 cyrillic csISOLatinCyrillic 8859_5 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_6</td>
+ *         <td>ISO-8859-6</td>
+ *         <td>ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ECMA-114 ASMO-708 arabic csISOLatinArabic 8859_6 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_7</td>
+ *         <td>ISO-8859-7</td>
+ *         <td>ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 sun_eu_greek </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_8</td>
+ *         <td>ISO-8859-8</td>
+ *         <td>ISO_8859-8:1988 iso-ir-138 ISO_8859-8 hebrew csISOLatinHebrew 8859_8 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>ISO8859_9</td>
+ *         <td>ISO-8859-9</td>
+ *         <td>ISO_8859-9:1989 iso-ir-148 ISO_8859-9 latin5 l5 csISOLatin5 8859_9 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>JISAutoDetect</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>JIS_C6626-1983</td>
+ *         <td>JIS_C6626-1983</td>
+ *         <td>x-JIS0208 JIS0208 csISO87JISX0208 x0208 JIS_X0208-1983 iso-ir-87 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>JIS_X0201</td>
+ *         <td>JIS_X0201</td>
+ *         <td>X0201 JIS0201 csHalfWidthKatakana </td>
+ *     </tr>
+ *     <tr>
+ *         <td>JIS_X0212-1990</td>
+ *         <td>JIS_X0212-1990</td>
+ *         <td>iso-ir-159 x0212 JIS0212 csISO159JISX02121990 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>KOI8_R</td>
+ *         <td>KOI8-R</td>
+ *         <td>csKOI8R koi8 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MS874</td>
+ *         <td>windows-874</td>
+ *         <td>cp874 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MS932</td>
+ *         <td>Windows-31J</td>
+ *         <td>windows-932 csWindows31J x-ms-cp932 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MS949</td>
+ *         <td>windows-949</td>
+ *         <td>windows949 ms_949 x-windows-949 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MS950</td>
+ *         <td>windows-950</td>
+ *         <td>x-windows-950 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MS950_HKSCS</td>
+ *         <td></td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacArabic</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacCentralEurope</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacCroatian</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacCyrillic</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacDingbat</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacGreek</td>
+ *         <td>MacGreek</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacHebrew</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacIceland</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacRoman</td>
+ *         <td>MacRoman</td>
+ *         <td>Macintosh MAC csMacintosh </td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacRomania</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacSymbol</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacThai</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacTurkish</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>MacUkraine</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>SJIS</td>
+ *         <td>Shift_JIS</td>
+ *         <td>MS_Kanji csShiftJIS shift-jis x-sjis pck </td>
+ *     </tr>
+ *     <tr>
+ *         <td>TIS620</td>
+ *         <td>TIS-620</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>UTF-16</td>
+ *         <td>UTF-16</td>
+ *         <td>UTF_16 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>UTF8</td>
+ *         <td>UTF-8</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>UnicodeBig</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>UnicodeBigUnmarked</td>
+ *         <td>UTF-16BE</td>
+ *         <td>X-UTF-16BE UTF_16BE ISO-10646-UCS-2 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>UnicodeLittle</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ *     <tr>
+ *         <td>UnicodeLittleUnmarked</td>
+ *         <td>UTF-16LE</td>
+ *         <td>UTF_16LE X-UTF-16LE </td>
+ *     </tr>
+ *     <tr>
+ *         <td>x-Johab</td>
+ *         <td>johab</td>
+ *         <td>johab cp1361 ms1361 ksc5601-1992 ksc5601_1992 </td>
+ *     </tr>
+ *     <tr>
+ *         <td>x-iso-8859-11</td>
+ *         <td>?</td>
+ *         <td></td>
+ *     </tr>
+ * </table>
+ *
+ *
+ * @version $Id: CharsetUtil.java,v 1.1 2004/10/25 07:26:46 ntherning Exp $
+ */
+public class CharsetUtil {
+    private static Log log = LogFactory.getLog(CharsetUtil.class);
+
+    private static class Charset implements Comparable<Charset> {
+        private String canonical = null;
+        private String mime = null;
+        private String[] aliases = null;
+
+        private Charset(String canonical, String mime, String[] aliases) {
+            this.canonical = canonical;
+            this.mime = mime;
+            this.aliases = aliases;
+        }
+
+        public int compareTo(Charset c) {
+            return this.canonical.compareTo(c.canonical);
+        }
+    }
+
+    private static Charset[] JAVA_CHARSETS = {
+        new Charset("ISO8859_1", "ISO-8859-1",
+                    new String[] {"ISO_8859-1:1987", "iso-ir-100", "ISO_8859-1",
+                                  "latin1", "l1", "IBM819", "CP819",
+                                  "csISOLatin1", "8859_1", "819", "IBM-819",
+                                  "ISO8859-1", "ISO_8859_1"}),
+        new Charset("ISO8859_2", "ISO-8859-2",
+                    new String[] {"ISO_8859-2:1987", "iso-ir-101", "ISO_8859-2",
+                                  "latin2", "l2", "csISOLatin2", "8859_2",
+                                  "iso8859_2"}),
+        new Charset("ISO8859_3", "ISO-8859-3", new String[] {"ISO_8859-3:1988", "iso-ir-109", "ISO_8859-3", "latin3", "l3", "csISOLatin3", "8859_3"}),
+        new Charset("ISO8859_4", "ISO-8859-4",
+                    new String[] {"ISO_8859-4:1988", "iso-ir-110", "ISO_8859-4",
+                                  "latin4", "l4", "csISOLatin4", "8859_4"}),
+        new Charset("ISO8859_5", "ISO-8859-5",
+                    new String[] {"ISO_8859-5:1988", "iso-ir-144", "ISO_8859-5",
+                                  "cyrillic", "csISOLatinCyrillic", "8859_5"}),
+        new Charset("ISO8859_6", "ISO-8859-6", new String[] {"ISO_8859-6:1987", "iso-ir-127", "ISO_8859-6", "ECMA-114", "ASMO-708", "arabic", "csISOLatinArabic", "8859_6"}),
+        new Charset("ISO8859_7", "ISO-8859-7",
+                    new String[] {"ISO_8859-7:1987", "iso-ir-126", "ISO_8859-7",
+                                  "ELOT_928", "ECMA-118", "greek", "greek8",
+                                  "csISOLatinGreek", "8859_7", "sun_eu_greek"}),
+        new Charset("ISO8859_8", "ISO-8859-8", new String[] {"ISO_8859-8:1988", "iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew", "8859_8"}),
+        new Charset("ISO8859_9", "ISO-8859-9",
+                    new String[] {"ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9",
+                                  "latin5", "l5", "csISOLatin5", "8859_9"}),
+
+        new Charset("ISO8859_13", "ISO-8859-13", new String[] {}),
+        new Charset("ISO8859_15", "ISO-8859-15",
+                    new String[] {"ISO_8859-15", "Latin-9", "8859_15",
+                                  "csISOlatin9", "IBM923", "cp923", "923", "L9",
+                                  "IBM-923", "ISO8859-15", "LATIN9", "LATIN0",
+                                  "csISOlatin0", "ISO8859_15_FDIS"}),
+        new Charset("KOI8_R", "KOI8-R", new String[] {"csKOI8R", "koi8"}),
+        new Charset("ASCII", "US-ASCII",
+                    new String[] {"ANSI_X3.4-1968", "iso-ir-6",
+                                  "ANSI_X3.4-1986", "ISO_646.irv:1991",
+                                  "ISO646-US", "us", "IBM367", "cp367",
+                                  "csASCII", "ascii7", "646", "iso_646.irv:1983"}),
+        new Charset("UTF8", "UTF-8", new String[] {}),
+        new Charset("UTF-16", "UTF-16", new String[] {"UTF_16"}),
+        new Charset("UnicodeBigUnmarked", "UTF-16BE", new String[] {"X-UTF-16BE", "UTF_16BE", "ISO-10646-UCS-2"}),
+        new Charset("UnicodeLittleUnmarked", "UTF-16LE", new String[] {"UTF_16LE", "X-UTF-16LE"}),
+        new Charset("Big5", "Big5", new String[] {"csBig5", "CN-Big5", "BIG-FIVE", "BIGFIVE"}),
+        new Charset("Big5_HKSCS", "Big5-HKSCS", new String[] {"big5hkscs"}),
+        new Charset("EUC_JP", "EUC-JP",
+                    new String[] {"csEUCPkdFmtJapanese",
+                              "Extended_UNIX_Code_Packed_Format_for_Japanese",
+                              "eucjis", "x-eucjp", "eucjp", "x-euc-jp"}),
+        new Charset("EUC_KR", "EUC-KR",
+                    new String[] {"csEUCKR", "ksc5601", "5601", "ksc5601_1987",
+                                  "ksc_5601", "ksc5601-1987", "ks_c_5601-1987",
+                                  "euckr"}),
+        new Charset("GB18030", "GB18030", new String[] {"gb18030-2000"}),
+        new Charset("EUC_CN", "GB2312", new String[] {"x-EUC-CN", "csGB2312", "euccn", "euc-cn", "gb2312-80", "gb2312-1980", "CN-GB", "CN-GB-ISOIR165"}),
+        new Charset("GBK", "windows-936", new String[] {"CP936", "MS936", "ms_936", "x-mswin-936"}),
+
+        new Charset("Cp037", "IBM037", new String[] {"ebcdic-cp-us", "ebcdic-cp-ca", "ebcdic-cp-wt", "ebcdic-cp-nl", "csIBM037"}),
+        new Charset("Cp273", "IBM273", new String[] {"csIBM273"}),
+        new Charset("Cp277", "IBM277", new String[] {"EBCDIC-CP-DK", "EBCDIC-CP-NO", "csIBM277"}),
+        new Charset("Cp278", "IBM278", new String[] {"CP278", "ebcdic-cp-fi", "ebcdic-cp-se", "csIBM278"}),
+        new Charset("Cp280", "IBM280", new String[] {"ebcdic-cp-it", "csIBM280"}),
+        new Charset("Cp284", "IBM284", new String[] {"ebcdic-cp-es", "csIBM284"}),
+        new Charset("Cp285", "IBM285", new String[] {"ebcdic-cp-gb", "csIBM285"}),
+        new Charset("Cp297", "IBM297", new String[] {"ebcdic-cp-fr", "csIBM297"}),
+        new Charset("Cp420", "IBM420", new String[] {"ebcdic-cp-ar1", "csIBM420"}),
+        new Charset("Cp424", "IBM424", new String[] {"ebcdic-cp-he", "csIBM424"}),
+        new Charset("Cp437", "IBM437", new String[] {"437", "csPC8CodePage437"}),
+        new Charset("Cp500", "IBM500", new String[] {"ebcdic-cp-be", "ebcdic-cp-ch", "csIBM500"}),
+        new Charset("Cp775", "IBM775", new String[] {"csPC775Baltic"}),
+        new Charset("Cp838", "IBM-Thai", new String[] {}),
+        new Charset("Cp850", "IBM850", new String[] {"850", "csPC850Multilingual"}),
+        new Charset("Cp852", "IBM852", new String[] {"852", "csPCp852"}),
+        new Charset("Cp855", "IBM855", new String[] {"855", "csIBM855"}),
+        new Charset("Cp857", "IBM857", new String[] {"857", "csIBM857"}),
+        new Charset("Cp858", "IBM00858",
+                new String[] {"CCSID00858", "CP00858",
+                              "PC-Multilingual-850+euro"}),
+        new Charset("Cp860", "IBM860", new String[] {"860", "csIBM860"}),
+        new Charset("Cp861", "IBM861", new String[] {"861", "cp-is", "csIBM861"}),
+        new Charset("Cp862", "IBM862", new String[] {"862", "csPC862LatinHebrew"}),
+        new Charset("Cp863", "IBM863", new String[] {"863", "csIBM863"}),
+        new Charset("Cp864", "IBM864", new String[] {"cp864", "csIBM864"}),
+        new Charset("Cp865", "IBM865", new String[] {"865", "csIBM865"}),
+        new Charset("Cp866", "IBM866", new String[] {"866", "csIBM866"}),
+        new Charset("Cp868", "IBM868", new String[] {"cp-ar", "csIBM868"}),
+        new Charset("Cp869", "IBM869", new String[] {"cp-gr", "csIBM869"}),
+        new Charset("Cp870", "IBM870", new String[] {"ebcdic-cp-roece", "ebcdic-cp-yu", "csIBM870"}),
+        new Charset("Cp871", "IBM871", new String[] {"ebcdic-cp-is", "csIBM871"}),
+        new Charset("Cp918", "IBM918", new String[] {"ebcdic-cp-ar2", "csIBM918"}),
+        new Charset("Cp1026", "IBM1026", new String[] {"csIBM1026"}),
+        new Charset("Cp1047", "IBM1047", new String[] {"IBM-1047"}),
+        new Charset("Cp1140", "IBM01140",
+                    new String[] {"CCSID01140", "CP01140",
+                                  "ebcdic-us-37+euro"}),
+        new Charset("Cp1141", "IBM01141",
+                    new String[] {"CCSID01141", "CP01141",
+                                  "ebcdic-de-273+euro"}),
+        new Charset("Cp1142", "IBM01142", new String[] {"CCSID01142", "CP01142", "ebcdic-dk-277+euro", "ebcdic-no-277+euro"}),
+        new Charset("Cp1143", "IBM01143", new String[] {"CCSID01143", "CP01143", "ebcdic-fi-278+euro", "ebcdic-se-278+euro"}),
+        new Charset("Cp1144", "IBM01144", new String[] {"CCSID01144", "CP01144", "ebcdic-it-280+euro"}),
+        new Charset("Cp1145", "IBM01145", new String[] {"CCSID01145", "CP01145", "ebcdic-es-284+euro"}),
+        new Charset("Cp1146", "IBM01146", new String[] {"CCSID01146", "CP01146", "ebcdic-gb-285+euro"}),
+        new Charset("Cp1147", "IBM01147", new String[] {"CCSID01147", "CP01147", "ebcdic-fr-297+euro"}),
+        new Charset("Cp1148", "IBM01148", new String[] {"CCSID01148", "CP01148", "ebcdic-international-500+euro"}),
+        new Charset("Cp1149", "IBM01149", new String[] {"CCSID01149", "CP01149", "ebcdic-is-871+euro"}),
+        new Charset("Cp1250", "windows-1250", new String[] {}),
+        new Charset("Cp1251", "windows-1251", new String[] {}),
+        new Charset("Cp1252", "windows-1252", new String[] {}),
+        new Charset("Cp1253", "windows-1253", new String[] {}),
+        new Charset("Cp1254", "windows-1254", new String[] {}),
+        new Charset("Cp1255", "windows-1255", new String[] {}),
+        new Charset("Cp1256", "windows-1256", new String[] {}),
+        new Charset("Cp1257", "windows-1257", new String[] {}),
+        new Charset("Cp1258", "windows-1258", new String[] {}),
+        new Charset("ISO2022CN", "ISO-2022-CN", new String[] {}),
+        new Charset("ISO2022JP", "ISO-2022-JP", new String[] {"csISO2022JP", "JIS", "jis_encoding", "csjisencoding"}),
+        new Charset("ISO2022KR", "ISO-2022-KR", new String[] {"csISO2022KR"}),
+        new Charset("JIS_X0201", "JIS_X0201", new String[] {"X0201", "JIS0201", "csHalfWidthKatakana"}),
+        new Charset("JIS_X0212-1990", "JIS_X0212-1990", new String[] {"iso-ir-159", "x0212", "JIS0212", "csISO159JISX02121990"}),
+        new Charset("JIS_C6626-1983", "JIS_C6626-1983", new String[] {"x-JIS0208", "JIS0208", "csISO87JISX0208", "x0208", "JIS_X0208-1983", "iso-ir-87"}),
+        new Charset("SJIS", "Shift_JIS", new String[] {"MS_Kanji", "csShiftJIS", "shift-jis", "x-sjis", "pck"}),
+        new Charset("TIS620", "TIS-620", new String[] {}),
+        new Charset("MS932", "Windows-31J", new String[] {"windows-932", "csWindows31J", "x-ms-cp932"}),
+        new Charset("EUC_TW", "EUC-TW", new String[] {"x-EUC-TW", "cns11643", "euctw"}),
+        new Charset("x-Johab", "johab", new String[] {"johab", "cp1361", "ms1361", "ksc5601-1992", "ksc5601_1992"}),
+        new Charset("MS950_HKSCS", "", new String[] {}),
+        new Charset("MS874", "windows-874", new String[] {"cp874"}),
+        new Charset("MS949", "windows-949", new String[] {"windows949", "ms_949", "x-windows-949"}),
+        new Charset("MS950", "windows-950", new String[] {"x-windows-950"}),
+
+        new Charset("Cp737", null, new String[] {}),
+        new Charset("Cp856", null, new String[] {}),
+        new Charset("Cp875", null, new String[] {}),
+        new Charset("Cp921", null, new String[] {}),
+        new Charset("Cp922", null, new String[] {}),
+        new Charset("Cp930", null, new String[] {}),
+        new Charset("Cp933", null, new String[] {}),
+        new Charset("Cp935", null, new String[] {}),
+        new Charset("Cp937", null, new String[] {}),
+        new Charset("Cp939", null, new String[] {}),
+        new Charset("Cp942", null, new String[] {}),
+        new Charset("Cp942C", null, new String[] {}),
+        new Charset("Cp943", null, new String[] {}),
+        new Charset("Cp943C", null, new String[] {}),
+        new Charset("Cp948", null, new String[] {}),
+        new Charset("Cp949", null, new String[] {}),
+        new Charset("Cp949C", null, new String[] {}),
+        new Charset("Cp950", null, new String[] {}),
+        new Charset("Cp964", null, new String[] {}),
+        new Charset("Cp970", null, new String[] {}),
+        new Charset("Cp1006", null, new String[] {}),
+        new Charset("Cp1025", null, new String[] {}),
+        new Charset("Cp1046", null, new String[] {}),
+        new Charset("Cp1097", null, new String[] {}),
+        new Charset("Cp1098", null, new String[] {}),
+        new Charset("Cp1112", null, new String[] {}),
+        new Charset("Cp1122", null, new String[] {}),
+        new Charset("Cp1123", null, new String[] {}),
+        new Charset("Cp1124", null, new String[] {}),
+        new Charset("Cp1381", null, new String[] {}),
+        new Charset("Cp1383", null, new String[] {}),
+        new Charset("Cp33722", null, new String[] {}),
+        new Charset("Big5_Solaris", null, new String[] {}),
+        new Charset("EUC_JP_LINUX", null, new String[] {}),
+        new Charset("EUC_JP_Solaris", null, new String[] {}),
+        new Charset("ISCII91", null, new String[] {"x-ISCII91", "iscii"}),
+        new Charset("ISO2022_CN_CNS", null, new String[] {}),
+        new Charset("ISO2022_CN_GB", null, new String[] {}),
+        new Charset("x-iso-8859-11", null, new String[] {}),
+        new Charset("JISAutoDetect", null, new String[] {}),
+        new Charset("MacArabic", null, new String[] {}),
+        new Charset("MacCentralEurope", null, new String[] {}),
+        new Charset("MacCroatian", null, new String[] {}),
+        new Charset("MacCyrillic", null, new String[] {}),
+        new Charset("MacDingbat", null, new String[] {}),
+        new Charset("MacGreek", "MacGreek", new String[] {}),
+        new Charset("MacHebrew", null, new String[] {}),
+        new Charset("MacIceland", null, new String[] {}),
+        new Charset("MacRoman", "MacRoman", new String[] {"Macintosh", "MAC", "csMacintosh"}),
+        new Charset("MacRomania", null, new String[] {}),
+        new Charset("MacSymbol", null, new String[] {}),
+        new Charset("MacThai", null, new String[] {}),
+        new Charset("MacTurkish", null, new String[] {}),
+        new Charset("MacUkraine", null, new String[] {}),
+        new Charset("UnicodeBig", null, new String[] {}),
+        new Charset("UnicodeLittle", null, new String[] {})
+    };
+
+    /**
+     * Contains the canonical names of character sets which can be used to
+     * decode bytes into Java chars.
+     */
+    private static TreeSet<String> decodingSupported = null;
+
+    /**
+     * Contains the canonical names of character sets which can be used to
+     * encode Java chars into bytes.
+     */
+    private static TreeSet<String> encodingSupported = null;
+
+    /**
+     * Maps character set names to Charset objects. All possible names of
+     * a charset will be mapped to the Charset.
+     */
+    private static HashMap<String, Charset> charsetMap = null;
+
+    static {
+        decodingSupported = new TreeSet<String>();
+        encodingSupported = new TreeSet<String>();
+        byte[] dummy = new byte[] {'d', 'u', 'm', 'm', 'y'};
+        for (int i = 0; i < JAVA_CHARSETS.length; i++) {
+            try {
+                String s = new String(dummy, JAVA_CHARSETS[i].canonical);
+                decodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));
+            } catch (UnsupportedOperationException e) {
+            } catch (UnsupportedEncodingException e) {
+            }
+            try {
+                "dummy".getBytes(JAVA_CHARSETS[i].canonical);
+                encodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));
+            } catch (UnsupportedOperationException e) {
+            } catch (UnsupportedEncodingException e) {
+            }
+        }
+
+        charsetMap = new HashMap<String, Charset>();
+        for (int i = 0; i < JAVA_CHARSETS.length; i++) {
+            Charset c = JAVA_CHARSETS[i];
+            charsetMap.put(c.canonical.toLowerCase(Locale.US), c);
+            if (c.mime != null) {
+                charsetMap.put(c.mime.toLowerCase(Locale.US), c);
+            }
+            if (c.aliases != null) {
+                for (int j = 0; j < c.aliases.length; j++) {
+                    charsetMap.put(c.aliases[j].toLowerCase(Locale.US), c);
+                }
+            }
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Character sets which support decoding: "
+                        + decodingSupported);
+            log.debug("Character sets which support encoding: "
+                        + encodingSupported);
+        }
+    }
+
+    /**
+     * ANDROID:  THE FOLLOWING SET OF STATIC STRINGS ARE COPIED FROM A NEWER VERSION OF MIME4J
+     */
+
+    /** carriage return - line feed sequence */
+    public static final String CRLF = "\r\n";
+
+    /** US-ASCII CR, carriage return (13) */
+    public static final int CR = '\r';
+
+    /** US-ASCII LF, line feed (10) */
+    public static final int LF = '\n';
+
+    /** US-ASCII SP, space (32) */
+    public static final int SP = ' ';
+
+    /** US-ASCII HT, horizontal-tab (9)*/
+    public static final int HT = '\t';
+
+    public static final java.nio.charset.Charset US_ASCII = java.nio.charset.Charset
+            .forName("US-ASCII");
+
+    public static final java.nio.charset.Charset ISO_8859_1 = java.nio.charset.Charset
+            .forName("ISO-8859-1");
+
+    public static final java.nio.charset.Charset UTF_8 = java.nio.charset.Charset
+            .forName("UTF-8");
+
+    /**
+     * Returns <code>true</code> if the specified character is a whitespace
+     * character (CR, LF, SP or HT).
+     *
+     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J
+     *
+     * @param ch
+     *            character to test.
+     * @return <code>true</code> if the specified character is a whitespace
+     *         character, <code>false</code> otherwise.
+     */
+    public static boolean isWhitespace(char ch) {
+        return ch == SP || ch == HT || ch == CR || ch == LF;
+    }
+
+    /**
+     * Returns <code>true</code> if the specified string consists entirely of
+     * whitespace characters.
+     *
+     * ANDROID:  COPIED FROM A NEWER VERSION OF MIME4J
+     *
+     * @param s
+     *            string to test.
+     * @return <code>true</code> if the specified string consists entirely of
+     *         whitespace characters, <code>false</code> otherwise.
+     */
+    public static boolean isWhitespace(final String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String may not be null");
+        }
+        final int len = s.length();
+        for (int i = 0; i < len; i++) {
+            if (!isWhitespace(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Determines if the VM supports encoding (chars to bytes) the
+     * specified character set. NOTE: the given character set name may
+     * not be known to the VM even if this method returns <code>true</code>.
+     * Use {@link #toJavaCharset(String)} to get the canonical Java character
+     * set name.
+     *
+     * @param charsetName the characters set name.
+     * @return <code>true</code> if encoding is supported, <code>false</code>
+     *         otherwise.
+     */
+    public static boolean isEncodingSupported(String charsetName) {
+        return encodingSupported.contains(charsetName.toLowerCase(Locale.US));
+    }
+
+    /**
+     * Determines if the VM supports decoding (bytes to chars) the
+     * specified character set. NOTE: the given character set name may
+     * not be known to the VM even if this method returns <code>true</code>.
+     * Use {@link #toJavaCharset(String)} to get the canonical Java character
+     * set name.
+     *
+     * @param charsetName the characters set name.
+     * @return <code>true</code> if decoding is supported, <code>false</code>
+     *         otherwise.
+     */
+    public static boolean isDecodingSupported(String charsetName) {
+        return decodingSupported.contains(charsetName.toLowerCase(Locale.US));
+    }
+
+    /**
+     * Gets the preferred MIME character set name for the specified
+     * character set or <code>null</code> if not known.
+     *
+     * @param charsetName the character set name to look for.
+     * @return the MIME preferred name or <code>null</code> if not known.
+     */
+    public static String toMimeCharset(String charsetName) {
+        Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));
+        if (c != null) {
+            return c.mime;
+        }
+        return null;
+    }
+
+    /**
+     * Gets the canonical Java character set name for the specified
+     * character set or <code>null</code> if not known. This should be
+     * called before doing any conversions using the Java API. NOTE:
+     * you must use {@link #isEncodingSupported(String)} or
+     * {@link #isDecodingSupported(String)} to make sure the returned
+     * Java character set is supported by the current VM.
+     *
+     * @param charsetName the character set name to look for.
+     * @return the canonical Java name or <code>null</code> if not known.
+     */
+    public static String toJavaCharset(String charsetName) {
+        Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));
+        if (c != null) {
+            return c.canonical;
+        }
+        return null;
+    }
+
+    public static java.nio.charset.Charset getCharset(String charsetName) {
+        String defaultCharset = "ISO-8859-1";
+
+        // Use the default chareset if given charset is null
+        if(charsetName == null) charsetName = defaultCharset;
+
+        try {
+            return java.nio.charset.Charset.forName(charsetName);
+        } catch (IllegalCharsetNameException e) {
+            log.info("Illegal charset " + charsetName + ", fallback to " +
+                    defaultCharset + ": " + e);
+            // Use default charset on exception
+            return java.nio.charset.Charset.forName(defaultCharset);
+        } catch (UnsupportedCharsetException ex) {
+            log.info("Unsupported charset " + charsetName + ", fallback to " +
+                    defaultCharset + ": " + ex);
+            // Use default charset on exception
+            return java.nio.charset.Charset.forName(defaultCharset);
+        }
+
+    }
+    /*
+     * Uncomment the code below and run the main method to regenerate the
+     * Javadoc table above when the known charsets change.
+     */
+
+    /*
+    private static String dumpHtmlTable() {
+        LinkedList l = new LinkedList(Arrays.asList(JAVA_CHARSETS));
+        Collections.sort(l);
+        StringBuffer sb = new StringBuffer();
+        sb.append(" * <table>\n");
+        sb.append(" *     <tr>\n");
+        sb.append(" *         <td>Canonical (Java) name</td>\n");
+        sb.append(" *         <td>MIME preferred</td>\n");
+        sb.append(" *         <td>Aliases</td>\n");
+        sb.append(" *     </tr>\n");
+
+        for (Iterator it = l.iterator(); it.hasNext();) {
+            Charset c = (Charset) it.next();
+            sb.append(" *     <tr>\n");
+            sb.append(" *         <td>" + c.canonical + "</td>\n");
+            sb.append(" *         <td>" + (c.mime == null ? "?" : c.mime)+ "</td>\n");
+            sb.append(" *         <td>");
+            for (int i = 0; c.aliases != null && i < c.aliases.length; i++) {
+                sb.append(c.aliases[i] + " ");
+            }
+            sb.append("</td>\n");
+            sb.append(" *     </tr>\n");
+        }
+        sb.append(" * </table>\n");
+        return sb.toString();
+    }
+
+    public static void main(String[] args) {
+        System.out.println(dumpHtmlTable());
+    }*/
+}
diff --git a/src/org/apache/james/mime4j/util/PartialInputStream.java b/src/org/apache/james/mime4j/util/PartialInputStream.java
index a5ac059..cf3b107 100644
--- a/src/org/apache/james/mime4j/util/PartialInputStream.java
+++ b/src/org/apache/james/mime4j/util/PartialInputStream.java
@@ -1,63 +1,63 @@
-/****************************************************************

- * 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.InputStream;

-import java.io.IOException;

-

-public class PartialInputStream extends PositionInputStream {

-    private final long limit;

-

-    public PartialInputStream(InputStream inputStream, long offset, long length) throws IOException {

-        super(inputStream);

-        inputStream.skip(offset);

-        this.limit = offset + length;

-    }

-

-    public int available() throws IOException {

-        return Math.min(super.available(), getBytesLeft());

-    }

-

-    public int read() throws IOException {

-        if (limit > position)

-            return super.read();

-        else

-            return -1;

-    }

-

-    public int read(byte b[]) throws IOException {

-        return read(b, 0, b.length);

-    }

-

-    public int read(byte b[], int off, int len) throws IOException {

-        len = Math.min(len, getBytesLeft());

-        return super.read(b, off, len);    //To change body of overridden methods use File | Settings | File Templates.

-    }

-

-    public long skip(long n) throws IOException {

-        n = Math.min(n, getBytesLeft());

-        return super.skip(n);    //To change body of overridden methods use File | Settings | File Templates.

-    }

-

-    private int getBytesLeft() {

-        return (int)Math.min(Integer.MAX_VALUE, limit - position);

-    }

-}

+/****************************************************************
+ * 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.InputStream;
+import java.io.IOException;
+
+public class PartialInputStream extends PositionInputStream {
+    private final long limit;
+
+    public PartialInputStream(InputStream inputStream, long offset, long length) throws IOException {
+        super(inputStream);
+        inputStream.skip(offset);
+        this.limit = offset + length;
+    }
+
+    public int available() throws IOException {
+        return Math.min(super.available(), getBytesLeft());
+    }
+
+    public int read() throws IOException {
+        if (limit > position)
+            return super.read();
+        else
+            return -1;
+    }
+
+    public int read(byte b[]) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    public int read(byte b[], int off, int len) throws IOException {
+        len = Math.min(len, getBytesLeft());
+        return super.read(b, off, len);    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    public long skip(long n) throws IOException {
+        n = Math.min(n, getBytesLeft());
+        return super.skip(n);    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    private int getBytesLeft() {
+        return (int)Math.min(Integer.MAX_VALUE, limit - position);
+    }
+}
diff --git a/src/org/apache/james/mime4j/util/PositionInputStream.java b/src/org/apache/james/mime4j/util/PositionInputStream.java
index 9fcd21d..7865b6e 100644
--- a/src/org/apache/james/mime4j/util/PositionInputStream.java
+++ b/src/org/apache/james/mime4j/util/PositionInputStream.java
@@ -1,87 +1,87 @@
-/****************************************************************

- * 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.InputStream;

-import java.io.IOException;

-

-public class PositionInputStream extends InputStream {

-

-    private final InputStream inputStream;

-    protected long position = 0;

-    private long markedPosition = 0;

-

-    public PositionInputStream(InputStream inputStream) {

-        this.inputStream = inputStream;

-    }

-

-    public long getPosition() {

-        return position;

-    }

-

-    public int available() throws IOException {

-        return inputStream.available();

-    }

-

-    public int read() throws IOException {

-        int b = inputStream.read();

-        if (b != -1)

-            position++;

-        return b;

-    }

-

-    public void close() throws IOException {

-        inputStream.close();

-    }

-

-    public void reset() throws IOException {

-        inputStream.reset();

-        position = markedPosition;

-    }

-

-    public boolean markSupported() {

-        return inputStream.markSupported();

-    }

-

-    public void mark(int readlimit) {

-        inputStream.mark(readlimit);

-        markedPosition = position;

-    }

-

-    public long skip(long n) throws IOException {

-        final long c = inputStream.skip(n);

-        position += c;

-        return c;

-    }

-

-    public int read(byte b[]) throws IOException {

-        final int c = inputStream.read(b);

-        position += c;

-        return c;

-    }

-

-    public int read(byte b[], int off, int len) throws IOException {

-        final int c = inputStream.read(b, off, len);

-        position += c;

-        return c;

-    }

-

-}

+/****************************************************************
+ * 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.InputStream;
+import java.io.IOException;
+
+public class PositionInputStream extends InputStream {
+
+    private final InputStream inputStream;
+    protected long position = 0;
+    private long markedPosition = 0;
+
+    public PositionInputStream(InputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+
+    public long getPosition() {
+        return position;
+    }
+
+    public int available() throws IOException {
+        return inputStream.available();
+    }
+
+    public int read() throws IOException {
+        int b = inputStream.read();
+        if (b != -1)
+            position++;
+        return b;
+    }
+
+    public void close() throws IOException {
+        inputStream.close();
+    }
+
+    public void reset() throws IOException {
+        inputStream.reset();
+        position = markedPosition;
+    }
+
+    public boolean markSupported() {
+        return inputStream.markSupported();
+    }
+
+    public void mark(int readlimit) {
+        inputStream.mark(readlimit);
+        markedPosition = position;
+    }
+
+    public long skip(long n) throws IOException {
+        final long c = inputStream.skip(n);
+        position += c;
+        return c;
+    }
+
+    public int read(byte b[]) throws IOException {
+        final int c = inputStream.read(b);
+        position += c;
+        return c;
+    }
+
+    public int read(byte b[], int off, int len) throws IOException {
+        final int c = inputStream.read(b, off, len);
+        position += c;
+        return c;
+    }
+
+}
diff --git a/src/org/apache/james/mime4j/util/SimpleTempStorage.java b/src/org/apache/james/mime4j/util/SimpleTempStorage.java
index 930e4db..32ce74b 100644
--- a/src/org/apache/james/mime4j/util/SimpleTempStorage.java
+++ b/src/org/apache/james/mime4j/util/SimpleTempStorage.java
@@ -1,238 +1,238 @@
-/****************************************************************

- * 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.BufferedInputStream;

-import java.io.BufferedOutputStream;

-import java.io.File;

-import java.io.FileInputStream;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.util.Random;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-

-/**

- * 

- * @version $Id: SimpleTempStorage.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-public class SimpleTempStorage extends TempStorage {

-    private static Log log = LogFactory.getLog(SimpleTempStorage.class);

-    

-    private TempPath rootPath = null;

-    private Random random = new Random();

-    

-    /**

-     * Creates a new <code>SimpleTempStorageManager</code> instance.

-     */

-    public SimpleTempStorage() {

-        rootPath = new SimpleTempPath(System.getProperty("java.io.tmpdir"));

-    }

-    

-    private TempPath createTempPath(TempPath parent, String prefix) 

-            throws IOException {

-        

-        if (prefix == null) {

-            prefix = "";

-        }

-        

-        File p = null;

-        int count = 1000;

-        do {

-            long n = Math.abs(random.nextLong());

-            p = new File(parent.getAbsolutePath(), prefix + n);

-            count--;

-        } while (p.exists() && count > 0);

-        

-        if (p.exists() || !p.mkdirs()) {

-            log.error("Unable to mkdirs on " + p.getAbsolutePath());

-            throw new IOException("Creating dir '" 

-                                    + p.getAbsolutePath() + "' failed."); 

-        }

-        

-        return new SimpleTempPath(p);

-    }

-    

-    private TempFile createTempFile(TempPath parent, String prefix, 

-                                    String suffix) throws IOException {

-        

-        if (prefix == null) {

-            prefix = "";

-        }

-        if (suffix == null) {

-            suffix = ".tmp";

-        }

-        

-        File f = null;

-        

-        int count = 1000;

-        synchronized (this) {

-            do {

-                long n = Math.abs(random.nextLong());

-                f = new File(parent.getAbsolutePath(), prefix + n + suffix);

-                count--;

-            } while (f.exists() && count > 0);

-            

-            if (f.exists()) {

-                throw new IOException("Creating temp file failed: "

-                                         + "Unable to find unique file name");

-            }

-            

-            try {

-                f.createNewFile();

-            } catch (IOException e) {

-                throw new IOException("Creating dir '" 

-                                        + f.getAbsolutePath() + "' failed."); 

-            }

-        }

-        

-        return new SimpleTempFile(f);

-    }

-    

-    /**

-     * @see org.apache.james.mime4j.util.TempStorage#getRootTempPath()

-     */

-    public TempPath getRootTempPath() {

-        return rootPath;

-    }

-

-    private class SimpleTempPath implements TempPath {

-        private File path = null;

-        

-        private SimpleTempPath(String path) {

-            this.path = new File(path);

-        }

-        

-        private SimpleTempPath(File path) {

-            this.path = path;

-        }

-        

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#createTempFile()

-         */

-        public TempFile createTempFile() throws IOException {

-            return SimpleTempStorage.this.createTempFile(this, null, null);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#createTempFile(java.lang.String, java.lang.String)

-         */

-        public TempFile createTempFile(String prefix, String suffix) 

-                throws IOException {

-            

-            return SimpleTempStorage.this.createTempFile(this, prefix, suffix);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#createTempFile(java.lang.String, java.lang.String, boolean)

-         */

-        public TempFile createTempFile(String prefix, String suffix, 

-                                       boolean allowInMemory) 

-            throws IOException {

-            

-            return SimpleTempStorage.this.createTempFile(this, prefix, suffix);

-        }

-        

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#getAbsolutePath()

-         */

-        public String getAbsolutePath() {

-            return path.getAbsolutePath();

-        }

-

-        /**

-         * Do nothing

-         */

-        public void delete() {

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#createTempPath()

-         */

-        public TempPath createTempPath() throws IOException {

-            return SimpleTempStorage.this.createTempPath(this, null);

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempPath#createTempPath(java.lang.String)

-         */

-        public TempPath createTempPath(String prefix) throws IOException {

-            return SimpleTempStorage.this.createTempPath(this, prefix);

-        }

-        

-    }

-    

-    private class SimpleTempFile implements TempFile {

-        private File file = null;

-

-        private SimpleTempFile(File file) {

-            this.file = file;

-            this.file.deleteOnExit();

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempFile#getInputStream()

-         */

-        public InputStream getInputStream() throws IOException {

-            return new BufferedInputStream(new FileInputStream(file));

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempFile#getOutputStream()

-         */

-        public OutputStream getOutputStream() throws IOException {

-            return new BufferedOutputStream(new FileOutputStream(file));

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempFile#getAbsolutePath()

-         */

-        public String getAbsolutePath() {

-            return file.getAbsolutePath();

-        }

-

-        /**

-         * Do nothing

-         */

-        public void delete() {

-            // Not implementated

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempFile#isInMemory()

-         */

-        public boolean isInMemory() {

-            return false;

-        }

-

-        /**

-         * @see org.apache.james.mime4j.util.TempFile#length()

-         */

-        public long length() {

-            return file.length();

-        }

-        

-    }

-}

+/****************************************************************
+ * 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.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Random;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * 
+ * @version $Id: SimpleTempStorage.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class SimpleTempStorage extends TempStorage {
+    private static Log log = LogFactory.getLog(SimpleTempStorage.class);
+    
+    private TempPath rootPath = null;
+    private Random random = new Random();
+    
+    /**
+     * Creates a new <code>SimpleTempStorageManager</code> instance.
+     */
+    public SimpleTempStorage() {
+        rootPath = new SimpleTempPath(System.getProperty("java.io.tmpdir"));
+    }
+    
+    private TempPath createTempPath(TempPath parent, String prefix) 
+            throws IOException {
+        
+        if (prefix == null) {
+            prefix = "";
+        }
+        
+        File p = null;
+        int count = 1000;
+        do {
+            long n = Math.abs(random.nextLong());
+            p = new File(parent.getAbsolutePath(), prefix + n);
+            count--;
+        } while (p.exists() && count > 0);
+        
+        if (p.exists() || !p.mkdirs()) {
+            log.error("Unable to mkdirs on " + p.getAbsolutePath());
+            throw new IOException("Creating dir '" 
+                                    + p.getAbsolutePath() + "' failed."); 
+        }
+        
+        return new SimpleTempPath(p);
+    }
+    
+    private TempFile createTempFile(TempPath parent, String prefix, 
+                                    String suffix) throws IOException {
+        
+        if (prefix == null) {
+            prefix = "";
+        }
+        if (suffix == null) {
+            suffix = ".tmp";
+        }
+        
+        File f = null;
+        
+        int count = 1000;
+        synchronized (this) {
+            do {
+                long n = Math.abs(random.nextLong());
+                f = new File(parent.getAbsolutePath(), prefix + n + suffix);
+                count--;
+            } while (f.exists() && count > 0);
+            
+            if (f.exists()) {
+                throw new IOException("Creating temp file failed: "
+                                         + "Unable to find unique file name");
+            }
+            
+            try {
+                f.createNewFile();
+            } catch (IOException e) {
+                throw new IOException("Creating dir '" 
+                                        + f.getAbsolutePath() + "' failed."); 
+            }
+        }
+        
+        return new SimpleTempFile(f);
+    }
+    
+    /**
+     * @see org.apache.james.mime4j.util.TempStorage#getRootTempPath()
+     */
+    public TempPath getRootTempPath() {
+        return rootPath;
+    }
+
+    private class SimpleTempPath implements TempPath {
+        private File path = null;
+        
+        private SimpleTempPath(String path) {
+            this.path = new File(path);
+        }
+        
+        private SimpleTempPath(File path) {
+            this.path = path;
+        }
+        
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#createTempFile()
+         */
+        public TempFile createTempFile() throws IOException {
+            return SimpleTempStorage.this.createTempFile(this, null, null);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#createTempFile(java.lang.String, java.lang.String)
+         */
+        public TempFile createTempFile(String prefix, String suffix) 
+                throws IOException {
+            
+            return SimpleTempStorage.this.createTempFile(this, prefix, suffix);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#createTempFile(java.lang.String, java.lang.String, boolean)
+         */
+        public TempFile createTempFile(String prefix, String suffix, 
+                                       boolean allowInMemory) 
+            throws IOException {
+            
+            return SimpleTempStorage.this.createTempFile(this, prefix, suffix);
+        }
+        
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#getAbsolutePath()
+         */
+        public String getAbsolutePath() {
+            return path.getAbsolutePath();
+        }
+
+        /**
+         * Do nothing
+         */
+        public void delete() {
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#createTempPath()
+         */
+        public TempPath createTempPath() throws IOException {
+            return SimpleTempStorage.this.createTempPath(this, null);
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempPath#createTempPath(java.lang.String)
+         */
+        public TempPath createTempPath(String prefix) throws IOException {
+            return SimpleTempStorage.this.createTempPath(this, prefix);
+        }
+        
+    }
+    
+    private class SimpleTempFile implements TempFile {
+        private File file = null;
+
+        private SimpleTempFile(File file) {
+            this.file = file;
+            this.file.deleteOnExit();
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempFile#getInputStream()
+         */
+        public InputStream getInputStream() throws IOException {
+            return new BufferedInputStream(new FileInputStream(file));
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempFile#getOutputStream()
+         */
+        public OutputStream getOutputStream() throws IOException {
+            return new BufferedOutputStream(new FileOutputStream(file));
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempFile#getAbsolutePath()
+         */
+        public String getAbsolutePath() {
+            return file.getAbsolutePath();
+        }
+
+        /**
+         * Do nothing
+         */
+        public void delete() {
+            // Not implementated
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempFile#isInMemory()
+         */
+        public boolean isInMemory() {
+            return false;
+        }
+
+        /**
+         * @see org.apache.james.mime4j.util.TempFile#length()
+         */
+        public long length() {
+            return file.length();
+        }
+        
+    }
+}
diff --git a/src/org/apache/james/mime4j/util/TempFile.java b/src/org/apache/james/mime4j/util/TempFile.java
index f67e1e9..cc2ea6d 100644
--- a/src/org/apache/james/mime4j/util/TempFile.java
+++ b/src/org/apache/james/mime4j/util/TempFile.java
@@ -1,84 +1,84 @@
-/****************************************************************

- * 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.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-/**

- * @version $Id: TempFile.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $

- */

-public interface TempFile {

-    /**

-     * Gets an <code>InputStream</code> to read bytes from this temporary file.

-     * NOTE: The stream should NOT be wrapped in 

-     * <code>BufferedInputStream</code> by the caller. If the implementing 

-     * <code>TempFile</code> creates a <code>FileInputStream</code> or any

-     * other stream which would benefit from being buffered it's the 

-     * <code>TempFile</code>'s responsibility to wrap it.

-     * 

-     * @return the stream.

-     * @throws IOException

-     */

-    InputStream getInputStream() throws IOException;

-    

-    /**

-     * Gets an <code>OutputStream</code> to write bytes to this temporary file.

-     * NOTE: The stream should NOT be wrapped in 

-     * <code>BufferedOutputStream</code> by the caller. If the implementing 

-     * <code>TempFile</code> creates a <code>FileOutputStream</code> or any

-     * other stream which would benefit from being buffered it's the 

-     * <code>TempFile</code>'s responsibility to wrap it.

-     * 

-     * @return the stream.

-     * @throws IOException

-     */

-    OutputStream getOutputStream() throws IOException;

-    

-    /**

-     * Returns the absolute path including file name of this 

-     * <code>TempFile</code>. The path may be <code>null</code> if this is

-     * an in-memory file.

-     * 

-     * @return the absolute path.

-     */

-    String getAbsolutePath();

-    

-    /**

-     * Deletes this file as soon as possible.

-     */

-    void delete();

-    

-    /**

-     * Determines if this is an in-memory file.

-     * 

-     * @return <code>true</code> if this file is currently in memory,

-     *         <code>false</code> otherwise.

-     */

-    boolean isInMemory();

-    

-    /**

-     * Gets the length of this temporary file.

-     * 

-     * @return the length.

-     */

-    long length();

-}

+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @version $Id: TempFile.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
+ */
+public interface TempFile {
+    /**
+     * Gets an <code>InputStream</code> to read bytes from this temporary file.
+     * NOTE: The stream should NOT be wrapped in 
+     * <code>BufferedInputStream</code> by the caller. If the implementing 
+     * <code>TempFile</code> creates a <code>FileInputStream</code> or any
+     * other stream which would benefit from being buffered it's the 
+     * <code>TempFile</code>'s responsibility to wrap it.
+     * 
+     * @return the stream.
+     * @throws IOException
+     */
+    InputStream getInputStream() throws IOException;
+    
+    /**
+     * Gets an <code>OutputStream</code> to write bytes to this temporary file.
+     * NOTE: The stream should NOT be wrapped in 
+     * <code>BufferedOutputStream</code> by the caller. If the implementing 
+     * <code>TempFile</code> creates a <code>FileOutputStream</code> or any
+     * other stream which would benefit from being buffered it's the 
+     * <code>TempFile</code>'s responsibility to wrap it.
+     * 
+     * @return the stream.
+     * @throws IOException
+     */
+    OutputStream getOutputStream() throws IOException;
+    
+    /**
+     * Returns the absolute path including file name of this 
+     * <code>TempFile</code>. The path may be <code>null</code> if this is
+     * an in-memory file.
+     * 
+     * @return the absolute path.
+     */
+    String getAbsolutePath();
+    
+    /**
+     * Deletes this file as soon as possible.
+     */
+    void delete();
+    
+    /**
+     * Determines if this is an in-memory file.
+     * 
+     * @return <code>true</code> if this file is currently in memory,
+     *         <code>false</code> otherwise.
+     */
+    boolean isInMemory();
+    
+    /**
+     * Gets the length of this temporary file.
+     * 
+     * @return the length.
+     */
+    long length();
+}
diff --git a/src/org/apache/james/mime4j/util/TempPath.java b/src/org/apache/james/mime4j/util/TempPath.java
index 3b55aa6..3c4cf81 100644
--- a/src/org/apache/james/mime4j/util/TempPath.java
+++ b/src/org/apache/james/mime4j/util/TempPath.java
@@ -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.util;

-

-import java.io.IOException;

-

-/**

- * 

- * @version $Id: TempPath.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-public interface TempPath {

-    TempPath createTempPath() throws IOException;

-    TempPath createTempPath(String prefix) throws IOException;

-    

-    /**

-     * Creates a new temporary file. Wheter it will be be created in memory

-     * or on disk is up to to the implementation. 

-     * The prefix will be empty and the suffix will be 

-     * <code>.tmp</code> if created on disk. 

-     * 

-     * @return the temporary file.

-     */

-    TempFile createTempFile() throws IOException;

-    

-    /**

-     * Creates a new temporary file. Wheter it will be be created in memory

-     * or on disk is up to to the implementation. 

-     * The prefix and suffix can be set by the user. 

-     * 

-     * @param prefix the prefix to use. <code>null</code> gives no prefix.

-     * @param suffix the suffix to use. <code>null</code> gives 

-     *        <code>.tmp</code>.

-     * @return the temporary file.

-     */

-    TempFile createTempFile(String prefix, String suffix) throws IOException;

-    

-    /**

-     * Creates a new temporary file. Wheter it will be be created in memory

-     * or on disk can be specified using the <code>allowInMemory</code> 

-     * parameter. If the implementation doesn't support in-memory files

-     * the new file will be created on disk. 

-     * The prefix and suffix can be set by the user. 

-     * 

-     * @param prefix the prefix to use. <code>null</code> gives no prefix.

-     * @param suffix the suffix to use. <code>null</code> gives 

-     *        <code>.tmp</code>.

-     * @param allowInMemory if <code>true</code> the file MIGHT be created in 

-     *        memory if supported by the implentation. If <code>false</code> the

-     *        file MUST be created on disk.

-     * @return the temporary file.

-     */

-    TempFile createTempFile(String prefix, String suffix, 

-                            boolean allowInMemory) throws IOException;

-    String getAbsolutePath();

-    void delete();

-}

+/****************************************************************
+ * 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.IOException;
+
+/**
+ * 
+ * @version $Id: TempPath.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public interface TempPath {
+    TempPath createTempPath() throws IOException;
+    TempPath createTempPath(String prefix) throws IOException;
+    
+    /**
+     * Creates a new temporary file. Wheter it will be be created in memory
+     * or on disk is up to to the implementation. 
+     * The prefix will be empty and the suffix will be 
+     * <code>.tmp</code> if created on disk. 
+     * 
+     * @return the temporary file.
+     */
+    TempFile createTempFile() throws IOException;
+    
+    /**
+     * Creates a new temporary file. Wheter it will be be created in memory
+     * or on disk is up to to the implementation. 
+     * The prefix and suffix can be set by the user. 
+     * 
+     * @param prefix the prefix to use. <code>null</code> gives no prefix.
+     * @param suffix the suffix to use. <code>null</code> gives 
+     *        <code>.tmp</code>.
+     * @return the temporary file.
+     */
+    TempFile createTempFile(String prefix, String suffix) throws IOException;
+    
+    /**
+     * Creates a new temporary file. Wheter it will be be created in memory
+     * or on disk can be specified using the <code>allowInMemory</code> 
+     * parameter. If the implementation doesn't support in-memory files
+     * the new file will be created on disk. 
+     * The prefix and suffix can be set by the user. 
+     * 
+     * @param prefix the prefix to use. <code>null</code> gives no prefix.
+     * @param suffix the suffix to use. <code>null</code> gives 
+     *        <code>.tmp</code>.
+     * @param allowInMemory if <code>true</code> the file MIGHT be created in 
+     *        memory if supported by the implentation. If <code>false</code> the
+     *        file MUST be created on disk.
+     * @return the temporary file.
+     */
+    TempFile createTempFile(String prefix, String suffix, 
+                            boolean allowInMemory) throws IOException;
+    String getAbsolutePath();
+    void delete();
+}
diff --git a/src/org/apache/james/mime4j/util/TempStorage.java b/src/org/apache/james/mime4j/util/TempStorage.java
index 4d3be47..c1cd229 100644
--- a/src/org/apache/james/mime4j/util/TempStorage.java
+++ b/src/org/apache/james/mime4j/util/TempStorage.java
@@ -1,72 +1,72 @@
-/****************************************************************

- * 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;

-

-//BEGIN android-changed: Stubbing out logging

-import org.apache.james.mime4j.Log;

-import org.apache.james.mime4j.LogFactory;

-//END android-changed

-

-/**

- * 

- * @version $Id: TempStorage.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $

- */

-public abstract class TempStorage {

-    private static Log log = LogFactory.getLog(TempStorage.class);

-    private static TempStorage inst = null;

-    

-    static {

-        

-        String clazz = System.getProperty("org.apache.james.mime4j.tempStorage");

-        try {

-            

-            if (inst != null) {

-                inst = (TempStorage) Class.forName(clazz).newInstance();

-            }

-            

-        } catch (Throwable t) {

-            log.warn("Unable to create or instantiate TempStorage class '" 

-                      + clazz + "' using SimpleTempStorage instead", t);

-        }

-

-        if (inst == null) {

-            inst = new SimpleTempStorage();            

-        }

-    }

-    

-    /**

-     * Gets the root temporary path which should be used to 

-     * create new temporary paths or files.

-     * 

-     * @return the root temporary path.

-     */

-    public abstract TempPath getRootTempPath();

-    

-    public static TempStorage getInstance() {

-        return inst;

-    }

-    

-    public static void setInstance(TempStorage inst) {

-        if (inst == null) {

-            throw new NullPointerException("inst");

-        }

-        TempStorage.inst = inst;

-    }

-}

+/****************************************************************
+ * 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;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * 
+ * @version $Id: TempStorage.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public abstract class TempStorage {
+    private static Log log = LogFactory.getLog(TempStorage.class);
+    private static TempStorage inst = null;
+    
+    static {
+        
+        String clazz = System.getProperty("org.apache.james.mime4j.tempStorage");
+        try {
+            
+            if (inst != null) {
+                inst = (TempStorage) Class.forName(clazz).newInstance();
+            }
+            
+        } catch (Throwable t) {
+            log.warn("Unable to create or instantiate TempStorage class '" 
+                      + clazz + "' using SimpleTempStorage instead", t);
+        }
+
+        if (inst == null) {
+            inst = new SimpleTempStorage();            
+        }
+    }
+    
+    /**
+     * Gets the root temporary path which should be used to 
+     * create new temporary paths or files.
+     * 
+     * @return the root temporary path.
+     */
+    public abstract TempPath getRootTempPath();
+    
+    public static TempStorage getInstance() {
+        return inst;
+    }
+    
+    public static void setInstance(TempStorage inst) {
+        if (inst == null) {
+            throw new NullPointerException("inst");
+        }
+        TempStorage.inst = inst;
+    }
+}
diff --git a/tests/src/com/android/emailcommon/mail/AddressUnitTests.java b/tests/src/com/android/emailcommon/mail/AddressUnitTests.java
index 48186e4..32a10f9 100644
--- a/tests/src/com/android/emailcommon/mail/AddressUnitTests.java
+++ b/tests/src/com/android/emailcommon/mail/AddressUnitTests.java
@@ -20,9 +20,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import org.apache.james.mime4j.decoder.DecoderUtil;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
 /**
  * This is a series of unit tests for the Address class.  These tests must be locally
  * complete - no server(s) required.
@@ -71,7 +68,7 @@
     }
 
     // see documentation of DecoderUtil.decodeEncodedWords() for details
-    private String padEncoded(String s) {
+    private static String padEncoded(String s) {
         return "=?UTF-8?B?" + s + "?=";
     }
 
@@ -683,36 +680,4 @@
         // isAllValid() must accept empty address list as valid
         assertTrue("Empty address list is valid", Address.isAllValid(""));
     }
-
-    /**
-     * Legacy pack() used for testing legacyUnpack().
-     * The packed list is a comma separated list of:
-     * URLENCODE(address)[;URLENCODE(personal)]
-     * @See pack()
-     */
-    private static String legacyPack(Address[] addresses) {
-        if (addresses == null) {
-            return null;
-        } else if (addresses.length == 0) {
-            return "";
-        }
-        StringBuffer sb = new StringBuffer();
-        for (int i = 0, count = addresses.length; i < count; i++) {
-            Address address = addresses[i];
-            try {
-                sb.append(URLEncoder.encode(address.getAddress(), "UTF-8"));
-                if (address.getPersonal() != null) {
-                    sb.append(';');
-                    sb.append(URLEncoder.encode(address.getPersonal(), "UTF-8"));
-                }
-                if (i < count - 1) {
-                    sb.append(',');
-                }
-            }
-            catch (UnsupportedEncodingException uee) {
-                return null;
-            }
-        }
-        return sb.toString();
-    }
 }
diff --git a/tests/src/com/android/mail/compose/ComposeActivityTest.java b/tests/src/com/android/mail/compose/ComposeActivityTest.java
index ef25599..ee2ae25 100644
--- a/tests/src/com/android/mail/compose/ComposeActivityTest.java
+++ b/tests/src/com/android/mail/compose/ComposeActivityTest.java
@@ -1035,4 +1035,57 @@
             }
         });
     }
+
+    // Test existence of % signs and basic functionality (to, cc, bcc, subject)
+    public void testInitFromMailTo0() throws Throwable {
+        final ComposeActivity activity = getActivity();
+        final String input = "mailto:Test1@Test1.com?cc=Test2@Test2.com" +
+                "&bcc=Test3@Test3.com&subject=Hello&body=Bye%25Bye";
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.initFromMailTo(input);
+                assertEquals(1, activity.getToAddresses().length);
+                assertTrue(activity.getToAddresses()[0].contains("Test1@Test1.com"));
+                assertEquals(1, activity.getCcAddresses().length);
+                assertTrue(activity.getCcAddresses()[0].contains("Test2@Test2.com"));
+                assertEquals(1, activity.getBccAddresses().length);
+                assertTrue(activity.getBccAddresses()[0].contains("Test3@Test3.com"));
+                assertEquals("Hello", activity.getSubject());
+                assertEquals("%25 should be decoded into %",
+                        "Bye%Bye", activity.getBody().getText().toString());
+            }
+        });
+    }
+
+    // Test existence of + and space in addition to %
+    public void testInitFromMailTo1() throws Throwable {
+        final ComposeActivity activity = getActivity();
+        final String query = "Bye+Bye %";
+        final Uri uri = Uri.parse("mailto:test@test.com?body=" + encodeMailtoParam(query));
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.initFromMailTo(uri.toString());
+                assertEquals(query, activity.getBody().getText().toString());
+            }
+        });
+    }
+
+    // Test existence of random set of url encoded characters
+    public void testInitFromMailTo2() throws Throwable {
+        final ComposeActivity activity = getActivity();
+        final String query = "I'm TESTING @#$%^&*\"";
+        final Uri uri = Uri.parse("mailto:test@test.com?body=" + encodeMailtoParam(query));
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.initFromMailTo(uri.toString());
+                assertEquals(query, activity.getBody().getText().toString());
+            }
+        });
+    }
 }