am 3d353bcc: (-s ours) Import translations. DO NOT MERGE
* commit '3d353bcc0858208e7320cd3ef6ab930a0fe6d60f':
Import translations. DO NOT MERGE
diff --git a/Android.mk b/Android.mk
index 3e60c64..bc81af2 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
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_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 aea6024..c2f0700 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">"---------- Перанакіроўванае паведамленне ----------<br>Ад: <xliff:g id="FROM">%1$s</xliff:g><br>Дата: <xliff:g id="DATE">%2$s</xliff:g><br>Тэма: <xliff:g id="SUBJECT">%3$s</xliff:g><br>Каму: <xliff:g id="TO">%4$s</xliff:g><br>"</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><br>"</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гнаравана: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- <item quantity="other" msgid="1941193356069303587">"Праiгнаравана: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- </plurals>
- <plurals name="conversation_spammed">
- <item quantity="one" msgid="3060864972122279220">"Пазначана як спам: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- <item quantity="other" msgid="5071459475389411227">"Пазначана як спам: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- </plurals>
- <plurals name="conversation_not_spam">
- <item quantity="one" msgid="7361303622131552596">"<b><xliff:g id="COUNT">%1$d</xliff:g></b> пазначана як не спам."</item>
- <item quantity="other" msgid="2250720080221922267">"<b><xliff:g id="COUNT">%1$d</xliff:g></b> пазначана як не спам."</item>
- </plurals>
- <plurals name="conversation_not_important">
- <item quantity="one" msgid="788387259924027833">"Пазначаны як няважныя: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- <item quantity="other" msgid="6662213740575340397">"Пазначаны як няважныя: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- </plurals>
- <plurals name="conversation_phished">
- <item quantity="one" msgid="5386916574432049235">"<b><xliff:g id="COUNT">%1$d</xliff:g></b> пазначана як фiшынг."</item>
- <item quantity="other" msgid="3678624994662639442">"<b><xliff:g id="COUNT">%1$d</xliff:g></b> пазначана як фiшынг."</item>
- </plurals>
- <plurals name="conversation_archived">
- <item quantity="one" msgid="5457612502965117672">"Заархiвавана: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- <item quantity="other" msgid="3012054504370916482">"Заархiвавана: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- </plurals>
- <plurals name="conversation_deleted">
- <item quantity="one" msgid="4398693029405479323">"Выдалена: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</item>
- <item quantity="other" msgid="8630099095360065837">"Выдалена: <b><xliff:g id="COUNT">%1$d</xliff:g></b>."</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 d8c4ed8..e4e1a61 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 bd85ae4..5725d28 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 besked 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 a1675c2..5693f07 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 4a91ea5..1a46df4 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 099eb54..5fe582f 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 piakkoin."</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 a4f0e50..694d4ad 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 c62d0ff..71f4c17 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 8d9ce0e..017db00 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 bcd3f3e..20d361c 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 6d428d5..99f9b9d 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 0d6fe61..6e5c312 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 93b192c..86d596c 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 4d03ae9..0390810 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 50ec2e9..856e19b 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">"Váš e-mail sa zobrazí čoskoro."</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 2c5c705..6f88d5a 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 b11d4c7..3a88557 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 usawazishaji"</string>
- <string name="wait_for_sync_body" msgid="7718316938173643688">"Barua pepe yako itaonekana 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 c95401d..007b1ef 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 fa6b033..28a251f 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 9dce29d..4c55885 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/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..88bc270
--- /dev/null
+++ b/src/com/android/mail/analytics/AnalyticsTimer.java
@@ -0,0 +1,74 @@
+/*
+ * 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";
+
+ 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;
+ }
+
+ 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..52b10d9 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"
@@ -1518,20 +1518,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 +1543,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 +1575,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 +1717,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 +1730,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 +1754,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 +1768,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 +1780,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 +1788,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 +1804,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 +1812,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 +1822,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 +1833,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 +1841,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 +2066,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 +2087,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 +2122,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 +2175,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 +2275,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);
@@ -2324,7 +2406,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/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/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
index 93e7ca3..230e634 100644
--- a/src/com/android/mail/browse/MessageAttachmentBar.java
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -116,6 +116,7 @@
final Attachment prevAttachment = mAttachment;
mAttachment = attachment;
+ mActionHandler.setAccount(mAccount.getEmailAddress());
mActionHandler.setMessage(message);
mActionHandler.setAttachment(mAttachment);
@@ -224,6 +225,8 @@
menu.findItem(R.id.attachment_extra_option1).setVisible(shouldShowExtraOption1());
mPopup.show();
+
+ AttachmentActionHandler.onOverflowOpened(getContext());
}
} else {
// Handles clicking the attachment
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
index 9c081d0..f904b82 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,19 @@
private BidiFormatter mBidiFormatter;
+ private MessageFooterCallbacks mCallbacks;
+
+ /**
+ * 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);
+ }
+
public MessageFooterView(Context context) {
this(context, null);
}
@@ -90,18 +109,21 @@
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) {
+ public void bind(
+ MessageHeaderItem headerItem, MessageFooterItem footerItem, 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
@@ -117,12 +139,14 @@
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();
+ mMessageFooterItem = footerItem;
mMessageHeaderItem = headerItem;
final Integer attachmentLoaderId = getAttachmentLoaderId();
@@ -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()) {
@@ -197,8 +241,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 +260,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 +296,7 @@
@Override
public void onDetachedFromParent() {
- // Do nothing
+ // Do nothing.
}
@Override
@@ -285,6 +340,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 +352,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 +365,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..8b6ef04 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
@@ -563,13 +558,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 +842,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 +1071,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 +1088,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 +1417,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..fc6ac63 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,
@@ -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,12 @@
// 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) {
+ } catch (UnsupportedEncodingException ignore) {
// Should never happen; default charset is UTF-8
decodedPhoneExtra = extra;
}
@@ -228,10 +272,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 +283,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 +297,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 +366,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 +383,24 @@
* {@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) {
+ private void setupImageMenu(String url, ContextMenu menu) {
final Message 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..7903ab9 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.
@@ -319,14 +330,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 +351,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 +399,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 +428,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,6 +522,24 @@
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) {
@@ -585,6 +608,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) {
@@ -1465,6 +1530,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 +1592,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 */);
}
}
@@ -3502,7 +3582,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/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..29522e5 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,30 @@
// 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));
+ }
+
+ // 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");
+ new Attachment(context, viewablePart, emlFileUri, messageId, cid, true);
+ }
}
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/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..1c02015 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -75,6 +75,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 +178,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 +248,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 +1052,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 +1389,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 +1461,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 +1847,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 +1882,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 +2018,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);
}
}
@@ -1978,6 +2050,23 @@
if (!showNextConversation(target, operation)) {
// This method will be called again if the user selects an autoadvance option
+
+ // HACKFIX around b/9904716. We were not properly performing the last
+ // DestructiveAction. A proper fix probably involves rewriting
+ // the logic for animating changes in the list as well as undo
+ // and probably batch DestructiveActions. The change is limited to
+ // tablets where the issue occurs.
+ final ConversationListFragment convListFragment = getConversationListFragment();
+ if (mIsTablet && convListFragment != null) {
+ convListFragment.getAnimatedAdapter().setNextAction(
+ new SwipeableListView.ListItemsRemovedListener() {
+ @Override
+ public void onListItemsRemoved() {
+ action.performAction();
+ }
+ }
+ );
+ }
return;
}
// If the conversation is in the selected set, remove it from the set.
@@ -2285,7 +2374,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 +2424,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 +2436,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 +2498,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 +2523,7 @@
@Override
public void showWaitForInitialization() {
mViewMode.enterWaitingForInitializationMode();
- mWaitFragment = WaitFragment.newInstance(mAccount);
+ mWaitFragment = WaitFragment.newInstance(mAccount, true /* expectingMessages */);
}
private void updateWaitMode() {
@@ -2499,7 +2590,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 +2860,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 +2875,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 +2907,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 +2921,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 +3006,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 +3042,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);
}
}
@@ -3224,7 +3326,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 +3393,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 +3470,13 @@
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.name, ignoreInitialConversationLimit);
}
@Override
@@ -3733,15 +3843,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 +3865,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 +3887,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 +3907,11 @@
}
@Override
+ public void setUndoCallback(UndoCallback undoCallback) {
+ mUndoCallback = undoCallback;
+ }
+
+ @Override
public void performAction() {
if (isPerformed()) {
return;
@@ -3824,7 +3944,7 @@
}
}
ops.add(mConversationListCursor.getConversationFolderOperation(target,
- folderUris, adds, targetFolders.values()));
+ folderUris, adds, targetFolders.values(), mUndoCallback));
}
if (mConversationListCursor != null) {
mConversationListCursor.updateBulkValues(ops);
@@ -3850,28 +3970,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 +4202,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 +4211,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/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..95067e1 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;
@@ -867,6 +867,10 @@
mPendingDestruction = next;
}
+ /* package */ void setNextAction(ListItemsRemovedListener next) {
+ mPendingDestruction = next;
+ }
+
private void updateAnimatingConversationItems(Object obj, HashSet<Long> items) {
if (!items.isEmpty()) {
if (obj instanceof ConversationItemView) {
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..f0f8e46 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -27,7 +27,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 +38,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 +96,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 +147,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
+ private boolean mJustLoadedNewList;
/** 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.setEmptyView(mEmptyView);
+ 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);
mCanTakeDownLoadingView = false;
+ showLoadingView();
mHandler.removeCallbacks(mHideLoadingRunnable);
mHandler.postDelayed(mHideLoadingRunnable, MINIMUM_LOADING_DURATION);
}
@@ -179,6 +200,8 @@
}
};
+ // 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 +265,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 +286,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 +490,9 @@
mListView.setListItemSwipedListener(this);
mListView.setSwipeListener(this);
+ // 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 +624,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 {
@@ -697,6 +720,7 @@
* must be called on the UI thread.
*/
private void showList() {
+ mJustLoadedNewList = true;
mListView.setEmptyView(null);
onFolderUpdated(mActivity.getFolderController().getFolder());
onConversationListStatusUpdated();
@@ -891,8 +915,12 @@
}
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();
@@ -919,10 +947,7 @@
|| 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);
+ showEmptyView();
}
}
}
@@ -936,16 +961,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 +985,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;
}
}
@@ -998,6 +1025,10 @@
if (mConversationCursorHash == newCursorHash && mConversationCursorHash != 0) {
mListAdapter.notifyDataSetChanged();
}
+ if (newCursor != null) {
+ updateAnalyticsData(newCursor);
+ updateSearchResultHeader(newCursor.getCount());
+ }
mConversationCursorHash = newCursorHash;
if (newCursor != null && newCursor.getCount() > 0) {
@@ -1063,7 +1094,6 @@
@Override
public void onSetEmpty() {
- mSelectionModeExitedTimestamp = System.currentTimeMillis();
mSwipeRefreshWidget.setEnabled(true);
}
@@ -1109,4 +1139,31 @@
// This will call back to showSyncStatusBar():
mActivity.getFolderController().requestFolderRefresh();
}
+
+ /**
+ * Extracted function that handles Analytics state and logging updates whenever a new non-null
+ * cursor is set as the new cursor
+ * @param newCursor the new cursor pointer, cannot be null
+ */
+ private void updateAnalyticsData(ConversationCursor newCursor) {
+ // Check if the cursor is ready for display
+ if (!mLoadingViewPending) {
+ // If the count is 0, then we check which log is applicable
+ if (newCursor.getCount() == 0) {
+ if (mJustLoadedNewList) {
+ Analytics.getInstance().sendEvent("empty_state", "post_label_change",
+ mFolder.getTypeDescription(), 0);
+ } else if (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();
+ mJustLoadedNewList = false;
+ }
+ }
}
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..15a8739 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);
@@ -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);
@@ -1485,10 +1497,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 +1640,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 ee654ad..24678be 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..7703bd1 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:
@@ -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..8234da2 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -31,7 +31,6 @@
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.compose.ComposeActivity;
@@ -353,17 +352,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..43d8f6d 100644
--- a/src/com/android/mail/ui/SecureConversationViewController.java
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -30,9 +30,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 +57,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 +80,6 @@
public SecureConversationViewController(SecureConversationViewControllerCallbacks callbacks) {
mCallbacks = callbacks;
- mBidiFormatter = BidiFormatter.getInstance();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -159,7 +160,7 @@
mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager(),
- mCallbacks.getConversationAccountController());
+ mCallbacks.getConversationAccountController(), this);
mCallbacks.startMessageLoader();
@@ -170,6 +171,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 +226,8 @@
if (mMessage.hasAttachments) {
mMessageFooterView.setVisibility(View.VISIBLE);
- mMessageFooterView.bind(item, false);
+ mMessageFooterView.bind(
+ item, ConversationViewAdapter.newMessageFooterItem(null, item), false);
}
}
@@ -247,10 +255,6 @@
}
- public BidiFormatter getBidiFormatter() {
- return mBidiFormatter;
- }
-
// Start MessageHeaderViewCallbacks implementations
@Override
@@ -295,4 +299,23 @@
}
// 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/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..ae9f3ea 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";
@@ -1054,6 +1055,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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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<File> 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 >= minimum size
- * and <= 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 >= minimum size
+ * and <= 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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<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<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<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<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 < 0 or >= 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 < 0 or >= 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());
+ }
+ });
+ }
}