From 014b6c19c3c44d17028bfed7ce151f274f628ce3 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Tue, 13 Jul 2010 21:49:28 +0000 Subject: [PATCH] svn:eol-style=LF for src and XML resources Fixes issue 1982 --- res/values-de/strings.xml | 1562 ++-- src/com/beetstra/jutf7/Base64Util.java | 234 +- src/com/beetstra/jutf7/CharsetProvider.java | 180 +- .../beetstra/jutf7/ModifiedUTF7Charset.java | 114 +- src/com/beetstra/jutf7/UTF7Charset.java | 150 +- src/com/beetstra/jutf7/UTF7StyleCharset.java | 234 +- .../jutf7/UTF7StyleCharsetDecoder.java | 390 +- .../jutf7/UTF7StyleCharsetEncoder.java | 434 +- src/com/fsck/k9/FontSizes.java | 764 +- src/com/fsck/k9/Identity.java | 158 +- src/com/fsck/k9/activity/MessageList.java | 6296 ++++++++--------- .../fsck/k9/activity/ToggleScrollView.java | 66 +- src/com/fsck/k9/helper/AutoSyncSdk5.java | 42 +- src/com/fsck/k9/mail/BodyPart.java | 24 +- src/com/fsck/k9/mail/FetchProfile.java | 118 +- src/com/fsck/k9/mail/Multipart.java | 164 +- .../mail/transport/TrustedSocketFactory.java | 142 +- src/com/fsck/k9/service/MailService.java | 1072 +-- src/com/jcraft/jzlib/ZStreamException.java | 88 +- src/org/apache/commons/io/CopyUtils.java | 664 +- .../apache/commons/io/DirectoryWalker.java | 1240 ++-- src/org/apache/commons/io/EndianUtils.java | 978 +-- src/org/apache/commons/io/FileCleaner.java | 308 +- .../commons/io/FileCleaningTracker.java | 516 +- .../apache/commons/io/FileDeleteStrategy.java | 312 +- .../apache/commons/io/FileSystemUtils.java | 914 +-- src/org/apache/commons/io/FileUtils.java | 3780 +++++----- src/org/apache/commons/io/FilenameUtils.java | 2520 +++---- src/org/apache/commons/io/HexDump.java | 298 +- src/org/apache/commons/io/IOCase.java | 476 +- .../commons/io/IOExceptionWithCause.java | 138 +- src/org/apache/commons/io/IOUtils.java | 2548 +++---- src/org/apache/commons/io/LineIterator.java | 362 +- .../io/comparator/DefaultFileComparator.java | 136 +- .../comparator/ExtensionFileComparator.java | 224 +- .../LastModifiedFileComparator.java | 158 +- .../io/comparator/NameFileComparator.java | 212 +- .../io/comparator/PathFileComparator.java | 214 +- .../io/comparator/ReverseComparator.java | 114 +- .../io/comparator/SizeFileComparator.java | 264 +- .../apache/commons/io/comparator/package.html | 50 +- .../io/filefilter/AbstractFileFilter.java | 134 +- .../commons/io/filefilter/AgeFileFilter.java | 300 +- .../commons/io/filefilter/AndFileFilter.java | 334 +- .../io/filefilter/CanReadFileFilter.java | 184 +- .../io/filefilter/CanWriteFileFilter.java | 160 +- .../io/filefilter/ConditionalFileFilter.java | 134 +- .../io/filefilter/DelegateFileFilter.java | 208 +- .../io/filefilter/DirectoryFileFilter.java | 146 +- .../io/filefilter/EmptyFileFilter.java | 168 +- .../io/filefilter/FalseFileFilter.java | 144 +- .../commons/io/filefilter/FileFileFilter.java | 120 +- .../io/filefilter/FileFilterUtils.java | 722 +- .../io/filefilter/HiddenFileFilter.java | 152 +- .../commons/io/filefilter/IOFileFilter.java | 110 +- .../commons/io/filefilter/NameFileFilter.java | 382 +- .../commons/io/filefilter/NotFileFilter.java | 156 +- .../commons/io/filefilter/OrFileFilter.java | 322 +- .../io/filefilter/PrefixFileFilter.java | 394 +- .../io/filefilter/RegexFileFilter.java | 244 +- .../commons/io/filefilter/SizeFileFilter.java | 206 +- .../io/filefilter/SuffixFileFilter.java | 396 +- .../commons/io/filefilter/TrueFileFilter.java | 144 +- .../io/filefilter/WildcardFileFilter.java | 392 +- .../commons/io/filefilter/WildcardFilter.java | 280 +- .../apache/commons/io/filefilter/package.html | 286 +- .../io/input/AutoCloseInputStream.java | 258 +- .../commons/io/input/CharSequenceReader.java | 310 +- .../input/ClassLoaderObjectInputStream.java | 154 +- .../io/input/CloseShieldInputStream.java | 104 +- .../commons/io/input/ClosedInputStream.java | 96 +- .../commons/io/input/CountingInputStream.java | 350 +- .../commons/io/input/DemuxInputStream.java | 182 +- .../commons/io/input/NullInputStream.java | 658 +- .../apache/commons/io/input/NullReader.java | 626 +- .../commons/io/input/ProxyInputStream.java | 258 +- .../apache/commons/io/input/ProxyReader.java | 260 +- .../io/input/SwappedDataInputStream.java | 502 +- .../commons/io/input/TeeInputStream.java | 294 +- src/org/apache/commons/io/input/package.html | 50 +- .../io/output/ByteArrayOutputStream.java | 616 +- .../io/output/CloseShieldOutputStream.java | 104 +- .../commons/io/output/ClosedOutputStream.java | 100 +- .../io/output/CountingOutputStream.java | 308 +- .../io/output/DeferredFileOutputStream.java | 538 +- .../commons/io/output/DemuxOutputStream.java | 204 +- .../io/output/FileWriterWithEncoding.java | 648 +- .../commons/io/output/LockableFileWriter.java | 666 +- .../commons/io/output/NullOutputStream.java | 130 +- .../apache/commons/io/output/NullWriter.java | 192 +- .../commons/io/output/ProxyOutputStream.java | 178 +- .../apache/commons/io/output/ProxyWriter.java | 222 +- .../commons/io/output/TeeOutputStream.java | 188 +- .../io/output/ThresholdingOutputStream.java | 514 +- src/org/apache/commons/io/output/package.html | 50 +- src/org/apache/commons/io/overview.html | 64 +- src/org/apache/commons/io/package.html | 94 +- .../james/mime4j/AbstractContentHandler.java | 226 +- .../apache/james/mime4j/BodyDescriptor.java | 820 +-- .../james/mime4j/CloseShieldInputStream.java | 258 +- .../apache/james/mime4j/ContentHandler.java | 354 +- .../mime4j/EOLConvertingInputStream.java | 216 +- .../james/mime4j/MimeBoundaryInputStream.java | 368 +- .../apache/james/mime4j/MimeStreamParser.java | 640 +- .../apache/james/mime4j/RootInputStream.java | 222 +- .../james/mime4j/SimpleContentHandler.java | 200 +- .../mime4j/decoder/Base64InputStream.java | 292 +- .../james/mime4j/decoder/ByteQueue.java | 124 +- .../james/mime4j/decoder/DecoderUtil.java | 554 +- .../decoder/QuotedPrintableInputStream.java | 454 +- .../decoder/UnboundedFifoByteBuffer.java | 542 +- .../james/mime4j/field/AddressListField.java | 126 +- .../field/ContentTransferEncodingField.java | 176 +- .../james/mime4j/field/ContentTypeField.java | 512 +- .../james/mime4j/field/DateTimeField.java | 130 +- .../mime4j/field/DefaultFieldParser.java | 90 +- .../mime4j/field/DelegatingFieldParser.java | 94 +- src/org/apache/james/mime4j/field/Field.java | 384 +- .../james/mime4j/field/FieldParser.java | 42 +- .../james/mime4j/field/MailboxField.java | 136 +- .../james/mime4j/field/MailboxListField.java | 130 +- .../james/mime4j/field/UnstructuredField.java | 98 +- .../james/mime4j/field/address/Address.java | 104 +- .../mime4j/field/address/AddressList.java | 280 +- .../james/mime4j/field/address/Builder.java | 488 +- .../mime4j/field/address/DomainList.java | 152 +- .../james/mime4j/field/address/Group.java | 146 +- .../james/mime4j/field/address/Mailbox.java | 238 +- .../mime4j/field/address/MailboxList.java | 142 +- .../mime4j/field/address/NamedMailbox.java | 140 +- .../mime4j/field/address/parser/BaseNode.java | 58 +- .../james/mime4j/field/datetime/DateTime.java | 254 +- .../james/mime4j/message/AbstractBody.java | 94 +- .../james/mime4j/message/BinaryBody.java | 84 +- src/org/apache/james/mime4j/message/Body.java | 108 +- .../apache/james/mime4j/message/BodyPart.java | 84 +- .../apache/james/mime4j/message/Entity.java | 340 +- .../apache/james/mime4j/message/Header.java | 310 +- .../mime4j/message/MemoryBinaryBody.java | 180 +- .../james/mime4j/message/MemoryTextBody.java | 232 +- .../apache/james/mime4j/message/Message.java | 512 +- .../james/mime4j/message/Multipart.java | 406 +- .../mime4j/message/TempFileBinaryBody.java | 178 +- .../mime4j/message/TempFileTextBody.java | 230 +- .../apache/james/mime4j/message/TextBody.java | 84 +- .../apache/james/mime4j/util/CharsetUtil.java | 2490 +++---- .../james/mime4j/util/PartialInputStream.java | 126 +- .../mime4j/util/PositionInputStream.java | 174 +- .../james/mime4j/util/SimpleTempStorage.java | 472 +- .../apache/james/mime4j/util/TempFile.java | 168 +- .../apache/james/mime4j/util/TempPath.java | 146 +- .../apache/james/mime4j/util/TempStorage.java | 140 +- 152 files changed, 29771 insertions(+), 29771 deletions(-) diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 8723c2d2a4..d26789ddd0 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -1,781 +1,781 @@ - - - K-9 Mail - K-9 Mail BETA - Google, das K-9-Team und viele Weitere. - Autoren: %s - http://code.google.com/p/k9mail/wiki/ReleaseNotes - Versionsinformationen: %s - http://code.google.com/p/k9mail/ - - Anlagen der Nachricht lesen - Dieser Anwendung erlauben die Anlagen Ihrer Nachrichten zu lesen. - - Über %s - Ihre Konten - Erweitert - %s - - %s:%s - - Verfassen - Debug - Ordner wählen - Farbe wählen - - %s%s%s - - \u0020[%d] - \u0020(Lade %s:%s%s) - \u0020(Sende %s%s) - \u0020(Verarbeite %s:%s%s) - \u0020%s/%s - - \u0020(Nächstes Laden @ %s) - \u0020(Laden aus) - - - Weiter - OK - Abbrechen - Senden - Auswählen - Abwählen - Antworten - Allen antworten - Löschen - Sichern - Spam - Ordner leeren - Weiterleiten - Verschieben - Fortfahren - Fertig - Entfernen - Verwerfen - Als Entwurf speichern - Erneut versuchen - Aktualisieren - Nachrichten abrufen - Nachricht senden - Ordnerliste - Ordnerliste aktualisieren - Alle als gelesen markieren - Konto hinzufügen - Verfassen - Suchen - Suchergebnisse - Einstellungen - Öffnen - Kontoeinstellungen - Ordnereinstellungen - Konto entfernen - Ausstehende Aktionen abbrechen (Warnung!) - - Konten - - - Lesen - Als gelesen markieren - Weiterleiten (Alternativ) - Absender auswählen - - Alle Nachrichten als gelesen markieren - Alle Nachrichten in \'%s\' als gelesen markieren? (inklusive Nachrichten in diesem Ordner, die nicht von K-9 dargestellt werden) - - Als wichtig markieren - Wichtig-Markierung entfernen - Kopieren - - Als ungelesen markieren - Verschiebe nach - Ordner - Details anzeigen/verbergen - CC/BCC hinzufügen - Betreff bearbeiten - Anlage hinzufügen - Anlage hinzufügen (Bild) - Anlage hinzufügen (Video) - Einstellungen ausgeben (Dump) - Papierkorb leeren - Bereinigen (Expunge) - Sortierung wählen - Sortierung umkehren - Info - - Einstellungen - Konto-Optionen - Ordner-Optionen - - (Kein Betreff) - - Kein Absender - Lade - (Lade %s%s) - Lade Nachrichten\u2026 - Verbindungsfehler - Nachricht nicht gefunden - Fehler - Sende - - Ladevorgang erneut starten - - Bis zu - %d weitere abrufen - - GB - MB - KB - B - - - Konto \"%s\" von - %s - auf - %s - komprimiert - - - Komprimiere Konto \"%s\" - Bereinige Konto \"%s\" - - Neue Nachricht - Neue Nachricht von %s - %d Ungelesen (%s) - %d neue Nachricht(en) (%s) - in %d Konten - Nachricht nicht gesendet - - Auf neue Nachrichten prüfen: %s:%s - Auf neue Nachrichten prüfen - Sende Nachricht: %s - Sende Nachricht - : - - Posteingang - Postausgang - - Entwürfe - Papierkorb - Gesendet - Archiv - Spam - - - %s (Postausgang) - %s (Entwürfe) - %s (Papierkorb) - %s (Gesendet) - %s (Archiv) - %s (Spam) - - Fehler beim Senden von Nachrichten - Sehen Sie bitte im Ordner %s für Details nach. - K-9 hat beim Senden einiger Ihrer Nachrichten ein Problem festgestellt. - Hierbei kann K-9 aber nicht feststellen, ob die Nachricht gesendet wurde oder nicht. Die Empfänger haben möglicherweise schon Kopien der Nachricht erhalten. - \u000a\u000aDie Nachrichten, bei denen dieses Problem auftrat, sind in ihrem Postausgang markiert. Wenn Sie die Markierung entfernen, wird K-9 versuchen die Nachricht erneut zu versenden. - Durch langes Drücken auf den Postausgang erscheint ein Menü, bei dem mit "Nachricht senden" ein erneuter Sendeversuch gestartet werden kann.\u000A\u000a - Der Ordner %s enthält möglicherweise Fehlermeldungen mit Details zu diesem Problem. - - K-9-Alarm - Synchronisierung und Senden von Nachrichten aufgrund von fehlender Netzwerkverbindung eingestellt. - - Keine weiteren Nachrichten - - -Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung für Android und basiert auf Androids Standard-E-Mail-Programm. -\n -\n\nVerbesserte Funktionen in K-9: -\n * Laden von Nachrichten durch IMAP IDLE -\n * Höhere Performance -\n * E-Mail-Signaturen -\n * Automatische Blindkopie an sich selbst -\n * Ordner-Abonnements -\n * Synchronisation aller Ordner -\n * Konfiguration einer Antwort-Adresse -\n * Tastatur-Shortcuts -\n * Verbesserte IMAP-Unterstützung -\n * Speichern von Anlagen auf SD-Karte -\n * Papierkorb leeren -\n * Sortieren der Nachrichten -\n * ...und viele mehr -\n -\nBitte beachten Sie, dass K-9, wie viele andere E-Mail-Anwendungen auch, die meisten kostenlosen Hotmail-Accounts nicht unterstützt. Zudem gibt es einige Probleme mit Microsoft Exchange-Servern. -\n -\nUm Fehler zu melden, neue Funktionen vorzuschlagen oder Fragen zu stellen, besuchen Sie die Homepage unter -\nhttp://k9mail.googlecode.com/. - - - Version: %s - Debug-Aufzeichnungen einschalten - Zeichnet zusätzliche Diagnose-Informationen auf - Vertrauliche Informationen aufzeichnen - Passwörter in die Debug-Aufzeichnungen aufnehmen - - K-9 Mail for Android - - Alle Nachrichten - Neue Nachrichten aller Konten - Globaler Posteingang - - %s:%s - %s - %s - %s - %s - Weitere Nachrichten abrufen - An: %s - Löschen - Gelesen - Nicht gelesen - Wichtig - Nicht wichtig - - An - CC - BCC - Betreff - Nachrichtentext - \n\n-------- Original-Nachricht --------\nBetreff: %s\nVon: %s\nAn: %s\nCC: %s\n\n - \n\n%s schrieb:\n\n - Zitierter Text - Sie müssen mindestens einen Empfänger wählen. - Einige Anlagen wurden nicht heruntergeladen. Sie werden automatisch heruntergeladen, bevor diese Nachricht gesendet wird. - Einige Anlagen können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden. - - Von: %s <%s> - An: - CC: - Öffnen - Speichern - \u25BC - \u25B2 - Sichern - Verschieben - Spam - dd. MMM yyyy HH:mm - Anlage auf SD-Karte gespeichert als %s. - Anlage konnte nicht auf SD-Karte gespeichert werden. - Wählen Sie \"Bilder anzeigen\", um eingebettete Bilder abzurufen. - Bilder anzeigen - Lade Anlage. - Es wurde kein Anzeigeprogramm für %s gefunden. - - Ordner - Neuer Ordner - - Neuer Ordnername - - (Laden) - - Nachricht kopiert. - Nachricht verschoben. - Nachricht gelöscht. - Nachricht verworfen. - Nachricht als Entwurf gespeichert. - Nachricht konnte nicht gelöscht werden. - - Über %s - Version: %s - - Wichtige Nachrichten - Sterne weisen auf wichtige Nachrichten hin. - Mehrfachauswahl - Immer Mehrfachauswahl-Checkboxen anzeigen - Berührungsfreundliche Ansicht - - E-Mail-Konto einrichten - E-Mail-Adresse Ihres Kontos: - (Sie können %d weitere Konten hinzufügen.) - E-Mail-Adresse - %s ist keine gültige E-Mail-Adresse. - %s existiert bereits. - Passwort - Standardmäßig Nachrichten von diesem Konto aus senden. - Manuelle Einrichtung - - - Abrufen von Konto-Informationen\u2026 - Prüfe Einstellungen des Empfangsservers\u2026 - Prüfe Einstellungen des Ausgangsservers\u2026 - Vorgang wird abgeschlossen\u2026 - Breche ab\u2026 - - Fast fertig! - Ihr Konto ist eingerichtet, eine Test-E-Mail wurde versandt! - Kontoname (optional): - Ihr Name (Anzeige bei ausgehenden Nachrichten): - - Ihr Konto ist eingerichtet!\n\nLade Nachrichten\u2026 - - Neues E-Mail-Konto hinzufügen - Art des Kontos? - POP3-Konto - IMAP-Konto - WebDAV(Exchange)-Konto - - Einstellungen für Posteingangsserver - Benutzername - Passwort - POP3-Server - IMAP-Server - WebDAV(Exchange)-Server - Port - Sicherheitstyp - Authentifizierungstyp - Keine Verschlüsselung - SSL (falls verfügbar) - SSL (immer) - TLS (falls verfügbar) - TLS (immer) - - Aktion bei Löschen von Nachrichten: - Nie von Server löschen - Nach 7 Tagen löschen - Auch auf Server löschen - Auf Server als gelesen markieren - - Komprimierung bei Netzwerk verwenden: - Mobil - WLAN - Anderes - - Ordner bereinigen (Expunge) - Sofort nach Verschieben oder Kopieren - Bei jedem Abrufen - Nur manuell - - IMAP-Verzeichnispräfix - (Automatisch mit Hilfe von NAMESPACE falls verfügbar) - - Ordner für Entwürfe - Ordner für gesendete Objekte - Ordner für gelöschte Objekte - Ordner für archivierte Objekte - Ordner für Spam - Ordner für ausgehende Objekte - - Autom. Startordner - - WebDAV(Exchange)-Pfad - Optional - - Authentifizierungspfad - Optional - Mailbox-Pfad - Optional - - Einstellungen für Postausgangsserver - SMTP-Server - Port - Sicherheitstyp - Keine Verschlüsselung - SSL - TLS (falls verfügbar) - TLS (immer) - Anmeldung erforderlich. - Benutzername - Passwort - Authentifizierungstyp - - Benutzername & Passwort - Benutzername - Kennwort - POP vor SMTP - IMAP vor SMTP - WebDAV(Exchange) vor SMTP - - Ungültige Einstellung: %s - - Kontoeinstellungen - - Komprimieren - Alle Daten löschen (Achtung!) - - Häufigkeit der E-Mail-Abfrage - - Nie (nur manuell) - Jede Minute - Alle 5 Minuten - Alle 10 Minuten - Alle 15 Minuten - Alle 30 Minuten - Jede Stunde - Alle 2 Stunden - Alle 3 Stunden - Alle 6 Stunden - Alle 12 Stunden - Alle 24 Stunden - - Push-Mail für dieses Konto aktivieren - Neue Nachrichten werden nach dem Eintreffen umgehend abgerufen, falls Ihr Server dies unterstützt. Diese Einstellung kann zur Reduzierung der Laufzeit führen oder diese verbessern. - - Standardmäßig Nachrichten von diesem Konto aus senden. - Benachrichtigen, wenn Nachrichten eingehen. - Benachrichtigen, wenn Nachrichten synchronisiert werden. - - - Anzahl sichtbarer Nachrichten - 10 Nachrichten - 25 Nachrichten - 50 Nachrichten - 100 Nachrichten - 250 Nachrichten - 500 Nachrichten - 1000 Nachrichten - - Nachrichten, die nicht mit dem Server synchronisiert wurden, können nicht kopiert oder verschoben werden. - - Setup konnte nicht abgeschlossen werden - Benutzername oder Kennwort fehlerhaft.\n(%s) - Aufbau einer gesicherten Verbindung zum Server nicht möglich.\n(%s) - Verbindung zum Server nicht möglich.\n(%s) - Details bearbeiten - Weiter - - Kontoeinstellungen - Standardkonto - Standardkonto - Standardmäßig Nachrichten von diesem Konto aus senden. - Benachr. bei neuer Nachricht - Benachr. bei Synchronisation - Ihre E-Mail-Adresse - Zeigt eine Benachrichtigung in der Statuszeile, wenn eine neue Nachricht empfangen wird. - Zeigt eine Benachrichtigung in der Statuszeile, während Nachrichten empfangen werden. - Globalen Posteingang anzeigen - Benachr. für eigene Nachrichten - Benachrichtigungen für Nachrichten, die eine ihrer eigenen E-Mail-Adressen als Absender tragen. - - Navigations-Scrollbuttons - Nie - Nur wenn Tastatur aktiv - Immer - - Verschiebungsbuttons - Zeige Sichern, Verschieben und Spam Buttons. - Verschiebungs-Scrollbuttons - - Nachricht verfassen - Ordner synchronisieren - Ordner - Liste der Nachrichten - Anzeige der Nachricht - - Häufigkeit der E-Mail-Abfrage - Häufigkeit der Abfrage für Nebenordner - - Farbe des Kontos - Wählen Sie die Farbe, in der das Konto in der Konten- und Ordnerliste dargestellt wird - - Anzahl anzuzeigender Nachrichten - - Ordner anzeigen - Alle - Nur Hauptordner - Haupt- und Nebenordner - Alle außer Nebenordner - - Abzufragende Ordner - Alle - Nur Hauptordner - Haupt- und Nebenordner - Alle außer Nebenordner - Keine - - Ordner per Push abfragen - Alle - Nur Hauptordner - Haupt- und Nebenordner - Alle außer Nebenordner - Keine - - Zielordner für Kopieren/Verschieben - Alle - Nur Hauptordner - Haupt- und Nebenordner - Alle außer Nebenordner - - Ordner-Einstellungen - - Gruppe \'Oben anzeigen\' - Oben in der Ordnerliste anzeigen - - Anzeigeklasse - Keine - Hauptordner - Nebenordner - - Synchronisationsklasse - Keine - Hauptordner - Nebenordner - Wie Anzeigeklasse - - Push-Klasse - Keine - Hauptordner - Nebenordner - Wie Synchronisationsklasse - - Einstellungen Posteingang - Einstellungen für Posteingangsserver bearbeiten. - Einstellungen Postausgang - Einstellungen für Postausgangsserver bearbeiten. - Eine weiteres Konto hinzufügen - Kontoname - Ihr Name - Benachrichtigungseinstellungen - Klingeln bei eingehender Nachricht - Vibration - Vibration bei eingehender Nachricht - Klingelton - Servereinstellungen - - Optionen für das Verfassen von Nachrichten - Verfassen von Nachrichten - Standard-Absender, Blindkopie-Empfänger und Signatur konfigurieren - - Identitäten verwalten - Alternative Absenderadressen und Signaturen konfigurieren - - Identitäten verwalten - - Identität berarbeiten - - Identität bearbeiten - Neue Identität erstellen - - Alle Nachrichten mit BCC an - Dieser Adresse eine Kopie aller ausgehenden Nachrichten zustellen - - Bearbeiten - Nach oben verschieben - Nach unten verschieben - Ganz nach oben / Standard - Entfernen - - Name der Identität - (Optional) - Ihr Name - (Optional) - E-Mail-Adresse - (Erforderlich) - Signatur - (Optional) - - Signatur verwenden - Signatur - Signatur an jede Nachricht anfügen - - -- \nDiese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet. - Standard-Identität - Identität auswählen - Identität auswählen - - Gehen Sie zu \"Kontoeinstellungen\" -> \"Identitäten verwalten\" um Identitäten zu erstellen - Sie können Ihre einzige Identität nicht löschen - Sie können keine Identität ohne E-Mail-Adresse verwenden - Die Wahl Ihrer Identität und Änderungen an der Signatur werden bei einem Entwurf nicht gespeichert - - Älteste Nachrichten zuerst - Neueste Nachrichten zuerst - Alphabetisch nach Absender - Umgekehrt-Alphabetisch nach Absender - Alphabetisch nach Betreff - Umgekehrt-Alphabetisch nach Betreff - Markierte Nachrichten zuerst - Nicht markierte Nachrichten zuerst - Ungelesene Nachrichten zuerst - Gelesene Nachrichten zuerst - Nachrichten mit Anlagen zuerst - Nachrichten ohne Anlagen zuerst - - Sortieren nach... - Datum - Absender - Betreff - Wichtigkeit - Gelesen/Ungelesen - Anlage - %s - - Entfernen - Das Konto \"%s\" wird gelöscht. - - Kostenlose Yahoo!-Mail-Konten funktionieren nur über die T-Mobile-Funknetze. Anwender von \"Yahoo! Mail Plus\" sollten die POP-Einstellungen manuell vornehmen. - Nur einige \"Plus\"-Konten mit POP-Zugriff - ermöglichen diesem Programm die Verbindung. Wenn Sie sich nicht mit - der richtigen E-Mail-Adresse und dem richtigen Kennwort anmelden können, haben Sie möglicherweise die Gebühren für das - \"Plus\"-Konto nicht bezahlt. Starten Sie den Webbrowser, um auf - diese Mailkonten zuzugreifen. - - Ungültiges Zertifikat - Zertifikat akzeptieren - Zertifikat ablehnen - - Del (or D) - Löschen\u000AR - - Antworten\u000AA - Allen antworten\u000AF - Weiterleiten\u000AJ or P - Vorherige - Nachricht\u000AK, N - Nächste Nachricht\u000AM - Verschieben\u000AY - Kopieren\u000AZ - Heraus-Zoomen\u000AShift-Z - - Hinein-Zoomen\u000aG - Markieren - Del (or D) - Löschen\u000AR - - Antworten\u000AA - Allen Antworten\u000AC - Verfassen\u000AF - Weiterleiten\u000aM - Verschieben\u000AY - Kopieren\u000AG - Markieren\u000AO - Sortierung\u000AI - Sortierungsreihenfolge\u000AQ - - zurück zu den Konten\u000AS - Auswählen/abwählen - - - 1 - Nur Hauptordner anzeigen\u000A - 2 - Haupt- und Nebenordner anzeigen\u000A - 3 - Alle außer Nebenordner anzeigen\u000A - 4 - Alle Ordner anzeigen\u000A - Q - Zurück zu den Konten\u000A - S - Kontoeinstellungen bearbeiten - - - Ordner - Alle Ordner anzeigen - Nur Hauptordner anzeigen - Haupt- und Nebenordner anzeigen - Alle außer Nebenordner anzeigen - - Position der Signatur - Vor zitierter Nachricht - Nach zitierter Nachricht - Dunkel (Dark) - Hell (Light) - Globale Einstellungen - Debugging - Synchronisierung - Liste der Konten - Liste der Nachrichten - Theme - Sprache - - Systemvorgabe - - Hintergrund-Synchronisierung - Nie - Immer - Wenn \'Hintergrunddaten\' aktiviert ist - - Keine Nachricht ausgewählt - - Datumsformat - - SHORT - MEDIUM - dd.MMM yyyy - yyyy-MM-dd - - Mehrfachauswahl - Ausgewählte löschen - Ausgewählte als gelesen markieren - Ausgewählte als ungelesen markieren - Ausgewählte als wichtig markieren - Markierung bei Ausgewählten entfernen - Ausgewählte sichern - Ausgewählte als Spam markieren - Ausgewählte verschieben - Ausgewählte kopieren - Stern-Modus - Auswahl-Modus - Normaler Modus - Alle auswählen - Alle abwählen - - Max. Ordner mit Push abrufen - 10 Ordner - 25 Ordner - 50 Ordner - 100 Ordner - 250 Ordner - 500 Ordner - 1000 Ordner - - Animationen - Aufwändige visuelle Effekte benutzen - Gesten zulassen - Gesten für vorherige/nächste Nachricht aktivieren - - - Konto-Größe anzeigen - Deaktivieren für schnellere Anzeige - - Suchergebnisse zählen - Deaktivieren für schnellere Anzeige - - %s %s - - Wichtig - - Ungelesen - - Alle Nachrichten - Alle Nachrichten in durchsuchbaren Ordnern - - Gemeinsamer Posteingang - Alle Nachrichten aus integrierten Ordnern - - Umschlag oder Stern anklicken für ungelesene oder wichtige Nachrichten - - Integration in gemeinsamen Posteingang - Ungelesene Nachrichten im gemeinsamen Posteingang anzeigen - - Zu durchsuchende Ordner - Alle - Angezeigte - Keine - - K-9 Mail Fernsteuerung - Erlaubt dieser Anwendung den Ablauf und die Einstellungen von K-9 Mail zu kontrollieren. - - Schriftgröße - Schriftgrößen festlegen - - Konten-Liste - Konto-Name - Konto-Beschreibung - - Ordner-Liste - Ordner-Name - Ordner-Status - - Nachrichten-Liste - Betreff - Absender - Datum - - Nachrichtenanzeige - Absender - Empfänger (To) - Empfänger (CC) - Betreff - Zeit - Datum - Nachrichtentext - - Winzig - Sehr klein - Kleiner - Klein - Mittel - Groß - Größer - - Winzig - Kleiner - Normal - Größer - Riesig - - - Wählen Sie \"Einstellungen\" -> \"Galerie Work-Around aktivieren\" um Bilder oder Videos mithilfe der 3D-Galerie hinzufügen zu können. - - - Verwenden Sie \"Anlage hinzufügen (Bild)\" oder \"Anlage hinzufügen (Video)\" um Bilder oder Videos mit der 3D-Galerie hinzuzufügen. - - Verschiedenes - Galerie Work-Around aktivieren - Schaltflächen zum Hinzufügen von Bild- und Video-Anhängen anzeigen (um Fehler in 3D-Galerie zu umgehen) - - - + + + K-9 Mail + K-9 Mail BETA + Google, das K-9-Team und viele Weitere. + Autoren: %s + http://code.google.com/p/k9mail/wiki/ReleaseNotes + Versionsinformationen: %s + http://code.google.com/p/k9mail/ + + Anlagen der Nachricht lesen + Dieser Anwendung erlauben die Anlagen Ihrer Nachrichten zu lesen. + + Über %s + Ihre Konten + Erweitert + %s + + %s:%s + + Verfassen + Debug + Ordner wählen + Farbe wählen + + %s%s%s + + \u0020[%d] + \u0020(Lade %s:%s%s) + \u0020(Sende %s%s) + \u0020(Verarbeite %s:%s%s) + \u0020%s/%s + + \u0020(Nächstes Laden @ %s) + \u0020(Laden aus) + + + Weiter + OK + Abbrechen + Senden + Auswählen + Abwählen + Antworten + Allen antworten + Löschen + Sichern + Spam + Ordner leeren + Weiterleiten + Verschieben + Fortfahren + Fertig + Entfernen + Verwerfen + Als Entwurf speichern + Erneut versuchen + Aktualisieren + Nachrichten abrufen + Nachricht senden + Ordnerliste + Ordnerliste aktualisieren + Alle als gelesen markieren + Konto hinzufügen + Verfassen + Suchen + Suchergebnisse + Einstellungen + Öffnen + Kontoeinstellungen + Ordnereinstellungen + Konto entfernen + Ausstehende Aktionen abbrechen (Warnung!) + + Konten + + + Lesen + Als gelesen markieren + Weiterleiten (Alternativ) + Absender auswählen + + Alle Nachrichten als gelesen markieren + Alle Nachrichten in \'%s\' als gelesen markieren? (inklusive Nachrichten in diesem Ordner, die nicht von K-9 dargestellt werden) + + Als wichtig markieren + Wichtig-Markierung entfernen + Kopieren + + Als ungelesen markieren + Verschiebe nach + Ordner + Details anzeigen/verbergen + CC/BCC hinzufügen + Betreff bearbeiten + Anlage hinzufügen + Anlage hinzufügen (Bild) + Anlage hinzufügen (Video) + Einstellungen ausgeben (Dump) + Papierkorb leeren + Bereinigen (Expunge) + Sortierung wählen + Sortierung umkehren + Info + + Einstellungen + Konto-Optionen + Ordner-Optionen + + (Kein Betreff) + + Kein Absender + Lade + (Lade %s%s) + Lade Nachrichten\u2026 + Verbindungsfehler + Nachricht nicht gefunden + Fehler + Sende + + Ladevorgang erneut starten + + Bis zu + %d weitere abrufen + + GB + MB + KB + B + + + Konto \"%s\" von + %s + auf + %s + komprimiert + + + Komprimiere Konto \"%s\" + Bereinige Konto \"%s\" + + Neue Nachricht + Neue Nachricht von %s + %d Ungelesen (%s) + %d neue Nachricht(en) (%s) + in %d Konten + Nachricht nicht gesendet + + Auf neue Nachrichten prüfen: %s:%s + Auf neue Nachrichten prüfen + Sende Nachricht: %s + Sende Nachricht + : + + Posteingang + Postausgang + + Entwürfe + Papierkorb + Gesendet + Archiv + Spam + + + %s (Postausgang) + %s (Entwürfe) + %s (Papierkorb) + %s (Gesendet) + %s (Archiv) + %s (Spam) + + Fehler beim Senden von Nachrichten + Sehen Sie bitte im Ordner %s für Details nach. + K-9 hat beim Senden einiger Ihrer Nachrichten ein Problem festgestellt. + Hierbei kann K-9 aber nicht feststellen, ob die Nachricht gesendet wurde oder nicht. Die Empfänger haben möglicherweise schon Kopien der Nachricht erhalten. + \u000a\u000aDie Nachrichten, bei denen dieses Problem auftrat, sind in ihrem Postausgang markiert. Wenn Sie die Markierung entfernen, wird K-9 versuchen die Nachricht erneut zu versenden. + Durch langes Drücken auf den Postausgang erscheint ein Menü, bei dem mit "Nachricht senden" ein erneuter Sendeversuch gestartet werden kann.\u000A\u000a + Der Ordner %s enthält möglicherweise Fehlermeldungen mit Details zu diesem Problem. + + K-9-Alarm + Synchronisierung und Senden von Nachrichten aufgrund von fehlender Netzwerkverbindung eingestellt. + + Keine weiteren Nachrichten + + +Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung für Android und basiert auf Androids Standard-E-Mail-Programm. +\n +\n\nVerbesserte Funktionen in K-9: +\n * Laden von Nachrichten durch IMAP IDLE +\n * Höhere Performance +\n * E-Mail-Signaturen +\n * Automatische Blindkopie an sich selbst +\n * Ordner-Abonnements +\n * Synchronisation aller Ordner +\n * Konfiguration einer Antwort-Adresse +\n * Tastatur-Shortcuts +\n * Verbesserte IMAP-Unterstützung +\n * Speichern von Anlagen auf SD-Karte +\n * Papierkorb leeren +\n * Sortieren der Nachrichten +\n * ...und viele mehr +\n +\nBitte beachten Sie, dass K-9, wie viele andere E-Mail-Anwendungen auch, die meisten kostenlosen Hotmail-Accounts nicht unterstützt. Zudem gibt es einige Probleme mit Microsoft Exchange-Servern. +\n +\nUm Fehler zu melden, neue Funktionen vorzuschlagen oder Fragen zu stellen, besuchen Sie die Homepage unter +\nhttp://k9mail.googlecode.com/. + + + Version: %s + Debug-Aufzeichnungen einschalten + Zeichnet zusätzliche Diagnose-Informationen auf + Vertrauliche Informationen aufzeichnen + Passwörter in die Debug-Aufzeichnungen aufnehmen + + K-9 Mail for Android + + Alle Nachrichten + Neue Nachrichten aller Konten + Globaler Posteingang + + %s:%s + %s + %s + %s + %s + Weitere Nachrichten abrufen + An: %s + Löschen + Gelesen + Nicht gelesen + Wichtig + Nicht wichtig + + An + CC + BCC + Betreff + Nachrichtentext + \n\n-------- Original-Nachricht --------\nBetreff: %s\nVon: %s\nAn: %s\nCC: %s\n\n + \n\n%s schrieb:\n\n + Zitierter Text + Sie müssen mindestens einen Empfänger wählen. + Einige Anlagen wurden nicht heruntergeladen. Sie werden automatisch heruntergeladen, bevor diese Nachricht gesendet wird. + Einige Anlagen können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden. + + Von: %s <%s> + An: + CC: + Öffnen + Speichern + \u25BC + \u25B2 + Sichern + Verschieben + Spam + dd. MMM yyyy HH:mm + Anlage auf SD-Karte gespeichert als %s. + Anlage konnte nicht auf SD-Karte gespeichert werden. + Wählen Sie \"Bilder anzeigen\", um eingebettete Bilder abzurufen. + Bilder anzeigen + Lade Anlage. + Es wurde kein Anzeigeprogramm für %s gefunden. + + Ordner + Neuer Ordner + + Neuer Ordnername + + (Laden) + + Nachricht kopiert. + Nachricht verschoben. + Nachricht gelöscht. + Nachricht verworfen. + Nachricht als Entwurf gespeichert. + Nachricht konnte nicht gelöscht werden. + + Über %s + Version: %s + + Wichtige Nachrichten + Sterne weisen auf wichtige Nachrichten hin. + Mehrfachauswahl + Immer Mehrfachauswahl-Checkboxen anzeigen + Berührungsfreundliche Ansicht + + E-Mail-Konto einrichten + E-Mail-Adresse Ihres Kontos: + (Sie können %d weitere Konten hinzufügen.) + E-Mail-Adresse + %s ist keine gültige E-Mail-Adresse. + %s existiert bereits. + Passwort + Standardmäßig Nachrichten von diesem Konto aus senden. + Manuelle Einrichtung + + + Abrufen von Konto-Informationen\u2026 + Prüfe Einstellungen des Empfangsservers\u2026 + Prüfe Einstellungen des Ausgangsservers\u2026 + Vorgang wird abgeschlossen\u2026 + Breche ab\u2026 + + Fast fertig! + Ihr Konto ist eingerichtet, eine Test-E-Mail wurde versandt! + Kontoname (optional): + Ihr Name (Anzeige bei ausgehenden Nachrichten): + + Ihr Konto ist eingerichtet!\n\nLade Nachrichten\u2026 + + Neues E-Mail-Konto hinzufügen + Art des Kontos? + POP3-Konto + IMAP-Konto + WebDAV(Exchange)-Konto + + Einstellungen für Posteingangsserver + Benutzername + Passwort + POP3-Server + IMAP-Server + WebDAV(Exchange)-Server + Port + Sicherheitstyp + Authentifizierungstyp + Keine Verschlüsselung + SSL (falls verfügbar) + SSL (immer) + TLS (falls verfügbar) + TLS (immer) + + Aktion bei Löschen von Nachrichten: + Nie von Server löschen + Nach 7 Tagen löschen + Auch auf Server löschen + Auf Server als gelesen markieren + + Komprimierung bei Netzwerk verwenden: + Mobil + WLAN + Anderes + + Ordner bereinigen (Expunge) + Sofort nach Verschieben oder Kopieren + Bei jedem Abrufen + Nur manuell + + IMAP-Verzeichnispräfix + (Automatisch mit Hilfe von NAMESPACE falls verfügbar) + + Ordner für Entwürfe + Ordner für gesendete Objekte + Ordner für gelöschte Objekte + Ordner für archivierte Objekte + Ordner für Spam + Ordner für ausgehende Objekte + + Autom. Startordner + + WebDAV(Exchange)-Pfad + Optional + + Authentifizierungspfad + Optional + Mailbox-Pfad + Optional + + Einstellungen für Postausgangsserver + SMTP-Server + Port + Sicherheitstyp + Keine Verschlüsselung + SSL + TLS (falls verfügbar) + TLS (immer) + Anmeldung erforderlich. + Benutzername + Passwort + Authentifizierungstyp + + Benutzername & Passwort + Benutzername + Kennwort + POP vor SMTP + IMAP vor SMTP + WebDAV(Exchange) vor SMTP + + Ungültige Einstellung: %s + + Kontoeinstellungen + + Komprimieren + Alle Daten löschen (Achtung!) + + Häufigkeit der E-Mail-Abfrage + + Nie (nur manuell) + Jede Minute + Alle 5 Minuten + Alle 10 Minuten + Alle 15 Minuten + Alle 30 Minuten + Jede Stunde + Alle 2 Stunden + Alle 3 Stunden + Alle 6 Stunden + Alle 12 Stunden + Alle 24 Stunden + + Push-Mail für dieses Konto aktivieren + Neue Nachrichten werden nach dem Eintreffen umgehend abgerufen, falls Ihr Server dies unterstützt. Diese Einstellung kann zur Reduzierung der Laufzeit führen oder diese verbessern. + + Standardmäßig Nachrichten von diesem Konto aus senden. + Benachrichtigen, wenn Nachrichten eingehen. + Benachrichtigen, wenn Nachrichten synchronisiert werden. + + + Anzahl sichtbarer Nachrichten + 10 Nachrichten + 25 Nachrichten + 50 Nachrichten + 100 Nachrichten + 250 Nachrichten + 500 Nachrichten + 1000 Nachrichten + + Nachrichten, die nicht mit dem Server synchronisiert wurden, können nicht kopiert oder verschoben werden. + + Setup konnte nicht abgeschlossen werden + Benutzername oder Kennwort fehlerhaft.\n(%s) + Aufbau einer gesicherten Verbindung zum Server nicht möglich.\n(%s) + Verbindung zum Server nicht möglich.\n(%s) + Details bearbeiten + Weiter + + Kontoeinstellungen + Standardkonto + Standardkonto + Standardmäßig Nachrichten von diesem Konto aus senden. + Benachr. bei neuer Nachricht + Benachr. bei Synchronisation + Ihre E-Mail-Adresse + Zeigt eine Benachrichtigung in der Statuszeile, wenn eine neue Nachricht empfangen wird. + Zeigt eine Benachrichtigung in der Statuszeile, während Nachrichten empfangen werden. + Globalen Posteingang anzeigen + Benachr. für eigene Nachrichten + Benachrichtigungen für Nachrichten, die eine ihrer eigenen E-Mail-Adressen als Absender tragen. + + Navigations-Scrollbuttons + Nie + Nur wenn Tastatur aktiv + Immer + + Verschiebungsbuttons + Zeige Sichern, Verschieben und Spam Buttons. + Verschiebungs-Scrollbuttons + + Nachricht verfassen + Ordner synchronisieren + Ordner + Liste der Nachrichten + Anzeige der Nachricht + + Häufigkeit der E-Mail-Abfrage + Häufigkeit der Abfrage für Nebenordner + + Farbe des Kontos + Wählen Sie die Farbe, in der das Konto in der Konten- und Ordnerliste dargestellt wird + + Anzahl anzuzeigender Nachrichten + + Ordner anzeigen + Alle + Nur Hauptordner + Haupt- und Nebenordner + Alle außer Nebenordner + + Abzufragende Ordner + Alle + Nur Hauptordner + Haupt- und Nebenordner + Alle außer Nebenordner + Keine + + Ordner per Push abfragen + Alle + Nur Hauptordner + Haupt- und Nebenordner + Alle außer Nebenordner + Keine + + Zielordner für Kopieren/Verschieben + Alle + Nur Hauptordner + Haupt- und Nebenordner + Alle außer Nebenordner + + Ordner-Einstellungen + + Gruppe \'Oben anzeigen\' + Oben in der Ordnerliste anzeigen + + Anzeigeklasse + Keine + Hauptordner + Nebenordner + + Synchronisationsklasse + Keine + Hauptordner + Nebenordner + Wie Anzeigeklasse + + Push-Klasse + Keine + Hauptordner + Nebenordner + Wie Synchronisationsklasse + + Einstellungen Posteingang + Einstellungen für Posteingangsserver bearbeiten. + Einstellungen Postausgang + Einstellungen für Postausgangsserver bearbeiten. + Eine weiteres Konto hinzufügen + Kontoname + Ihr Name + Benachrichtigungseinstellungen + Klingeln bei eingehender Nachricht + Vibration + Vibration bei eingehender Nachricht + Klingelton + Servereinstellungen + + Optionen für das Verfassen von Nachrichten + Verfassen von Nachrichten + Standard-Absender, Blindkopie-Empfänger und Signatur konfigurieren + + Identitäten verwalten + Alternative Absenderadressen und Signaturen konfigurieren + + Identitäten verwalten + + Identität berarbeiten + + Identität bearbeiten + Neue Identität erstellen + + Alle Nachrichten mit BCC an + Dieser Adresse eine Kopie aller ausgehenden Nachrichten zustellen + + Bearbeiten + Nach oben verschieben + Nach unten verschieben + Ganz nach oben / Standard + Entfernen + + Name der Identität + (Optional) + Ihr Name + (Optional) + E-Mail-Adresse + (Erforderlich) + Signatur + (Optional) + + Signatur verwenden + Signatur + Signatur an jede Nachricht anfügen + + -- \nDiese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet. + Standard-Identität + Identität auswählen + Identität auswählen + + Gehen Sie zu \"Kontoeinstellungen\" -> \"Identitäten verwalten\" um Identitäten zu erstellen + Sie können Ihre einzige Identität nicht löschen + Sie können keine Identität ohne E-Mail-Adresse verwenden + Die Wahl Ihrer Identität und Änderungen an der Signatur werden bei einem Entwurf nicht gespeichert + + Älteste Nachrichten zuerst + Neueste Nachrichten zuerst + Alphabetisch nach Absender + Umgekehrt-Alphabetisch nach Absender + Alphabetisch nach Betreff + Umgekehrt-Alphabetisch nach Betreff + Markierte Nachrichten zuerst + Nicht markierte Nachrichten zuerst + Ungelesene Nachrichten zuerst + Gelesene Nachrichten zuerst + Nachrichten mit Anlagen zuerst + Nachrichten ohne Anlagen zuerst + + Sortieren nach... + Datum + Absender + Betreff + Wichtigkeit + Gelesen/Ungelesen + Anlage + %s + + Entfernen + Das Konto \"%s\" wird gelöscht. + + Kostenlose Yahoo!-Mail-Konten funktionieren nur über die T-Mobile-Funknetze. Anwender von \"Yahoo! Mail Plus\" sollten die POP-Einstellungen manuell vornehmen. + Nur einige \"Plus\"-Konten mit POP-Zugriff + ermöglichen diesem Programm die Verbindung. Wenn Sie sich nicht mit + der richtigen E-Mail-Adresse und dem richtigen Kennwort anmelden können, haben Sie möglicherweise die Gebühren für das + \"Plus\"-Konto nicht bezahlt. Starten Sie den Webbrowser, um auf + diese Mailkonten zuzugreifen. + + Ungültiges Zertifikat + Zertifikat akzeptieren + Zertifikat ablehnen + + Del (or D) - Löschen\u000AR - + Antworten\u000AA - Allen antworten\u000AF - Weiterleiten\u000AJ or P - Vorherige + Nachricht\u000AK, N - Nächste Nachricht\u000AM - Verschieben\u000AY - Kopieren\u000AZ - Heraus-Zoomen\u000AShift-Z - + Hinein-Zoomen\u000aG - Markieren + Del (or D) - Löschen\u000AR - + Antworten\u000AA - Allen Antworten\u000AC - Verfassen\u000AF - Weiterleiten\u000aM - Verschieben\u000AY - Kopieren\u000AG - Markieren\u000AO - Sortierung\u000AI - Sortierungsreihenfolge\u000AQ + - zurück zu den Konten\u000AS - Auswählen/abwählen + + + 1 - Nur Hauptordner anzeigen\u000A + 2 - Haupt- und Nebenordner anzeigen\u000A + 3 - Alle außer Nebenordner anzeigen\u000A + 4 - Alle Ordner anzeigen\u000A + Q - Zurück zu den Konten\u000A + S - Kontoeinstellungen bearbeiten + + + Ordner + Alle Ordner anzeigen + Nur Hauptordner anzeigen + Haupt- und Nebenordner anzeigen + Alle außer Nebenordner anzeigen + + Position der Signatur + Vor zitierter Nachricht + Nach zitierter Nachricht + Dunkel (Dark) + Hell (Light) + Globale Einstellungen + Debugging + Synchronisierung + Liste der Konten + Liste der Nachrichten + Theme + Sprache + + Systemvorgabe + + Hintergrund-Synchronisierung + Nie + Immer + Wenn \'Hintergrunddaten\' aktiviert ist + + Keine Nachricht ausgewählt + + Datumsformat + + SHORT + MEDIUM + dd.MMM yyyy + yyyy-MM-dd + + Mehrfachauswahl + Ausgewählte löschen + Ausgewählte als gelesen markieren + Ausgewählte als ungelesen markieren + Ausgewählte als wichtig markieren + Markierung bei Ausgewählten entfernen + Ausgewählte sichern + Ausgewählte als Spam markieren + Ausgewählte verschieben + Ausgewählte kopieren + Stern-Modus + Auswahl-Modus + Normaler Modus + Alle auswählen + Alle abwählen + + Max. Ordner mit Push abrufen + 10 Ordner + 25 Ordner + 50 Ordner + 100 Ordner + 250 Ordner + 500 Ordner + 1000 Ordner + + Animationen + Aufwändige visuelle Effekte benutzen + Gesten zulassen + Gesten für vorherige/nächste Nachricht aktivieren + + + Konto-Größe anzeigen + Deaktivieren für schnellere Anzeige + + Suchergebnisse zählen + Deaktivieren für schnellere Anzeige + + %s %s + - Wichtig + - Ungelesen + + Alle Nachrichten + Alle Nachrichten in durchsuchbaren Ordnern + + Gemeinsamer Posteingang + Alle Nachrichten aus integrierten Ordnern + + Umschlag oder Stern anklicken für ungelesene oder wichtige Nachrichten + + Integration in gemeinsamen Posteingang + Ungelesene Nachrichten im gemeinsamen Posteingang anzeigen + + Zu durchsuchende Ordner + Alle + Angezeigte + Keine + + K-9 Mail Fernsteuerung + Erlaubt dieser Anwendung den Ablauf und die Einstellungen von K-9 Mail zu kontrollieren. + + Schriftgröße + Schriftgrößen festlegen + + Konten-Liste + Konto-Name + Konto-Beschreibung + + Ordner-Liste + Ordner-Name + Ordner-Status + + Nachrichten-Liste + Betreff + Absender + Datum + + Nachrichtenanzeige + Absender + Empfänger (To) + Empfänger (CC) + Betreff + Zeit + Datum + Nachrichtentext + + Winzig + Sehr klein + Kleiner + Klein + Mittel + Groß + Größer + + Winzig + Kleiner + Normal + Größer + Riesig + + + Wählen Sie \"Einstellungen\" -> \"Galerie Work-Around aktivieren\" um Bilder oder Videos mithilfe der 3D-Galerie hinzufügen zu können. + + + Verwenden Sie \"Anlage hinzufügen (Bild)\" oder \"Anlage hinzufügen (Video)\" um Bilder oder Videos mit der 3D-Galerie hinzuzufügen. + + Verschiedenes + Galerie Work-Around aktivieren + Schaltflächen zum Hinzufügen von Bild- und Video-Anhängen anzeigen (um Fehler in 3D-Galerie zu umgehen) + + + diff --git a/src/com/beetstra/jutf7/Base64Util.java b/src/com/beetstra/jutf7/Base64Util.java index 6dffb32c5b..2f11cd88b8 100644 --- a/src/com/beetstra/jutf7/Base64Util.java +++ b/src/com/beetstra/jutf7/Base64Util.java @@ -1,117 +1,117 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -import java.util.Arrays; - -/** - *

- * Represent a base 64 mapping. The 64 characters used in the encoding can be - * specified, since modified-UTF-7 uses other characters than UTF-7 (',' instead - * of '/'). - *

- *

- * The exact type of the arguments and result values is adapted to the needs of - * the encoder and decoder, as opposed to following a strict interpretation of - * base 64. - *

- *

- * Base 64, as specified in RFC 2045, is an encoding used to encode bytes as - * characters. In (modified-)UTF-7 however, it is used to encode characters as - * bytes, using some intermediate steps: - *

- *
    - *
  1. Encode all characters as a 16-bit (UTF-16) integer value
  2. - *
  3. Write this as stream of bytes (most-significant first)
  4. - *
  5. Encode these bytes using (modified) base 64 encoding
  6. - *
  7. Write the thus formed stream of characters as a stream of bytes, using - * ASCII encoding
  8. - *
- * - * @author Jaap Beetstra - */ -class Base64Util { - private static final int ALPHABET_LENGTH = 64; - private final char[] alphabet; - private final int[] inverseAlphabet; - - /** - * Initializes the class with the specified encoding/decoding alphabet. - * - * @param alphabet - * @throws IllegalArgumentException if alphabet is not 64 characters long or - * contains characters which are not 7-bit ASCII - */ - Base64Util(final String alphabet) { - this.alphabet = alphabet.toCharArray(); - if (alphabet.length() != ALPHABET_LENGTH) - throw new IllegalArgumentException("alphabet has incorrect length (should be 64, not " - + alphabet.length() + ")"); - inverseAlphabet = new int[128]; - Arrays.fill(inverseAlphabet, -1); - for (int i = 0; i < this.alphabet.length; i++) { - final char ch = this.alphabet[i]; - if (ch >= 128) - throw new IllegalArgumentException("invalid character in alphabet: " + ch); - inverseAlphabet[ch] = i; - } - } - - /** - * Returns the integer value of the six bits represented by the specified - * character. - * - * @param ch The character, as a ASCII encoded byte - * @return The six bits, as an integer value, or -1 if the byte is not in - * the alphabet - */ - int getSextet(final byte ch) { - if (ch >= 128) - return -1; - return inverseAlphabet[ch]; - } - - /** - * Tells whether the alphabet contains the specified character. - * - * @param ch The character - * @return true if the alphabet contains ch, false otherwise - */ - boolean contains(final char ch) { - if (ch >= 128) - return false; - return inverseAlphabet[ch] >= 0; - } - - /** - * Encodes the six bit group as a character. - * - * @param sextet The six bit group to be encoded - * @return The ASCII value of the character - */ - byte getChar(final int sextet) { - return (byte)alphabet[sextet]; - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +import java.util.Arrays; + +/** + *

+ * Represent a base 64 mapping. The 64 characters used in the encoding can be + * specified, since modified-UTF-7 uses other characters than UTF-7 (',' instead + * of '/'). + *

+ *

+ * The exact type of the arguments and result values is adapted to the needs of + * the encoder and decoder, as opposed to following a strict interpretation of + * base 64. + *

+ *

+ * Base 64, as specified in RFC 2045, is an encoding used to encode bytes as + * characters. In (modified-)UTF-7 however, it is used to encode characters as + * bytes, using some intermediate steps: + *

+ *
    + *
  1. Encode all characters as a 16-bit (UTF-16) integer value
  2. + *
  3. Write this as stream of bytes (most-significant first)
  4. + *
  5. Encode these bytes using (modified) base 64 encoding
  6. + *
  7. Write the thus formed stream of characters as a stream of bytes, using + * ASCII encoding
  8. + *
+ * + * @author Jaap Beetstra + */ +class Base64Util { + private static final int ALPHABET_LENGTH = 64; + private final char[] alphabet; + private final int[] inverseAlphabet; + + /** + * Initializes the class with the specified encoding/decoding alphabet. + * + * @param alphabet + * @throws IllegalArgumentException if alphabet is not 64 characters long or + * contains characters which are not 7-bit ASCII + */ + Base64Util(final String alphabet) { + this.alphabet = alphabet.toCharArray(); + if (alphabet.length() != ALPHABET_LENGTH) + throw new IllegalArgumentException("alphabet has incorrect length (should be 64, not " + + alphabet.length() + ")"); + inverseAlphabet = new int[128]; + Arrays.fill(inverseAlphabet, -1); + for (int i = 0; i < this.alphabet.length; i++) { + final char ch = this.alphabet[i]; + if (ch >= 128) + throw new IllegalArgumentException("invalid character in alphabet: " + ch); + inverseAlphabet[ch] = i; + } + } + + /** + * Returns the integer value of the six bits represented by the specified + * character. + * + * @param ch The character, as a ASCII encoded byte + * @return The six bits, as an integer value, or -1 if the byte is not in + * the alphabet + */ + int getSextet(final byte ch) { + if (ch >= 128) + return -1; + return inverseAlphabet[ch]; + } + + /** + * Tells whether the alphabet contains the specified character. + * + * @param ch The character + * @return true if the alphabet contains ch, false otherwise + */ + boolean contains(final char ch) { + if (ch >= 128) + return false; + return inverseAlphabet[ch] >= 0; + } + + /** + * Encodes the six bit group as a character. + * + * @param sextet The six bit group to be encoded + * @return The ASCII value of the character + */ + byte getChar(final int sextet) { + return (byte)alphabet[sextet]; + } +} diff --git a/src/com/beetstra/jutf7/CharsetProvider.java b/src/com/beetstra/jutf7/CharsetProvider.java index f0cabe5624..5572736a32 100644 --- a/src/com/beetstra/jutf7/CharsetProvider.java +++ b/src/com/beetstra/jutf7/CharsetProvider.java @@ -1,90 +1,90 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -/** - *

- * Charset service-provider class used for both variants of the UTF-7 charset - * and the modified-UTF-7 charset. - *

- * - * @author Jaap Beetstra - */ -public class CharsetProvider extends java.nio.charset.spi.CharsetProvider { - private static final String UTF7_NAME = "UTF-7"; - private static final String UTF7_O_NAME = "X-UTF-7-OPTIONAL"; - private static final String UTF7_M_NAME = "X-MODIFIED-UTF-7"; - private static final String[] UTF7_ALIASES = new String[] { - "UNICODE-1-1-UTF-7", "CSUNICODE11UTF7", "X-RFC2152", "X-RFC-2152" - }; - private static final String[] UTF7_O_ALIASES = new String[] { - "X-RFC2152-OPTIONAL", "X-RFC-2152-OPTIONAL" - }; - private static final String[] UTF7_M_ALIASES = new String[] { - "X-IMAP-MODIFIED-UTF-7", "X-IMAP4-MODIFIED-UTF7", "X-IMAP4-MODIFIED-UTF-7", - "X-RFC3501", "X-RFC-3501" - }; - private Charset utf7charset = new UTF7Charset(UTF7_NAME, UTF7_ALIASES, false); - private Charset utf7oCharset = new UTF7Charset(UTF7_O_NAME, UTF7_O_ALIASES, true); - private Charset imap4charset = new ModifiedUTF7Charset(UTF7_M_NAME, UTF7_M_ALIASES); - private List charsets; - - public CharsetProvider() { - charsets = Arrays.asList(new Object[] { - utf7charset, imap4charset, utf7oCharset - }); - } - - /** - * {@inheritDoc} - */ - public Charset charsetForName(String charsetName) { - charsetName = charsetName.toUpperCase(); - for (Iterator iter = charsets.iterator(); iter.hasNext();) { - Charset charset = (Charset)iter.next(); - if (charset.name().equals(charsetName)) - return charset; - } - for (Iterator iter = charsets.iterator(); iter.hasNext();) { - Charset charset = (Charset)iter.next(); - if (charset.aliases().contains(charsetName)) - return charset; - } - return null; - } - - /** - * {@inheritDoc} - */ - public Iterator charsets() { - return charsets.iterator(); - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + *

+ * Charset service-provider class used for both variants of the UTF-7 charset + * and the modified-UTF-7 charset. + *

+ * + * @author Jaap Beetstra + */ +public class CharsetProvider extends java.nio.charset.spi.CharsetProvider { + private static final String UTF7_NAME = "UTF-7"; + private static final String UTF7_O_NAME = "X-UTF-7-OPTIONAL"; + private static final String UTF7_M_NAME = "X-MODIFIED-UTF-7"; + private static final String[] UTF7_ALIASES = new String[] { + "UNICODE-1-1-UTF-7", "CSUNICODE11UTF7", "X-RFC2152", "X-RFC-2152" + }; + private static final String[] UTF7_O_ALIASES = new String[] { + "X-RFC2152-OPTIONAL", "X-RFC-2152-OPTIONAL" + }; + private static final String[] UTF7_M_ALIASES = new String[] { + "X-IMAP-MODIFIED-UTF-7", "X-IMAP4-MODIFIED-UTF7", "X-IMAP4-MODIFIED-UTF-7", + "X-RFC3501", "X-RFC-3501" + }; + private Charset utf7charset = new UTF7Charset(UTF7_NAME, UTF7_ALIASES, false); + private Charset utf7oCharset = new UTF7Charset(UTF7_O_NAME, UTF7_O_ALIASES, true); + private Charset imap4charset = new ModifiedUTF7Charset(UTF7_M_NAME, UTF7_M_ALIASES); + private List charsets; + + public CharsetProvider() { + charsets = Arrays.asList(new Object[] { + utf7charset, imap4charset, utf7oCharset + }); + } + + /** + * {@inheritDoc} + */ + public Charset charsetForName(String charsetName) { + charsetName = charsetName.toUpperCase(); + for (Iterator iter = charsets.iterator(); iter.hasNext();) { + Charset charset = (Charset)iter.next(); + if (charset.name().equals(charsetName)) + return charset; + } + for (Iterator iter = charsets.iterator(); iter.hasNext();) { + Charset charset = (Charset)iter.next(); + if (charset.aliases().contains(charsetName)) + return charset; + } + return null; + } + + /** + * {@inheritDoc} + */ + public Iterator charsets() { + return charsets.iterator(); + } +} diff --git a/src/com/beetstra/jutf7/ModifiedUTF7Charset.java b/src/com/beetstra/jutf7/ModifiedUTF7Charset.java index 603a19ee91..26b0da4492 100644 --- a/src/com/beetstra/jutf7/ModifiedUTF7Charset.java +++ b/src/com/beetstra/jutf7/ModifiedUTF7Charset.java @@ -1,57 +1,57 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -/** - *

- * The character set specified in RFC 3501 to use for IMAP4rev1 mailbox name - * encoding. - *

- * - * @see RFC 3501< /a> - * @author Jaap Beetstra - */ -class ModifiedUTF7Charset extends UTF7StyleCharset { - private static final String MODIFIED_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - + "abcdefghijklmnopqrstuvwxyz" + "0123456789+,"; - - ModifiedUTF7Charset(String name, String[] aliases) { - super(name, aliases, MODIFIED_BASE64_ALPHABET, true); - } - - boolean canEncodeDirectly(char ch) { - if (ch == shift()) - return false; - return ch >= 0x20 && ch <= 0x7E; - } - - byte shift() { - return '&'; - } - - byte unshift() { - return '-'; - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +/** + *

+ * The character set specified in RFC 3501 to use for IMAP4rev1 mailbox name + * encoding. + *

+ * + * @see
RFC 3501< /a> + * @author Jaap Beetstra + */ +class ModifiedUTF7Charset extends UTF7StyleCharset { + private static final String MODIFIED_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + "0123456789+,"; + + ModifiedUTF7Charset(String name, String[] aliases) { + super(name, aliases, MODIFIED_BASE64_ALPHABET, true); + } + + boolean canEncodeDirectly(char ch) { + if (ch == shift()) + return false; + return ch >= 0x20 && ch <= 0x7E; + } + + byte shift() { + return '&'; + } + + byte unshift() { + return '-'; + } +} diff --git a/src/com/beetstra/jutf7/UTF7Charset.java b/src/com/beetstra/jutf7/UTF7Charset.java index 8c85472cf5..04e256224f 100644 --- a/src/com/beetstra/jutf7/UTF7Charset.java +++ b/src/com/beetstra/jutf7/UTF7Charset.java @@ -1,75 +1,75 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -/** - *

- * The character set specified in RFC 2152. Two variants are supported using the - * encodeOptional constructor flag - *

- * - * @see
RFC 2152< /a> - * @author Jaap Beetstra - */ -class UTF7Charset extends UTF7StyleCharset { - private static final String BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; - private static final String SET_D = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),-./:?"; - private static final String SET_O = "!\"#$%&*;<=>@[]^_`{|}"; - private static final String RULE_3 = " \t\r\n"; - final String directlyEncoded; - - UTF7Charset(String name, String[] aliases, boolean includeOptional) { - super(name, aliases, BASE64_ALPHABET, false); - if (includeOptional) - this.directlyEncoded = SET_D + SET_O + RULE_3; - else - this.directlyEncoded = SET_D + RULE_3; - } - - /* - * (non-Javadoc) - * @see com.beetstra.jutf7.UTF7StyleCharset#canEncodeDirectly(char) - */ - boolean canEncodeDirectly(char ch) { - return directlyEncoded.indexOf(ch) >= 0; - } - - /* - * (non-Javadoc) - * @see com.beetstra.jutf7.UTF7StyleCharset#shift() - */ - byte shift() { - return '+'; - } - - /* - * (non-Javadoc) - * @see com.beetstra.jutf7.UTF7StyleCharset#unshift() - */ - byte unshift() { - return '-'; - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +/** + *

+ * The character set specified in RFC 2152. Two variants are supported using the + * encodeOptional constructor flag + *

+ * + * @see
RFC 2152< /a> + * @author Jaap Beetstra + */ +class UTF7Charset extends UTF7StyleCharset { + private static final String BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + private static final String SET_D = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),-./:?"; + private static final String SET_O = "!\"#$%&*;<=>@[]^_`{|}"; + private static final String RULE_3 = " \t\r\n"; + final String directlyEncoded; + + UTF7Charset(String name, String[] aliases, boolean includeOptional) { + super(name, aliases, BASE64_ALPHABET, false); + if (includeOptional) + this.directlyEncoded = SET_D + SET_O + RULE_3; + else + this.directlyEncoded = SET_D + RULE_3; + } + + /* + * (non-Javadoc) + * @see com.beetstra.jutf7.UTF7StyleCharset#canEncodeDirectly(char) + */ + boolean canEncodeDirectly(char ch) { + return directlyEncoded.indexOf(ch) >= 0; + } + + /* + * (non-Javadoc) + * @see com.beetstra.jutf7.UTF7StyleCharset#shift() + */ + byte shift() { + return '+'; + } + + /* + * (non-Javadoc) + * @see com.beetstra.jutf7.UTF7StyleCharset#unshift() + */ + byte unshift() { + return '-'; + } +} diff --git a/src/com/beetstra/jutf7/UTF7StyleCharset.java b/src/com/beetstra/jutf7/UTF7StyleCharset.java index 0878af6031..53c9e0e10b 100644 --- a/src/com/beetstra/jutf7/UTF7StyleCharset.java +++ b/src/com/beetstra/jutf7/UTF7StyleCharset.java @@ -1,117 +1,117 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; -import java.util.Arrays; -import java.util.List; - -/** - *

- * Abstract base class for UTF-7 style encoding and decoding. - *

- * - * @author Jaap Beetstra - */ -abstract class UTF7StyleCharset extends Charset { - private static final List CONTAINED = Arrays.asList(new String[] { - "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16", "UTF-16LE", "UTF-16BE" - }); - final boolean strict; - Base64Util base64; - - /** - *

- * Besides the name and aliases, two additional parameters are required. - * First the base 64 alphabet used; in modified UTF-7 a slightly different - * alphabet is used. Additionally, it should be specified if encoders and - * decoders should be strict about the interpretation of malformed encoded - * sequences. This is used since modified UTF-7 specifically disallows some - * constructs which are allowed (or not specifically disallowed) in UTF-7 - * (RFC 2152). - *

- * - * @param canonicalName The name as defined in java.nio.charset.Charset - * @param aliases The aliases as defined in java.nio.charset.Charset - * @param alphabet The base 64 alphabet used - * @param strict True if strict handling of sequences is requested - */ - protected UTF7StyleCharset(String canonicalName, String[] aliases, String alphabet, - boolean strict) { - super(canonicalName, aliases); - this.base64 = new Base64Util(alphabet); - this.strict = strict; - } - - /* - * (non-Javadoc) - * @see java.nio.charset.Charset#contains(java.nio.charset.Charset) - */ - public boolean contains(final Charset cs) { - return CONTAINED.contains(cs.name()); - } - - /* - * (non-Javadoc) - * @see java.nio.charset.Charset#newDecoder() - */ - public CharsetDecoder newDecoder() { - return new UTF7StyleCharsetDecoder(this, base64, strict); - } - - /* - * (non-Javadoc) - * @see java.nio.charset.Charset#newEncoder() - */ - public CharsetEncoder newEncoder() { - return new UTF7StyleCharsetEncoder(this, base64, strict); - } - - /** - * Tells if a character can be encoded using simple (US-ASCII) encoding or - * requires base 64 encoding. - * - * @param ch The character - * @return True if the character can be encoded directly, false otherwise - */ - abstract boolean canEncodeDirectly(char ch); - - /** - * Returns character used to switch to base 64 encoding. - * - * @return The shift character - */ - abstract byte shift(); - - /** - * Returns character used to switch from base 64 encoding to simple - * encoding. - * - * @return The unshift character - */ - abstract byte unshift(); -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; +import java.util.List; + +/** + *

+ * Abstract base class for UTF-7 style encoding and decoding. + *

+ * + * @author Jaap Beetstra + */ +abstract class UTF7StyleCharset extends Charset { + private static final List CONTAINED = Arrays.asList(new String[] { + "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16", "UTF-16LE", "UTF-16BE" + }); + final boolean strict; + Base64Util base64; + + /** + *

+ * Besides the name and aliases, two additional parameters are required. + * First the base 64 alphabet used; in modified UTF-7 a slightly different + * alphabet is used. Additionally, it should be specified if encoders and + * decoders should be strict about the interpretation of malformed encoded + * sequences. This is used since modified UTF-7 specifically disallows some + * constructs which are allowed (or not specifically disallowed) in UTF-7 + * (RFC 2152). + *

+ * + * @param canonicalName The name as defined in java.nio.charset.Charset + * @param aliases The aliases as defined in java.nio.charset.Charset + * @param alphabet The base 64 alphabet used + * @param strict True if strict handling of sequences is requested + */ + protected UTF7StyleCharset(String canonicalName, String[] aliases, String alphabet, + boolean strict) { + super(canonicalName, aliases); + this.base64 = new Base64Util(alphabet); + this.strict = strict; + } + + /* + * (non-Javadoc) + * @see java.nio.charset.Charset#contains(java.nio.charset.Charset) + */ + public boolean contains(final Charset cs) { + return CONTAINED.contains(cs.name()); + } + + /* + * (non-Javadoc) + * @see java.nio.charset.Charset#newDecoder() + */ + public CharsetDecoder newDecoder() { + return new UTF7StyleCharsetDecoder(this, base64, strict); + } + + /* + * (non-Javadoc) + * @see java.nio.charset.Charset#newEncoder() + */ + public CharsetEncoder newEncoder() { + return new UTF7StyleCharsetEncoder(this, base64, strict); + } + + /** + * Tells if a character can be encoded using simple (US-ASCII) encoding or + * requires base 64 encoding. + * + * @param ch The character + * @return True if the character can be encoded directly, false otherwise + */ + abstract boolean canEncodeDirectly(char ch); + + /** + * Returns character used to switch to base 64 encoding. + * + * @return The shift character + */ + abstract byte shift(); + + /** + * Returns character used to switch from base 64 encoding to simple + * encoding. + * + * @return The unshift character + */ + abstract byte unshift(); +} diff --git a/src/com/beetstra/jutf7/UTF7StyleCharsetDecoder.java b/src/com/beetstra/jutf7/UTF7StyleCharsetDecoder.java index 2fa9d34358..ecaf7e52e5 100644 --- a/src/com/beetstra/jutf7/UTF7StyleCharsetDecoder.java +++ b/src/com/beetstra/jutf7/UTF7StyleCharsetDecoder.java @@ -1,195 +1,195 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; - -/** - *

- * The CharsetDecoder used to decode both variants of the UTF-7 charset and the - * modified-UTF-7 charset. - *

- * - * @author Jaap Beetstra - */ -class UTF7StyleCharsetDecoder extends CharsetDecoder { - private final Base64Util base64; - private final byte shift; - private final byte unshift; - private final boolean strict; - private boolean base64mode; - private int bitsRead; - private int tempChar; - private boolean justShifted; - private boolean justUnshifted; - - UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) { - super(cs, 0.6f, 1.0f); - this.base64 = base64; - this.strict = strict; - this.shift = cs.shift(); - this.unshift = cs.unshift(); - } - - /* - * (non-Javadoc) - * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer, - * java.nio.CharBuffer) - */ - protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { - while (in.hasRemaining()) { - byte b = in.get(); - if (base64mode) { - if (b == unshift) { - if (base64bitsWaiting()) - return malformed(in); - if (justShifted) { - if (!out.hasRemaining()) - return overflow(in); - out.put((char)shift); - } else - justUnshifted = true; - setUnshifted(); - } else { - if (!out.hasRemaining()) - return overflow(in); - CoderResult result = handleBase64(in, out, b); - if (result != null) - return result; - } - justShifted = false; - } else { - if (b == shift) { - base64mode = true; - if (justUnshifted && strict) - return malformed(in); - justShifted = true; - continue; - } - if (!out.hasRemaining()) - return overflow(in); - out.put((char)b); - justUnshifted = false; - } - } - return CoderResult.UNDERFLOW; - } - - private CoderResult overflow(ByteBuffer in) { - in.position(in.position() - 1); - return CoderResult.OVERFLOW; - } - - /** - *

- * Decodes a byte in base 64 mode. Will directly write a character to - * the output buffer if completed. - *

- * - * @param in The input buffer - * @param out The output buffer - * @param lastRead Last byte read from the input buffer - * @return CoderResult.malformed if a non-base 64 character was encountered - * in strict mode, null otherwise - */ - private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) { - CoderResult result = null; - int sextet = base64.getSextet(lastRead); - if (sextet >= 0) { - bitsRead += 6; - if (bitsRead < 16) { - tempChar += sextet << (16 - bitsRead); - } else { - bitsRead -= 16; - tempChar += sextet >> (bitsRead); - out.put((char)tempChar); - tempChar = (sextet << (16 - bitsRead)) & 0xFFFF; - } - } else { - if (strict) - return malformed(in); - out.put((char)lastRead); - if (base64bitsWaiting()) - result = malformed(in); - setUnshifted(); - } - return result; - } - - /* - * (non-Javadoc) - * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer) - */ - protected CoderResult implFlush(CharBuffer out) { - if ((base64mode && strict) || base64bitsWaiting()) - return CoderResult.malformedForLength(1); - return CoderResult.UNDERFLOW; - } - - /* - * (non-Javadoc) - * @see java.nio.charset.CharsetDecoder#implReset() - */ - protected void implReset() { - setUnshifted(); - justUnshifted = false; - } - - /** - *

- * Resets the input buffer position to just before the last byte read, and - * returns a result indicating to skip the last byte. - *

- * - * @param in The input buffer - * @return CoderResult.malformedForLength(1); - */ - private CoderResult malformed(ByteBuffer in) { - in.position(in.position() - 1); - return CoderResult.malformedForLength(1); - } - - /** - * @return True if there are base64 encoded characters waiting to be written - */ - private boolean base64bitsWaiting() { - return tempChar != 0 || bitsRead >= 6; - } - - /** - *

- * Updates internal state to reflect the decoder is no longer in base 64 - * mode - *

- */ - private void setUnshifted() { - base64mode = false; - bitsRead = 0; - tempChar = 0; - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; + +/** + *

+ * The CharsetDecoder used to decode both variants of the UTF-7 charset and the + * modified-UTF-7 charset. + *

+ * + * @author Jaap Beetstra + */ +class UTF7StyleCharsetDecoder extends CharsetDecoder { + private final Base64Util base64; + private final byte shift; + private final byte unshift; + private final boolean strict; + private boolean base64mode; + private int bitsRead; + private int tempChar; + private boolean justShifted; + private boolean justUnshifted; + + UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) { + super(cs, 0.6f, 1.0f); + this.base64 = base64; + this.strict = strict; + this.shift = cs.shift(); + this.unshift = cs.unshift(); + } + + /* + * (non-Javadoc) + * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer, + * java.nio.CharBuffer) + */ + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + while (in.hasRemaining()) { + byte b = in.get(); + if (base64mode) { + if (b == unshift) { + if (base64bitsWaiting()) + return malformed(in); + if (justShifted) { + if (!out.hasRemaining()) + return overflow(in); + out.put((char)shift); + } else + justUnshifted = true; + setUnshifted(); + } else { + if (!out.hasRemaining()) + return overflow(in); + CoderResult result = handleBase64(in, out, b); + if (result != null) + return result; + } + justShifted = false; + } else { + if (b == shift) { + base64mode = true; + if (justUnshifted && strict) + return malformed(in); + justShifted = true; + continue; + } + if (!out.hasRemaining()) + return overflow(in); + out.put((char)b); + justUnshifted = false; + } + } + return CoderResult.UNDERFLOW; + } + + private CoderResult overflow(ByteBuffer in) { + in.position(in.position() - 1); + return CoderResult.OVERFLOW; + } + + /** + *

+ * Decodes a byte in base 64 mode. Will directly write a character to + * the output buffer if completed. + *

+ * + * @param in The input buffer + * @param out The output buffer + * @param lastRead Last byte read from the input buffer + * @return CoderResult.malformed if a non-base 64 character was encountered + * in strict mode, null otherwise + */ + private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) { + CoderResult result = null; + int sextet = base64.getSextet(lastRead); + if (sextet >= 0) { + bitsRead += 6; + if (bitsRead < 16) { + tempChar += sextet << (16 - bitsRead); + } else { + bitsRead -= 16; + tempChar += sextet >> (bitsRead); + out.put((char)tempChar); + tempChar = (sextet << (16 - bitsRead)) & 0xFFFF; + } + } else { + if (strict) + return malformed(in); + out.put((char)lastRead); + if (base64bitsWaiting()) + result = malformed(in); + setUnshifted(); + } + return result; + } + + /* + * (non-Javadoc) + * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer) + */ + protected CoderResult implFlush(CharBuffer out) { + if ((base64mode && strict) || base64bitsWaiting()) + return CoderResult.malformedForLength(1); + return CoderResult.UNDERFLOW; + } + + /* + * (non-Javadoc) + * @see java.nio.charset.CharsetDecoder#implReset() + */ + protected void implReset() { + setUnshifted(); + justUnshifted = false; + } + + /** + *

+ * Resets the input buffer position to just before the last byte read, and + * returns a result indicating to skip the last byte. + *

+ * + * @param in The input buffer + * @return CoderResult.malformedForLength(1); + */ + private CoderResult malformed(ByteBuffer in) { + in.position(in.position() - 1); + return CoderResult.malformedForLength(1); + } + + /** + * @return True if there are base64 encoded characters waiting to be written + */ + private boolean base64bitsWaiting() { + return tempChar != 0 || bitsRead >= 6; + } + + /** + *

+ * Updates internal state to reflect the decoder is no longer in base 64 + * mode + *

+ */ + private void setUnshifted() { + base64mode = false; + bitsRead = 0; + tempChar = 0; + } +} diff --git a/src/com/beetstra/jutf7/UTF7StyleCharsetEncoder.java b/src/com/beetstra/jutf7/UTF7StyleCharsetEncoder.java index de82397134..b59ef07b2d 100644 --- a/src/com/beetstra/jutf7/UTF7StyleCharsetEncoder.java +++ b/src/com/beetstra/jutf7/UTF7StyleCharsetEncoder.java @@ -1,217 +1,217 @@ -/* ==================================================================== - * Copyright (c) 2006 J.T. Beetstra - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ==================================================================== - */ - -package com.beetstra.jutf7; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CoderResult; - -/** - *

- * The CharsetEncoder used to encode both variants of the UTF-7 charset and the - * modified-UTF-7 charset. - *

- *

- * Please note this class does not behave strictly according to the - * specification in Sun Java VMs before 1.6. This is done to get around - * a bug in the implementation of - * {@link java.nio.charset.CharsetEncoder#encode(CharBuffer)}. Unfortunately, - * that method cannot be overridden. - *

- * - * @see
JDK - * bug 6221056< /a> - * @author Jaap Beetstra - */ -class UTF7StyleCharsetEncoder extends CharsetEncoder { - private static final float AVG_BYTES_PER_CHAR = 1.5f; - private static final float MAX_BYTES_PER_CHAR = 5.0f; - private final UTF7StyleCharset cs; - private final Base64Util base64; - private final byte shift; - private final byte unshift; - private final boolean strict; - private boolean base64mode; - private int bitsToOutput; - private int sextet; - static boolean useUglyHackToForceCallToFlushInJava5; - static { - String version = System.getProperty("java.specification.version"); - String vendor = System.getProperty("java.vm.vendor"); - useUglyHackToForceCallToFlushInJava5 = "1.4".equals(version) || "1.5".equals(version); - useUglyHackToForceCallToFlushInJava5 &= "Sun Microsystems Inc.".equals(vendor); - } - - UTF7StyleCharsetEncoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) { - super(cs, AVG_BYTES_PER_CHAR, MAX_BYTES_PER_CHAR); - this.cs = cs; - this.base64 = base64; - this.strict = strict; - this.shift = cs.shift(); - this.unshift = cs.unshift(); - } - - /* - * (non-Javadoc) - * @see java.nio.charset.CharsetEncoder#implReset() - */ - protected void implReset() { - base64mode = false; - sextet = 0; - bitsToOutput = 0; - } - - /** - * {@inheritDoc} - *

- * Note that this method might return CoderResult.OVERFLOW (as - * is required by the specification) if insufficient space is available in - * the output buffer. However, calling it again on JDKs before Java 6 - * triggers a bug in - * {@link java.nio.charset.CharsetEncoder#flush(ByteBuffer)} causing it to - * throw an IllegalStateException (the buggy method is final, - * thus cannot be overridden). - *

- * - * @see
- * JDK bug 6227608< /a> - * @param out The output byte buffer - * @return A coder-result object describing the reason for termination - */ - protected CoderResult implFlush(ByteBuffer out) { - if (base64mode) { - if (out.remaining() < 2) - return CoderResult.OVERFLOW; - if (bitsToOutput != 0) - out.put(base64.getChar(sextet)); - out.put(unshift); - } - return CoderResult.UNDERFLOW; - } - - /** - * {@inheritDoc} - *

- * Note that this method might return CoderResult.OVERFLOW, - * even though there is sufficient space available in the output buffer. - * This is done to force the broken implementation of - * {@link java.nio.charset.CharsetEncoder#encode(CharBuffer)} to call flush - * (the buggy method is final, thus cannot be overridden). - *

- *

- * However, String.getBytes() fails if CoderResult.OVERFLOW is returned, - * since this assumes it always allocates sufficient bytes (maxBytesPerChar - * * nr_of_chars). Thus, as an extra check, the size of the input buffer is - * compared against the size of the output buffer. A static variable is used - * to indicate if a broken java version is used. - *

- *

- * It is not possible to directly write the last few bytes, since more bytes - * might be waiting to be encoded then those available in the input buffer. - *

- * - * @see
- * JDK bug 6221056< /a> - * @param in The input character buffer - * @param out The output byte buffer - * @return A coder-result object describing the reason for termination - */ - protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { - while (in.hasRemaining()) { - if (out.remaining() < 4) - return CoderResult.OVERFLOW; - char ch = in.get(); - if (cs.canEncodeDirectly(ch)) { - unshift(out, ch); - out.put((byte)ch); - } else if (!base64mode && ch == shift) { - out.put(shift); - out.put(unshift); - } else - encodeBase64(ch, out); - } - /* - * These lines are required to trick JDK 1.5 and - * earlier into flushing when using Charset.encode(String), - * Charset.encode(CharBuffer) or CharsetEncoder.encode(CharBuffer) - * Without them, the last few bytes may be missing. - */ - if (base64mode && useUglyHackToForceCallToFlushInJava5 - && out.limit() != MAX_BYTES_PER_CHAR * in.limit()) - return CoderResult.OVERFLOW; - /* */ - return CoderResult.UNDERFLOW; - } - - /** - *

- * Writes the bytes necessary to leave base 64 mode. This might - * include an unshift character. - *

- * - * @param out - * @param ch - */ - private void unshift(ByteBuffer out, char ch) { - if (!base64mode) - return; - if (bitsToOutput != 0) - out.put(base64.getChar(sextet)); - if (base64.contains(ch) || ch == unshift || strict) - out.put(unshift); - base64mode = false; - sextet = 0; - bitsToOutput = 0; - } - - /** - *

- * Writes the bytes necessary to encode a character in base 64 mode. - * All bytes which are fully determined will be written. The fields - * bitsToOutput and sextet are used to remember - * the bytes not yet fully determined. - *

- * - * @param out - * @param ch - */ - private void encodeBase64(char ch, ByteBuffer out) { - if (!base64mode) - out.put(shift); - base64mode = true; - bitsToOutput += 16; - while (bitsToOutput >= 6) { - bitsToOutput -= 6; - sextet += (ch >> bitsToOutput); - sextet &= 0x3F; - out.put(base64.getChar(sextet)); - sextet = 0; - } - sextet = (ch << (6 - bitsToOutput)) & 0x3F; - } -} +/* ==================================================================== + * Copyright (c) 2006 J.T. Beetstra + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ==================================================================== + */ + +package com.beetstra.jutf7; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +/** + *

+ * The CharsetEncoder used to encode both variants of the UTF-7 charset and the + * modified-UTF-7 charset. + *

+ *

+ * Please note this class does not behave strictly according to the + * specification in Sun Java VMs before 1.6. This is done to get around + * a bug in the implementation of + * {@link java.nio.charset.CharsetEncoder#encode(CharBuffer)}. Unfortunately, + * that method cannot be overridden. + *

+ * + * @see
JDK + * bug 6221056< /a> + * @author Jaap Beetstra + */ +class UTF7StyleCharsetEncoder extends CharsetEncoder { + private static final float AVG_BYTES_PER_CHAR = 1.5f; + private static final float MAX_BYTES_PER_CHAR = 5.0f; + private final UTF7StyleCharset cs; + private final Base64Util base64; + private final byte shift; + private final byte unshift; + private final boolean strict; + private boolean base64mode; + private int bitsToOutput; + private int sextet; + static boolean useUglyHackToForceCallToFlushInJava5; + static { + String version = System.getProperty("java.specification.version"); + String vendor = System.getProperty("java.vm.vendor"); + useUglyHackToForceCallToFlushInJava5 = "1.4".equals(version) || "1.5".equals(version); + useUglyHackToForceCallToFlushInJava5 &= "Sun Microsystems Inc.".equals(vendor); + } + + UTF7StyleCharsetEncoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) { + super(cs, AVG_BYTES_PER_CHAR, MAX_BYTES_PER_CHAR); + this.cs = cs; + this.base64 = base64; + this.strict = strict; + this.shift = cs.shift(); + this.unshift = cs.unshift(); + } + + /* + * (non-Javadoc) + * @see java.nio.charset.CharsetEncoder#implReset() + */ + protected void implReset() { + base64mode = false; + sextet = 0; + bitsToOutput = 0; + } + + /** + * {@inheritDoc} + *

+ * Note that this method might return CoderResult.OVERFLOW (as + * is required by the specification) if insufficient space is available in + * the output buffer. However, calling it again on JDKs before Java 6 + * triggers a bug in + * {@link java.nio.charset.CharsetEncoder#flush(ByteBuffer)} causing it to + * throw an IllegalStateException (the buggy method is final, + * thus cannot be overridden). + *

+ * + * @see
+ * JDK bug 6227608< /a> + * @param out The output byte buffer + * @return A coder-result object describing the reason for termination + */ + protected CoderResult implFlush(ByteBuffer out) { + if (base64mode) { + if (out.remaining() < 2) + return CoderResult.OVERFLOW; + if (bitsToOutput != 0) + out.put(base64.getChar(sextet)); + out.put(unshift); + } + return CoderResult.UNDERFLOW; + } + + /** + * {@inheritDoc} + *

+ * Note that this method might return CoderResult.OVERFLOW, + * even though there is sufficient space available in the output buffer. + * This is done to force the broken implementation of + * {@link java.nio.charset.CharsetEncoder#encode(CharBuffer)} to call flush + * (the buggy method is final, thus cannot be overridden). + *

+ *

+ * However, String.getBytes() fails if CoderResult.OVERFLOW is returned, + * since this assumes it always allocates sufficient bytes (maxBytesPerChar + * * nr_of_chars). Thus, as an extra check, the size of the input buffer is + * compared against the size of the output buffer. A static variable is used + * to indicate if a broken java version is used. + *

+ *

+ * It is not possible to directly write the last few bytes, since more bytes + * might be waiting to be encoded then those available in the input buffer. + *

+ * + * @see
+ * JDK bug 6221056< /a> + * @param in The input character buffer + * @param out The output byte buffer + * @return A coder-result object describing the reason for termination + */ + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + while (in.hasRemaining()) { + if (out.remaining() < 4) + return CoderResult.OVERFLOW; + char ch = in.get(); + if (cs.canEncodeDirectly(ch)) { + unshift(out, ch); + out.put((byte)ch); + } else if (!base64mode && ch == shift) { + out.put(shift); + out.put(unshift); + } else + encodeBase64(ch, out); + } + /* + * These lines are required to trick JDK 1.5 and + * earlier into flushing when using Charset.encode(String), + * Charset.encode(CharBuffer) or CharsetEncoder.encode(CharBuffer) + * Without them, the last few bytes may be missing. + */ + if (base64mode && useUglyHackToForceCallToFlushInJava5 + && out.limit() != MAX_BYTES_PER_CHAR * in.limit()) + return CoderResult.OVERFLOW; + /* */ + return CoderResult.UNDERFLOW; + } + + /** + *

+ * Writes the bytes necessary to leave base 64 mode. This might + * include an unshift character. + *

+ * + * @param out + * @param ch + */ + private void unshift(ByteBuffer out, char ch) { + if (!base64mode) + return; + if (bitsToOutput != 0) + out.put(base64.getChar(sextet)); + if (base64.contains(ch) || ch == unshift || strict) + out.put(unshift); + base64mode = false; + sextet = 0; + bitsToOutput = 0; + } + + /** + *

+ * Writes the bytes necessary to encode a character in base 64 mode. + * All bytes which are fully determined will be written. The fields + * bitsToOutput and sextet are used to remember + * the bytes not yet fully determined. + *

+ * + * @param out + * @param ch + */ + private void encodeBase64(char ch, ByteBuffer out) { + if (!base64mode) + out.put(shift); + base64mode = true; + bitsToOutput += 16; + while (bitsToOutput >= 6) { + bitsToOutput -= 6; + sextet += (ch >> bitsToOutput); + sextet &= 0x3F; + out.put(base64.getChar(sextet)); + sextet = 0; + } + sextet = (ch << (6 - bitsToOutput)) & 0x3F; + } +} diff --git a/src/com/fsck/k9/FontSizes.java b/src/com/fsck/k9/FontSizes.java index 7aeb457438..b1569809bf 100644 --- a/src/com/fsck/k9/FontSizes.java +++ b/src/com/fsck/k9/FontSizes.java @@ -1,382 +1,382 @@ -package com.fsck.k9; - -import android.content.SharedPreferences; -import android.webkit.WebSettings.TextSize; - -/** - * Manage font size of the information displayed in the account list, folder - * list, message list and in the message view. - */ -public class FontSizes -{ - /* - * Keys for the preference storage. - */ - private static final String ACCOUNT_NAME = "fontSizeAccountName"; - private static final String ACCOUNT_DESCRIPTION = "fontSizeAccountDescription"; - private static final String FOLDER_NAME = "fontSizeFolderName"; - private static final String FOLDER_STATUS = "fontSizeFolderStatus"; - private static final String MESSAGE_LIST_SUBJECT = "fontSizeMessageListSubject"; - private static final String MESSAGE_LIST_SENDER = "fontSizeMessageListSender"; - private static final String MESSAGE_LIST_DATE = "fontSizeMessageListDate"; - private static final String MESSAGE_VIEW_SENDER = "fontSizeMessageViewSender"; - private static final String MESSAGE_VIEW_TO = "fontSizeMessageViewTo"; - private static final String MESSAGE_VIEW_CC = "fontSizeMessageViewCC"; - private static final String MESSAGE_VIEW_ADDITIONAL_HEADERS = "fontSizeMessageViewAdditionalHeaders"; - private static final String MESSAGE_VIEW_SUBJECT = "fontSizeMessageViewSubject"; - private static final String MESSAGE_VIEW_TIME = "fontSizeMessageViewTime"; - private static final String MESSAGE_VIEW_DATE = "fontSizeMessageViewDate"; - private static final String MESSAGE_VIEW_CONTENT = "fontSizeMessageViewContent"; - - /* - * Values for the font sizes in DIP (device independent pixel) - */ - public static final int FONT_10DIP = 10; - public static final int FONT_12DIP = 12; - public static final int SMALL = 14; // ?android:attr/textAppearanceSmall - public static final int FONT_16DIP = 16; - public static final int MEDIUM = 18; // ?android:attr/textAppearanceMedium - public static final int FONT_20DIP = 20; - public static final int LARGE = 22; // ?android:attr/textAppearanceLarge - - - /** - * Font size of account names in the account list activity. - */ - private int accountName; - - /** - * Font size of account descriptions in the account list activity. - */ - private int accountDescription; - - /** - * Font size of folder names in the folder list activity. - */ - private int folderName; - - /** - * Font size of the folder status in the folder list activity. - */ - private int folderStatus; - - /** - * Font size of message subjects in the message list activity. - */ - private int messageListSubject; - - /** - * Font size of message senders in the message list activity. - */ - private int messageListSender; - - /** - * Font size of message dates in the message list activity. - */ - private int messageListDate; - - /** - * Font size of the message sender in the message view activity. - */ - private int messageViewSender; - - /** - * Font size of the message receiver(s) (To) in the message view activity. - */ - private int messageViewTo; - - /** - * Font size of the message receiver(s) (CC) in the message view activity. - */ - private int messageViewCC; - - /** - * Font size of additional headers in the message view activity. - */ - private int messageViewAdditionalHeaders; - - /** - * Font size of the message subject in the message view activity. - */ - private int messageViewSubject; - - /** - * Font size of the message time in the message view activity. - */ - private int messageViewTime; - - /** - * Font size of the message date in the message view activity. - */ - private int messageViewDate; - - /** - * Font size of the message content in the message view activity. - * - * Note: The unit is WebSettings.TextSize - */ - private TextSize messageViewContent = TextSize.NORMAL; - - /** - * Create a FontSizes object with default values. - */ - public FontSizes() - { - accountName = MEDIUM; - accountDescription = SMALL; - - folderName = LARGE; - folderStatus = SMALL; - - messageListSubject = SMALL; - messageListSender = SMALL; - messageListDate = SMALL; - - messageViewSender = SMALL; - messageViewTo = FONT_12DIP; - messageViewCC = FONT_12DIP; - messageViewAdditionalHeaders = FONT_12DIP; - messageViewSubject = FONT_12DIP; - messageViewTime = FONT_10DIP; - messageViewDate = FONT_10DIP; - } - - /** - * Permanently save the font size settings. - * - * @param editor Used to save the font size settings. - */ - public void save(SharedPreferences.Editor editor) - { - editor.putInt(ACCOUNT_NAME, accountName); - editor.putInt(ACCOUNT_DESCRIPTION, accountDescription); - - editor.putInt(FOLDER_NAME, folderName); - editor.putInt(FOLDER_STATUS, folderStatus); - - editor.putInt(MESSAGE_LIST_SUBJECT, messageListSubject); - editor.putInt(MESSAGE_LIST_SENDER, messageListSender); - editor.putInt(MESSAGE_LIST_DATE, messageListDate); - - editor.putInt(MESSAGE_VIEW_SUBJECT, messageViewSubject); - editor.putInt(MESSAGE_VIEW_TO, messageViewTo); - editor.putInt(MESSAGE_VIEW_CC, messageViewCC); - editor.putInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders); - editor.putInt(MESSAGE_VIEW_SENDER, messageViewSender); - editor.putInt(MESSAGE_VIEW_TIME, messageViewTime); - editor.putInt(MESSAGE_VIEW_DATE, messageViewDate); - editor.putInt(MESSAGE_VIEW_CONTENT, getMessageViewContentAsInt()); - } - - /** - * Load the font size settings from permanent storage. - * - * @param prefs Used to load the font size settings. - */ - public void load(SharedPreferences prefs) - { - accountName = prefs.getInt(ACCOUNT_NAME, accountName); - accountDescription = prefs.getInt(ACCOUNT_DESCRIPTION, accountDescription); - - folderName = prefs.getInt(FOLDER_NAME, folderName); - folderStatus = prefs.getInt(FOLDER_STATUS, folderStatus); - - messageListSubject = prefs.getInt(MESSAGE_LIST_SUBJECT, messageListSubject); - messageListSender = prefs.getInt(MESSAGE_LIST_SENDER, messageListSender); - messageListDate = prefs.getInt(MESSAGE_LIST_DATE, messageListDate); - - messageViewSubject = prefs.getInt(MESSAGE_VIEW_SENDER, FONT_12DIP); - messageViewTo = prefs.getInt(MESSAGE_VIEW_TO, messageViewTo); - messageViewCC = prefs.getInt(MESSAGE_VIEW_CC, messageViewCC); - messageViewAdditionalHeaders = prefs.getInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders); - messageViewSender = prefs.getInt(MESSAGE_VIEW_SUBJECT, messageViewSender); - messageViewTime = prefs.getInt(MESSAGE_VIEW_TIME, messageViewTime); - messageViewDate = prefs.getInt(MESSAGE_VIEW_DATE, messageViewDate); - setMessageViewContent(prefs.getInt(MESSAGE_VIEW_CONTENT, 3)); - } - - public int getAccountName() - { - return accountName; - } - - public void setAccountName(int accountName) - { - this.accountName = accountName; - } - - public int getAccountDescription() - { - return accountDescription; - } - - public void setAccountDescription(int accountDescription) - { - this.accountDescription = accountDescription; - } - - public int getFolderName() - { - return folderName; - } - - public void setFolderName(int folderName) - { - this.folderName = folderName; - } - - public int getFolderStatus() - { - return folderStatus; - } - - public void setFolderStatus(int folderStatus) - { - this.folderStatus = folderStatus; - } - - public int getMessageListSubject() - { - return messageListSubject; - } - - public void setMessageListSubject(int messageListSubject) - { - this.messageListSubject = messageListSubject; - } - - public int getMessageListSender() - { - return messageListSender; - } - - public void setMessageListSender(int messageListSender) - { - this.messageListSender = messageListSender; - } - - public int getMessageListDate() - { - return messageListDate; - } - - public void setMessageListDate(int messageListDate) - { - this.messageListDate = messageListDate; - } - - public int getMessageViewSender() - { - return messageViewSender; - } - - public void setMessageViewSender(int messageViewSender) - { - this.messageViewSender = messageViewSender; - } - - public int getMessageViewTo() - { - return messageViewTo; - } - - public void setMessageViewTo(int messageViewTo) - { - this.messageViewTo = messageViewTo; - } - - public int getMessageViewCC() - { - return messageViewCC; - } - - public void setMessageViewCC(int messageViewCC) - { - this.messageViewCC = messageViewCC; - } - - public int getMessageViewAdditionalHeaders() - { - return messageViewAdditionalHeaders; - } - - public void setMessageViewAdditionalHeaders(int messageViewAdditionalHeaders) - { - this.messageViewAdditionalHeaders = messageViewAdditionalHeaders; - } - - public int getMessageViewSubject() - { - return messageViewSubject; - } - - public void setMessageViewSubject(int messageViewSubject) - { - this.messageViewSubject = messageViewSubject; - } - - public int getMessageViewTime() - { - return messageViewTime; - } - - public void setMessageViewTime(int messageViewTime) - { - this.messageViewTime = messageViewTime; - } - - public int getMessageViewDate() - { - return messageViewDate; - } - - public void setMessageViewDate(int messageViewDate) - { - this.messageViewDate = messageViewDate; - } - - public TextSize getMessageViewContent() - { - return messageViewContent; - } - - public int getMessageViewContentAsInt() - { - switch (messageViewContent) - { - case SMALLEST: - return 1; - case SMALLER: - return 2; - default: - case NORMAL: - return 3; - case LARGER: - return 4; - case LARGEST: - return 5; - } - } - - public void setMessageViewContent(int size) - { - switch (size) - { - case 1: - messageViewContent = TextSize.SMALLEST; - break; - case 2: - messageViewContent = TextSize.SMALLER; - break; - case 3: - messageViewContent = TextSize.NORMAL; - break; - case 4: - messageViewContent = TextSize.LARGER; - break; - case 5: - messageViewContent = TextSize.LARGEST; - break; - } - } -} +package com.fsck.k9; + +import android.content.SharedPreferences; +import android.webkit.WebSettings.TextSize; + +/** + * Manage font size of the information displayed in the account list, folder + * list, message list and in the message view. + */ +public class FontSizes +{ + /* + * Keys for the preference storage. + */ + private static final String ACCOUNT_NAME = "fontSizeAccountName"; + private static final String ACCOUNT_DESCRIPTION = "fontSizeAccountDescription"; + private static final String FOLDER_NAME = "fontSizeFolderName"; + private static final String FOLDER_STATUS = "fontSizeFolderStatus"; + private static final String MESSAGE_LIST_SUBJECT = "fontSizeMessageListSubject"; + private static final String MESSAGE_LIST_SENDER = "fontSizeMessageListSender"; + private static final String MESSAGE_LIST_DATE = "fontSizeMessageListDate"; + private static final String MESSAGE_VIEW_SENDER = "fontSizeMessageViewSender"; + private static final String MESSAGE_VIEW_TO = "fontSizeMessageViewTo"; + private static final String MESSAGE_VIEW_CC = "fontSizeMessageViewCC"; + private static final String MESSAGE_VIEW_ADDITIONAL_HEADERS = "fontSizeMessageViewAdditionalHeaders"; + private static final String MESSAGE_VIEW_SUBJECT = "fontSizeMessageViewSubject"; + private static final String MESSAGE_VIEW_TIME = "fontSizeMessageViewTime"; + private static final String MESSAGE_VIEW_DATE = "fontSizeMessageViewDate"; + private static final String MESSAGE_VIEW_CONTENT = "fontSizeMessageViewContent"; + + /* + * Values for the font sizes in DIP (device independent pixel) + */ + public static final int FONT_10DIP = 10; + public static final int FONT_12DIP = 12; + public static final int SMALL = 14; // ?android:attr/textAppearanceSmall + public static final int FONT_16DIP = 16; + public static final int MEDIUM = 18; // ?android:attr/textAppearanceMedium + public static final int FONT_20DIP = 20; + public static final int LARGE = 22; // ?android:attr/textAppearanceLarge + + + /** + * Font size of account names in the account list activity. + */ + private int accountName; + + /** + * Font size of account descriptions in the account list activity. + */ + private int accountDescription; + + /** + * Font size of folder names in the folder list activity. + */ + private int folderName; + + /** + * Font size of the folder status in the folder list activity. + */ + private int folderStatus; + + /** + * Font size of message subjects in the message list activity. + */ + private int messageListSubject; + + /** + * Font size of message senders in the message list activity. + */ + private int messageListSender; + + /** + * Font size of message dates in the message list activity. + */ + private int messageListDate; + + /** + * Font size of the message sender in the message view activity. + */ + private int messageViewSender; + + /** + * Font size of the message receiver(s) (To) in the message view activity. + */ + private int messageViewTo; + + /** + * Font size of the message receiver(s) (CC) in the message view activity. + */ + private int messageViewCC; + + /** + * Font size of additional headers in the message view activity. + */ + private int messageViewAdditionalHeaders; + + /** + * Font size of the message subject in the message view activity. + */ + private int messageViewSubject; + + /** + * Font size of the message time in the message view activity. + */ + private int messageViewTime; + + /** + * Font size of the message date in the message view activity. + */ + private int messageViewDate; + + /** + * Font size of the message content in the message view activity. + * + * Note: The unit is WebSettings.TextSize + */ + private TextSize messageViewContent = TextSize.NORMAL; + + /** + * Create a FontSizes object with default values. + */ + public FontSizes() + { + accountName = MEDIUM; + accountDescription = SMALL; + + folderName = LARGE; + folderStatus = SMALL; + + messageListSubject = SMALL; + messageListSender = SMALL; + messageListDate = SMALL; + + messageViewSender = SMALL; + messageViewTo = FONT_12DIP; + messageViewCC = FONT_12DIP; + messageViewAdditionalHeaders = FONT_12DIP; + messageViewSubject = FONT_12DIP; + messageViewTime = FONT_10DIP; + messageViewDate = FONT_10DIP; + } + + /** + * Permanently save the font size settings. + * + * @param editor Used to save the font size settings. + */ + public void save(SharedPreferences.Editor editor) + { + editor.putInt(ACCOUNT_NAME, accountName); + editor.putInt(ACCOUNT_DESCRIPTION, accountDescription); + + editor.putInt(FOLDER_NAME, folderName); + editor.putInt(FOLDER_STATUS, folderStatus); + + editor.putInt(MESSAGE_LIST_SUBJECT, messageListSubject); + editor.putInt(MESSAGE_LIST_SENDER, messageListSender); + editor.putInt(MESSAGE_LIST_DATE, messageListDate); + + editor.putInt(MESSAGE_VIEW_SUBJECT, messageViewSubject); + editor.putInt(MESSAGE_VIEW_TO, messageViewTo); + editor.putInt(MESSAGE_VIEW_CC, messageViewCC); + editor.putInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders); + editor.putInt(MESSAGE_VIEW_SENDER, messageViewSender); + editor.putInt(MESSAGE_VIEW_TIME, messageViewTime); + editor.putInt(MESSAGE_VIEW_DATE, messageViewDate); + editor.putInt(MESSAGE_VIEW_CONTENT, getMessageViewContentAsInt()); + } + + /** + * Load the font size settings from permanent storage. + * + * @param prefs Used to load the font size settings. + */ + public void load(SharedPreferences prefs) + { + accountName = prefs.getInt(ACCOUNT_NAME, accountName); + accountDescription = prefs.getInt(ACCOUNT_DESCRIPTION, accountDescription); + + folderName = prefs.getInt(FOLDER_NAME, folderName); + folderStatus = prefs.getInt(FOLDER_STATUS, folderStatus); + + messageListSubject = prefs.getInt(MESSAGE_LIST_SUBJECT, messageListSubject); + messageListSender = prefs.getInt(MESSAGE_LIST_SENDER, messageListSender); + messageListDate = prefs.getInt(MESSAGE_LIST_DATE, messageListDate); + + messageViewSubject = prefs.getInt(MESSAGE_VIEW_SENDER, FONT_12DIP); + messageViewTo = prefs.getInt(MESSAGE_VIEW_TO, messageViewTo); + messageViewCC = prefs.getInt(MESSAGE_VIEW_CC, messageViewCC); + messageViewAdditionalHeaders = prefs.getInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders); + messageViewSender = prefs.getInt(MESSAGE_VIEW_SUBJECT, messageViewSender); + messageViewTime = prefs.getInt(MESSAGE_VIEW_TIME, messageViewTime); + messageViewDate = prefs.getInt(MESSAGE_VIEW_DATE, messageViewDate); + setMessageViewContent(prefs.getInt(MESSAGE_VIEW_CONTENT, 3)); + } + + public int getAccountName() + { + return accountName; + } + + public void setAccountName(int accountName) + { + this.accountName = accountName; + } + + public int getAccountDescription() + { + return accountDescription; + } + + public void setAccountDescription(int accountDescription) + { + this.accountDescription = accountDescription; + } + + public int getFolderName() + { + return folderName; + } + + public void setFolderName(int folderName) + { + this.folderName = folderName; + } + + public int getFolderStatus() + { + return folderStatus; + } + + public void setFolderStatus(int folderStatus) + { + this.folderStatus = folderStatus; + } + + public int getMessageListSubject() + { + return messageListSubject; + } + + public void setMessageListSubject(int messageListSubject) + { + this.messageListSubject = messageListSubject; + } + + public int getMessageListSender() + { + return messageListSender; + } + + public void setMessageListSender(int messageListSender) + { + this.messageListSender = messageListSender; + } + + public int getMessageListDate() + { + return messageListDate; + } + + public void setMessageListDate(int messageListDate) + { + this.messageListDate = messageListDate; + } + + public int getMessageViewSender() + { + return messageViewSender; + } + + public void setMessageViewSender(int messageViewSender) + { + this.messageViewSender = messageViewSender; + } + + public int getMessageViewTo() + { + return messageViewTo; + } + + public void setMessageViewTo(int messageViewTo) + { + this.messageViewTo = messageViewTo; + } + + public int getMessageViewCC() + { + return messageViewCC; + } + + public void setMessageViewCC(int messageViewCC) + { + this.messageViewCC = messageViewCC; + } + + public int getMessageViewAdditionalHeaders() + { + return messageViewAdditionalHeaders; + } + + public void setMessageViewAdditionalHeaders(int messageViewAdditionalHeaders) + { + this.messageViewAdditionalHeaders = messageViewAdditionalHeaders; + } + + public int getMessageViewSubject() + { + return messageViewSubject; + } + + public void setMessageViewSubject(int messageViewSubject) + { + this.messageViewSubject = messageViewSubject; + } + + public int getMessageViewTime() + { + return messageViewTime; + } + + public void setMessageViewTime(int messageViewTime) + { + this.messageViewTime = messageViewTime; + } + + public int getMessageViewDate() + { + return messageViewDate; + } + + public void setMessageViewDate(int messageViewDate) + { + this.messageViewDate = messageViewDate; + } + + public TextSize getMessageViewContent() + { + return messageViewContent; + } + + public int getMessageViewContentAsInt() + { + switch (messageViewContent) + { + case SMALLEST: + return 1; + case SMALLER: + return 2; + default: + case NORMAL: + return 3; + case LARGER: + return 4; + case LARGEST: + return 5; + } + } + + public void setMessageViewContent(int size) + { + switch (size) + { + case 1: + messageViewContent = TextSize.SMALLEST; + break; + case 2: + messageViewContent = TextSize.SMALLER; + break; + case 3: + messageViewContent = TextSize.NORMAL; + break; + case 4: + messageViewContent = TextSize.LARGER; + break; + case 5: + messageViewContent = TextSize.LARGEST; + break; + } + } +} diff --git a/src/com/fsck/k9/Identity.java b/src/com/fsck/k9/Identity.java index 8386fa7b12..aa09625e3a 100644 --- a/src/com/fsck/k9/Identity.java +++ b/src/com/fsck/k9/Identity.java @@ -1,79 +1,79 @@ -package com.fsck.k9; - -import java.io.Serializable; - -public class Identity implements Serializable -{ - private String mDescription; - private String mName; - private String mEmail; - private String mSignature; - private boolean mSignatureUse; - private String replyTo; - - public synchronized String getName() - { - return mName; - } - - public synchronized void setName(String name) - { - mName = name; - } - - public synchronized String getEmail() - { - return mEmail; - } - - public synchronized void setEmail(String email) - { - mEmail = email; - } - - public synchronized boolean getSignatureUse() - { - return mSignatureUse; - } - - public synchronized void setSignatureUse(boolean signatureUse) - { - mSignatureUse = signatureUse; - } - - public synchronized String getSignature() - { - return mSignature; - } - - public synchronized void setSignature(String signature) - { - mSignature = signature; - } - - public synchronized String getDescription() - { - return mDescription; - } - - public synchronized void setDescription(String description) - { - mDescription = description; - } - - public synchronized String getReplyTo() - { - return replyTo; - } - - public synchronized void setReplyTo(String replyTo) - { - this.replyTo = replyTo; - } - - @Override - public synchronized String toString() - { - return "Account.Identity(description=" + mDescription + ", name=" + mName + ", email=" + mEmail + ", replyTo=" + replyTo + ", signature=" + mSignature; - } -} +package com.fsck.k9; + +import java.io.Serializable; + +public class Identity implements Serializable +{ + private String mDescription; + private String mName; + private String mEmail; + private String mSignature; + private boolean mSignatureUse; + private String replyTo; + + public synchronized String getName() + { + return mName; + } + + public synchronized void setName(String name) + { + mName = name; + } + + public synchronized String getEmail() + { + return mEmail; + } + + public synchronized void setEmail(String email) + { + mEmail = email; + } + + public synchronized boolean getSignatureUse() + { + return mSignatureUse; + } + + public synchronized void setSignatureUse(boolean signatureUse) + { + mSignatureUse = signatureUse; + } + + public synchronized String getSignature() + { + return mSignature; + } + + public synchronized void setSignature(String signature) + { + mSignature = signature; + } + + public synchronized String getDescription() + { + return mDescription; + } + + public synchronized void setDescription(String description) + { + mDescription = description; + } + + public synchronized String getReplyTo() + { + return replyTo; + } + + public synchronized void setReplyTo(String replyTo) + { + this.replyTo = replyTo; + } + + @Override + public synchronized String toString() + { + return "Account.Identity(description=" + mDescription + ", name=" + mName + ", email=" + mEmail + ", replyTo=" + replyTo + ", signature=" + mSignature; + } +} diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index 9274ac8401..d81e80b257 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -1,2278 +1,2278 @@ -package com.fsck.k9.activity; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.Handler; -import android.text.Spannable; -import android.text.style.TextAppearanceSpan; -import android.util.Config; -import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.BaseAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.fsck.k9.Account; -import com.fsck.k9.AccountStats; -import com.fsck.k9.FontSizes; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; -import com.fsck.k9.R; -import com.fsck.k9.SearchSpecification; -import com.fsck.k9.activity.setup.AccountSettings; -import com.fsck.k9.activity.setup.FolderSettings; -import com.fsck.k9.activity.setup.Prefs; -import com.fsck.k9.controller.MessagingController; -import com.fsck.k9.controller.MessagingController.SORT_TYPE; -import com.fsck.k9.controller.MessagingListener; -import com.fsck.k9.helper.Utility; -import com.fsck.k9.mail.Address; -import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Folder; -import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.LocalStore; -import com.fsck.k9.mail.store.LocalStore.LocalFolder; -import com.fsck.k9.mail.store.LocalStore.LocalMessage; - - -/** - * MessageList is the primary user interface for the program. This Activity - * shows a list of messages. - * From this Activity the user can perform all standard message operations. - */ -public class MessageList - extends K9Activity - implements OnClickListener, AdapterView.OnItemClickListener -{ - private static final int DIALOG_MARK_ALL_AS_READ = 1; - - private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1; - private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2; - private static final int ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH = 3; - private static final int ACTIVITY_CHOOSE_FOLDER_COPY_BATCH = 4; - - private static final String EXTRA_ACCOUNT = "account"; - private static final String EXTRA_FOLDER = "folder"; - private static final String EXTRA_QUERY = "query"; - private static final String EXTRA_QUERY_FLAGS = "queryFlags"; - private static final String EXTRA_FORBIDDEN_FLAGS = "forbiddenFlags"; - private static final String EXTRA_INTEGRATE = "integrate"; - private static final String EXTRA_ACCOUNT_UUIDS = "accountUuids"; - private static final String EXTRA_FOLDER_NAMES = "folderNames"; - private static final String EXTRA_TITLE = "title"; - private static final String EXTRA_LIST_POSITION = "listPosition"; - - private ListView mListView; - - private boolean mTouchView = true; - - private MessageListAdapter mAdapter; - - private FolderInfoHolder mCurrentFolder; - - private LayoutInflater mInflater; - - private MessagingController mController; - - private Account mAccount; - private int mUnreadMessageCount = 0; - - private GestureDetector gestureDetector; - private View.OnTouchListener gestureListener; - /** - * Stores the name of the folder that we want to open as soon as possible - * after load. - */ - private String mFolderName; - - /** - * If we're doing a search, this contains the query string. - */ - private String mQueryString; - private Flag[] mQueryFlags = null; - private Flag[] mForbiddenFlags = null; - private boolean mIntegrate = false; - private String[] mAccountUuids = null; - private String[] mFolderNames = null; - private String mTitle; - - private MessageListHandler mHandler = new MessageListHandler(); - - private SORT_TYPE sortType = SORT_TYPE.SORT_DATE; - - private boolean sortAscending = true; - private boolean sortDateAscending = false; - - private boolean mStars = true; - private boolean mCheckboxes = true; - private int mSelectedCount = 0; - - private View mBatchButtonArea; - private ImageButton mBatchReadButton; - private ImageButton mBatchDeleteButton; - private ImageButton mBatchFlagButton; - private ImageButton mBatchDoneButton; - - private FontSizes mFontSizes = K9.getFontSizes(); - - private Bundle mState = null; - - class MessageListHandler extends Handler - { - public void removeMessage(final List messages) - { - runOnUiThread(new Runnable() - { - public void run() - { - for (MessageInfoHolder message : messages) - { - if (message != null) - { - if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) - { - if (message.selected && mSelectedCount > 0) - { - mSelectedCount--; - } - mAdapter.messages.remove(message); - } - } - } - resetUnreadCountOnThread(); - - mAdapter.notifyDataSetChanged(); - toggleBatchButtons(); - } - }); - } - - public void addMessages(final List messages) - { - final boolean wasEmpty = mAdapter.messages.isEmpty(); - runOnUiThread(new Runnable() - { - public void run() - { - for (final MessageInfoHolder message : messages) - { - if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) - { - int index; - synchronized (mAdapter.messages) - { - index = Collections.binarySearch(mAdapter.messages, message); - } - - if (index < 0) - { - index = (index * -1) - 1; - } - - mAdapter.messages.add(index, message); - } - } - - if (wasEmpty) - { - mListView.setSelection(0); - } - resetUnreadCountOnThread(); - - mAdapter.notifyDataSetChanged(); - } - }); - } - - private void resetUnreadCount() - { - runOnUiThread(new Runnable() - { - public void run() - { - resetUnreadCountOnThread(); - } - }); - } - - private void resetUnreadCountOnThread() - { - if (mQueryString != null) - { - int unreadCount = 0; - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - unreadCount += holder.read ? 0 : 1; - } - } - mUnreadMessageCount = unreadCount; - refreshTitleOnThread(); - } - } - - private void sortMessages() - { - runOnUiThread(new Runnable() - { - public void run() - { - synchronized (mAdapter.messages) - { - Collections.sort(mAdapter.messages); - } - mAdapter.notifyDataSetChanged(); - } - }); - } - - public void folderLoading(String folder, boolean loading) - { - if (mCurrentFolder != null && mCurrentFolder.name.equals(folder)) - { - mCurrentFolder.loading = loading; - } - } - - private void refreshTitle() - { - runOnUiThread(new Runnable() - { - public void run() - { - refreshTitleOnThread(); - } - }); - } - - private void refreshTitleOnThread() - { - setWindowTitle(); - setWindowProgress(); - } - - private void setWindowProgress() - { - int level = Window.PROGRESS_END; - - if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) - { - level = (Window.PROGRESS_END / mAdapter.mListener.getFolderTotal()) * (mAdapter.mListener.getFolderCompleted()) ; - if (level > Window.PROGRESS_END) - { - level = Window.PROGRESS_END; - } - } - - getWindow().setFeatureInt(Window.FEATURE_PROGRESS, level); - } - - private void setWindowTitle() - { - String displayName; - - if (mFolderName != null) - { - displayName = mFolderName; - - if (K9.INBOX.equalsIgnoreCase(displayName)) - { - displayName = getString(R.string.special_mailbox_name_inbox); - } - - String dispString = mAdapter.mListener.formatHeader(MessageList.this, getString(R.string.message_list_title, mAccount.getDescription(), displayName), mUnreadMessageCount, getTimeFormat()); - setTitle(dispString); - } - else if (mQueryString != null) - { - if (mTitle != null) - { - String dispString = mAdapter.mListener.formatHeader(MessageList.this, mTitle, mUnreadMessageCount, getTimeFormat()); - setTitle(dispString); - } - else - { - setTitle(getString(R.string.search_results) + ": "+ mQueryString); - } - } - } - - public void progress(final boolean progress) - { - runOnUiThread(new Runnable() - { - public void run() - { - showProgressIndicator(progress); - } - }); - } - } - - public static void actionHandleFolder(Context context, Account account, String folder) - { - Intent intent = actionHandleFolderIntent(context,account,folder); - context.startActivity(intent); - } - - public static Intent actionHandleFolderIntent(Context context, Account account, String folder) - { - Intent intent = new Intent(context, MessageList.class); - intent.putExtra(EXTRA_ACCOUNT, account.getUuid()); - - if (folder != null) - { - intent.putExtra(EXTRA_FOLDER, folder); - } - return intent; - } - - public static void actionHandle(Context context, String title, String queryString, boolean integrate, Flag[] flags, Flag[] forbiddenFlags) - { - Intent intent = new Intent(context, MessageList.class); - intent.putExtra(EXTRA_QUERY, queryString); - if (flags != null) - { - intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(flags, ',')); - } - if (forbiddenFlags != null) - { - intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(forbiddenFlags, ',')); - } - intent.putExtra(EXTRA_INTEGRATE, integrate); - intent.putExtra(EXTRA_TITLE, title); - context.startActivity(intent); - } - - public static void actionHandle(Context context, String title, SearchSpecification searchSpecification) - { - Intent intent = new Intent(context, MessageList.class); - intent.putExtra(EXTRA_QUERY, searchSpecification.getQuery()); - if (searchSpecification.getRequiredFlags() != null) - { - intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(searchSpecification.getRequiredFlags(), ',')); - } - if (searchSpecification.getForbiddenFlags() != null) - { - intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(searchSpecification.getForbiddenFlags(), ',')); - } - intent.putExtra(EXTRA_INTEGRATE, searchSpecification.isIntegrate()); - intent.putExtra(EXTRA_ACCOUNT_UUIDS, searchSpecification.getAccountUuids()); - intent.putExtra(EXTRA_FOLDER_NAMES, searchSpecification.getFolderNames()); - intent.putExtra(EXTRA_TITLE, title); - context.startActivity(intent); - } - - public void onItemClick(AdapterView parent, View view, int position, long id) - { - if (mCurrentFolder != null && ((position+1) == mAdapter.getCount())) - { - mController.loadMoreMessages(mAccount, mFolderName, mAdapter.mListener); - return; - } - - MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); - if (mSelectedCount > 0) - { - // In multiselect mode make sure that clicking on the item results - // in toggling the 'selected' checkbox. - setSelected(message, !message.selected); - return; - } - else - { - onOpenMessage(message); - return; - } - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - mInflater = getLayoutInflater(); - initializeLayout(); - onNewIntent(getIntent()); - } - - @Override - public void onNewIntent(Intent intent) - { - setIntent(intent); // onNewIntent doesn't autoset our "internal" intent - - // Only set "touchable" when we're first starting up the activity. - // Otherwise we get force closes when the user toggles it midstream. - mTouchView = K9.messageListTouchable(); - - String accountUuid = intent.getStringExtra(EXTRA_ACCOUNT); - mAccount = Preferences.getPreferences(this).getAccount(accountUuid); - mFolderName = intent.getStringExtra(EXTRA_FOLDER); - mQueryString = intent.getStringExtra(EXTRA_QUERY); - - String queryFlags = intent.getStringExtra(EXTRA_QUERY_FLAGS); - if (queryFlags != null) - { - String[] flagStrings = queryFlags.split(","); - mQueryFlags = new Flag[flagStrings.length]; - for (int i = 0; i < flagStrings.length; i++) - { - mQueryFlags[i] = Flag.valueOf(flagStrings[i]); - } - } - String forbiddenFlags = intent.getStringExtra(EXTRA_FORBIDDEN_FLAGS); - if (forbiddenFlags != null) - { - String[] flagStrings = forbiddenFlags.split(","); - mForbiddenFlags = new Flag[flagStrings.length]; - for (int i = 0; i < flagStrings.length; i++) - { - mForbiddenFlags[i] = Flag.valueOf(flagStrings[i]); - } - } - mIntegrate = intent.getBooleanExtra(EXTRA_INTEGRATE, false); - mAccountUuids = intent.getStringArrayExtra(EXTRA_ACCOUNT_UUIDS); - mFolderNames = intent.getStringArrayExtra(EXTRA_FOLDER_NAMES); - mTitle = intent.getStringExtra(EXTRA_TITLE); - - // Take the initial folder into account only if we are *not* restoring - // the activity already. - if (mFolderName == null && mQueryString == null) - { - mFolderName = mAccount.getAutoExpandFolderName(); - } - - mAdapter = new MessageListAdapter(); - final Object previousData = getLastNonConfigurationInstance(); - - if (previousData != null) - { - //noinspection unchecked - mAdapter.messages.addAll((List) previousData); - } - - if (mFolderName != null) - { - mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount); - } - - mController = MessagingController.getInstance(getApplication()); - mListView.setAdapter(mAdapter); - } - - @Override - public void onPause() - { - super.onPause(); - mController.removeListener(mAdapter.mListener); - saveListState(); - } - - public void saveListState() - { - mState = new Bundle(); - mState.putInt(EXTRA_LIST_POSITION, mListView.getSelectedItemPosition()); - } - - public void restoreListState() - { - if (mState == null) - { - return; - } - - int pos = mState.getInt(EXTRA_LIST_POSITION, ListView.INVALID_POSITION); - - if (pos >= mListView.getCount()) - { - pos = mListView.getCount() - 1; - } - - if (pos == ListView.INVALID_POSITION) - { - mListView.setSelected(false); - } - else - { - mListView.setSelection(pos); - } - } - - /** - * On resume we refresh messages for the folder that is currently open. - * This guarantees that things like unread message count and read status - * are updated. - */ - @Override - public void onResume() - { - super.onResume(); - - mStars = K9.messageListStars(); - mCheckboxes = K9.messageListCheckboxes(); - - sortType = mController.getSortType(); - sortAscending = mController.isSortAscending(sortType); - sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); - - mController.addListener(mAdapter.mListener); - mAdapter.messages.clear(); - mAdapter.notifyDataSetChanged(); - - if (mFolderName != null) - { - mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener); - mController.notifyAccountCancel(this, mAccount); - - MessagingController.getInstance(getApplication()).notifyAccountCancel(this, mAccount); - - mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener); - } - else if (mQueryString != null) - { - mController.searchLocalMessages(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener); - } - - mHandler.refreshTitle(); - - restoreListState(); - } - - private void initializeLayout() - { - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - requestWindowFeature(Window.FEATURE_PROGRESS); - setContentView(R.layout.message_list); - - mListView = (ListView) findViewById(R.id.message_list); - mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET); - mListView.setLongClickable(true); - mListView.setFastScrollEnabled(true); - mListView.setScrollingCacheEnabled(true); - mListView.setOnItemClickListener(this); - - registerForContextMenu(mListView); - - mBatchButtonArea = findViewById(R.id.batch_button_area); - mBatchReadButton = (ImageButton) findViewById(R.id.batch_read_button); - mBatchReadButton.setOnClickListener(this); - mBatchDeleteButton = (ImageButton) findViewById(R.id.batch_delete_button); - mBatchDeleteButton.setOnClickListener(this); - mBatchFlagButton = (ImageButton) findViewById(R.id.batch_flag_button); - mBatchFlagButton.setOnClickListener(this); - mBatchDoneButton = (ImageButton) findViewById(R.id.batch_done_button); - - mBatchDoneButton.setOnClickListener(this); - - // Gesture detection - gestureDetector = new GestureDetector(new MyGestureDetector()); - gestureListener = new View.OnTouchListener() - { - public boolean onTouch(View v, MotionEvent event) - { - if (gestureDetector.onTouchEvent(event)) - { - return true; - } - return false; - } - }; - - mListView.setOnTouchListener(gestureListener); - } - - @Override - public Object onRetainNonConfigurationInstance() - { - return mAdapter.messages; - } - - @Override - public void onBackPressed() - { - // This will be called either automatically for you on 2.0 - // or later, or by the code above on earlier versions of the - // platform. - if (K9.manageBack()) - { - if (mQueryString == null) - { - onShowFolderList(); - } - else - { - onAccounts(); - } - } - else - { - finish(); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - if ( - // XXX TODO - when we go to android 2.0, uncomment this - // android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR && - keyCode == KeyEvent.KEYCODE_BACK - && event.getRepeatCount() == 0 - ) - { - // Take care of calling this method on earlier versions of - // the platform where it doesn't exist. - onBackPressed(); - return true; - } - - // Shortcuts that work no matter what is selected - switch (keyCode) - { - case KeyEvent.KEYCODE_DPAD_LEFT: - { - if (mBatchButtonArea.hasFocus()) - { - return false; - } - else - { - return true; - } - } - case KeyEvent.KEYCODE_DPAD_RIGHT: - { - if (mBatchButtonArea.hasFocus()) - { - return false; - } - else - { - return true; - } - } - case KeyEvent.KEYCODE_C: - { - onCompose(); - return true; - } - case KeyEvent.KEYCODE_Q: - { - onShowFolderList(); - return true; - } - case KeyEvent.KEYCODE_O: - { - onCycleSort(); - return true; - } - case KeyEvent.KEYCODE_I: - { - onToggleSortAscending(); - return true; - } - case KeyEvent.KEYCODE_H: - { - Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG); - toast.show(); - return true; - } - } - - boolean result; - int position = mListView.getSelectedItemPosition(); - try - { - if (position >= 0) - { - MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); - - if (message != null) - { - switch (keyCode) - { - case KeyEvent.KEYCODE_DEL: - { - onDelete(message, position); - return true; - } - case KeyEvent.KEYCODE_S: - { - setSelected(message, !message.selected); - return true; - } - case KeyEvent.KEYCODE_D: - { - onDelete(message, position); - return true; - } - case KeyEvent.KEYCODE_F: - { - onForward(message); - return true; - } - case KeyEvent.KEYCODE_A: - { - onReplyAll(message); - return true; - } - case KeyEvent.KEYCODE_R: - { - onReply(message); - return true; - } - case KeyEvent.KEYCODE_G: - { - onToggleFlag(message); - return true; - } - case KeyEvent.KEYCODE_M: - { - onMove(message); - return true; - } - case KeyEvent.KEYCODE_V: - { - onArchive(message); - return true; - } - case KeyEvent.KEYCODE_Y: - { - onCopy(message); - return true; - } - case KeyEvent.KEYCODE_Z: - { - onToggleRead(message); - return true; - } - } - } - } - } - finally - { - result = super.onKeyDown(keyCode, event); - } - - return result; - } - - private void onOpenMessage(MessageInfoHolder message) - { - if (message.folder.name.equals(message.message.getFolder().getAccount().getDraftsFolderName())) - { - MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message); - } - else - { - // Need to get the list before the sort starts - ArrayList messageRefs = new ArrayList(); - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - MessageReference ref = holder.message.makeMessageReference(); - messageRefs.add(ref); - } - } - MessageReference ref = message.message.makeMessageReference(); - Log.i(K9.LOG_TAG, "MessageList sending message " + ref); - - MessageView.actionView(this, ref, messageRefs); - } - - /* - * We set read=true here for UI performance reasons. The actual value - * will get picked up on the refresh when the Activity is resumed but - * that may take a second or so and we don't want this to show and - * then go away. I've gone back and forth on this, and this gives a - * better UI experience, so I am putting it back in. - */ - if (!message.read) - { - message.read = true; - mHandler.sortMessages(); - } - } - - private void onAccounts() - { - Accounts.listAccounts(this); - finish(); - } - - private void onShowFolderList() - { - FolderList.actionHandleAccount(this, mAccount); - finish(); - } - - private void onCompose() - { - if (mQueryString != null) - { - /* - * If we have a query string, we don't have an account to let - * compose start the default action. - */ - MessageCompose.actionCompose(this, null); - } - else - { - MessageCompose.actionCompose(this, mAccount); - } - } - - private void onEditPrefs() - { - Prefs.actionPrefs(this); - } - - private void onEditAccount() - { - AccountSettings.actionSettings(this, mAccount); - } - - private void changeSort(SORT_TYPE newSortType) - { - if (sortType == newSortType) - { - onToggleSortAscending(); - } - else - { - sortType = newSortType; - mController.setSortType(sortType); - sortAscending = mController.isSortAscending(sortType); - sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); - reSort(); - } - } - - private void reSort() - { - int toastString = sortType.getToast(sortAscending); - - Toast toast = Toast.makeText(this, toastString, Toast.LENGTH_SHORT); - toast.show(); - - mHandler.sortMessages(); - } - - private void onCycleSort() - { - SORT_TYPE[] sorts = SORT_TYPE.values(); - int curIndex = 0; - - for (int i = 0; i < sorts.length; i++) - { - if (sorts[i] == sortType) - { - curIndex = i; - break; - } - } - - curIndex++; - - if (curIndex == sorts.length) - { - curIndex = 0; - } - - changeSort(sorts[curIndex]); - } - - private void onToggleSortAscending() - { - mController.setSortAscending(sortType, !sortAscending); - - sortAscending = mController.isSortAscending(sortType); - sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); - - reSort(); - } - - private void onDelete(MessageInfoHolder holder, int position) - { - mAdapter.removeMessage(holder); - mController.deleteMessages(new Message[] { holder.message }, null); - } - - private void onMove(MessageInfoHolder holder) - { - if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) - { - return; - } - - if (mController.isMoveCapable(holder.message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - - final Account account = holder.message.getFolder().getAccount(); - - Intent intent = new Intent(this, ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); - intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name); - intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); - intent.putExtra(ChooseFolder.EXTRA_MESSAGE, holder.message.makeMessageReference()); - startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE); - } - - private void onArchive(MessageInfoHolder holder) - { - if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) - { - return; - } - - if (mController.isMoveCapable(holder.message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - - onMoveChosen(holder, holder.message.getFolder().getAccount().getArchiveFolderName()); - } - - private void onSpam(MessageInfoHolder holder) - { - if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) - { - return; - } - - if (mController.isMoveCapable(holder.message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - - onMoveChosen(holder, holder.message.getFolder().getAccount().getSpamFolderName()); - } - - private void onCopy(MessageInfoHolder holder) - { - if (mController.isCopyCapable(holder.message.getFolder().getAccount()) == false) - { - return; - } - - if (mController.isCopyCapable(holder.message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - - final Account account = holder.message.getFolder().getAccount(); - - Intent intent = new Intent(this, ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); - intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name); - intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); - intent.putExtra(ChooseFolder.EXTRA_MESSAGE, holder.message.makeMessageReference()); - startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) - { - if (resultCode != RESULT_OK) - return; - - switch (requestCode) - { - case ACTIVITY_CHOOSE_FOLDER_MOVE: - case ACTIVITY_CHOOSE_FOLDER_COPY: - { - if (data == null) - return; - - final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); - final MessageReference ref = (MessageReference)data.getSerializableExtra(ChooseFolder.EXTRA_MESSAGE); - final MessageInfoHolder m = mAdapter.getMessage(ref); - - if ((destFolderName != null) && (m != null)) - { - final Account account = m.message.getFolder().getAccount(); - - account.setLastSelectedFolderName(destFolderName); - - switch (requestCode) - { - case ACTIVITY_CHOOSE_FOLDER_MOVE: - onMoveChosen(m, destFolderName); - break; - - case ACTIVITY_CHOOSE_FOLDER_COPY: - onCopyChosen(m, destFolderName); - break; - } - } - break; - } - case ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH: - case ACTIVITY_CHOOSE_FOLDER_COPY_BATCH: - { - final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); - final String accountUuid = data.getStringExtra(ChooseFolder.EXTRA_ACCOUNT); - final Account account = Preferences.getPreferences(this).getAccount(accountUuid); - - account.setLastSelectedFolderName(destFolderName); - - switch (requestCode) - { - case ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH: - onMoveChosenBatch(destFolderName); - break; - - case ACTIVITY_CHOOSE_FOLDER_COPY_BATCH: - onCopyChosenBatch(destFolderName); - break; - } - } - } - } - - private void onMoveChosen(MessageInfoHolder holder, String folderName) - { - if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == true && folderName != null) - { - if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) - { - return; - } - mAdapter.removeMessage(holder); - mController.moveMessage(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), holder.message, folderName, null); - } - } - - private void onCopyChosen(MessageInfoHolder holder, String folderName) - { - if (mController.isCopyCapable(holder.message.getFolder().getAccount()) == true && folderName != null) - { - mController.copyMessage(holder.message.getFolder().getAccount(), - holder.message.getFolder().getName(), holder.message, folderName, null); - } - } - - private void onReply(MessageInfoHolder holder) - { - MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false); - } - - private void onReplyAll(MessageInfoHolder holder) - { - MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true); - } - - private void onForward(MessageInfoHolder holder) - { - MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message); - } - - private void onMarkAllAsRead(final Account account, final String folder) - { - showDialog(DIALOG_MARK_ALL_AS_READ); - } - - private void onExpunge(final Account account, String folderName) - { - mController.expunge(account, folderName, null); - } - - @Override - public Dialog onCreateDialog(int id) - { - switch (id) - { - case DIALOG_MARK_ALL_AS_READ: - return createMarkAllAsReadDialog(); - } - - return super.onCreateDialog(id); - } - - @Override - public void onPrepareDialog(int id, Dialog dialog) - { - switch (id) - { - case DIALOG_MARK_ALL_AS_READ: - { - if (mCurrentFolder != null) - { - ((AlertDialog)dialog).setMessage(getString(R.string.mark_all_as_read_dlg_instructions_fmt, - mCurrentFolder.displayName)); - } - break; - } - default: - { - super.onPrepareDialog(id, dialog); - } - } - } - - private Dialog createMarkAllAsReadDialog() - { - return new AlertDialog.Builder(this) - .setTitle(R.string.mark_all_as_read_dlg_title) - .setMessage(getString(R.string.mark_all_as_read_dlg_instructions_fmt, - mCurrentFolder.displayName)) - .setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int whichButton) - { - dismissDialog(DIALOG_MARK_ALL_AS_READ); - - try - { - mController.markAllMessagesRead(mAccount, mCurrentFolder.name); - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - holder.read = true; - } - } - mHandler.sortMessages(); - } - catch (Exception e) - { - // Ignore - } - } - }) - .setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int whichButton) - { - dismissDialog(DIALOG_MARK_ALL_AS_READ); - } - }) - .create(); - } - - private void onToggleRead(MessageInfoHolder holder) - { - mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.SEEN, !holder.read); - holder.read = !holder.read; - mHandler.sortMessages(); - } - - private void onToggleFlag(MessageInfoHolder holder) - { - mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.FLAGGED, !holder.flagged); - holder.flagged = !holder.flagged; - mHandler.sortMessages(); - } - - private void checkMail(Account account, String folderName) - { - mController.synchronizeMailbox(account, folderName, mAdapter.mListener, null); - sendMail(account); - } - - private void sendMail(Account account) - { - mController.sendPendingMessages(account, mAdapter.mListener); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - int itemId = item.getItemId(); - switch (itemId) - { - case R.id.compose: - { - onCompose(); - return true; - } - case R.id.accounts: - { - onAccounts(); - return true; - } - case R.id.set_sort_date: - { - changeSort(SORT_TYPE.SORT_DATE); - return true; - } - case R.id.set_sort_subject: - { - changeSort(SORT_TYPE.SORT_SUBJECT); - return true; - } - case R.id.set_sort_sender: - { - changeSort(SORT_TYPE.SORT_SENDER); - return true; - } - case R.id.set_sort_flag: - { - changeSort(SORT_TYPE.SORT_FLAGGED); - return true; - } - case R.id.set_sort_unread: - { - changeSort(SORT_TYPE.SORT_UNREAD); - return true; - } - case R.id.set_sort_attach: - { - changeSort(SORT_TYPE.SORT_ATTACHMENT); - return true; - } - case R.id.select_all: - case R.id.batch_select_all: - { - setAllSelected(true); - toggleBatchButtons(); - return true; - } - case R.id.batch_deselect_all: - { - setAllSelected(false); - toggleBatchButtons(); - return true; - } - case R.id.batch_delete_op: - { - deleteSelected(); - return true; - } - case R.id.batch_mark_read_op: - { - flagSelected(Flag.SEEN, true); - return true; - } - case R.id.batch_mark_unread_op: - { - flagSelected(Flag.SEEN, false); - return true; - } - case R.id.batch_flag_op: - { - flagSelected(Flag.FLAGGED, true); - return true; - } - case R.id.batch_unflag_op: - { - flagSelected(Flag.FLAGGED, false); - return true; - } - case R.id.settings: - { - if (mQueryString == null) - { - break; - } - - /* - * Fall-through in search results view. Otherwise a sub-menu - * with only one option would be opened. - */ - } - case R.id.app_settings: - { - onEditPrefs(); - return true; - } - } - - if (mQueryString != null) - { - // None of the options after this point are "safe" for search results - //TODO: This is not true for "unread" and "starred" searches in regular folders - return false; - } - - switch (itemId) - { - case R.id.check_mail: - { - if (mFolderName != null) - { - checkMail(mAccount, mFolderName); - } - return true; - } - case R.id.send_messages: - { - sendMail(mAccount); - return true; - } - case R.id.list_folders: - { - onShowFolderList(); - return true; - } - case R.id.mark_all_as_read: - { - if (mFolderName != null) - { - onMarkAllAsRead(mAccount, mFolderName); - } - return true; - } - case R.id.folder_settings: - { - if (mFolderName != null) - { - FolderSettings.actionSettings(this, mAccount, mFolderName); - } - return true; - } - case R.id.account_settings: - { - onEditAccount(); - return true; - } - case R.id.batch_copy_op: - { - onCopyBatch(); - return true; - } - case R.id.batch_archive_op: - { - onArchiveBatch(); - return true; - } - case R.id.batch_spam_op: - { - onSpamBatch(); - return true; - } - case R.id.batch_move_op: - { - onMoveBatch(); - return true; - } - case R.id.expunge: - { - if (mCurrentFolder != null) - { - onExpunge(mAccount, mCurrentFolder.name); - } - return true; - } - default: - { - return super.onOptionsItemSelected(item); - } - } - } - - private final int[] batch_ops = { R.id.batch_copy_op, R.id.batch_delete_op, R.id.batch_flag_op, - R.id.batch_unflag_op, R.id.batch_mark_read_op, R.id.batch_mark_unread_op, - R.id.batch_archive_op, R.id.batch_spam_op, R.id.batch_move_op, - R.id.batch_select_all, R.id.batch_deselect_all - }; - - private void setOpsState(Menu menu, boolean state, boolean enabled) - { - for (int id : batch_ops) - { - menu.findItem(id).setVisible(state); - menu.findItem(id).setEnabled(enabled); - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) - { - boolean anySelected = anySelected(); - - menu.findItem(R.id.select_all).setVisible(! anySelected); - menu.findItem(R.id.batch_ops).setVisible(anySelected); - - setOpsState(menu, true, anySelected); - - if (mQueryString != null) - { - menu.findItem(R.id.mark_all_as_read).setVisible(false); - menu.findItem(R.id.list_folders).setVisible(false); - menu.findItem(R.id.expunge).setVisible(false); - menu.findItem(R.id.batch_archive_op).setVisible(false); - menu.findItem(R.id.batch_spam_op).setVisible(false); - menu.findItem(R.id.batch_move_op).setVisible(false); - menu.findItem(R.id.batch_copy_op).setVisible(false); - menu.findItem(R.id.check_mail).setVisible(false); - menu.findItem(R.id.send_messages).setVisible(false); - } - else - { - if (mCurrentFolder != null && mCurrentFolder.outbox) - { - menu.findItem(R.id.check_mail).setVisible(false); - } - else - { - menu.findItem(R.id.send_messages).setVisible(false); - } - - if (mCurrentFolder != null && K9.ERROR_FOLDER_NAME.equals(mCurrentFolder.name)) - { - menu.findItem(R.id.expunge).setVisible(false); - } - } - - boolean newFlagState = computeBatchDirection(true); - boolean newReadState = computeBatchDirection(false); - menu.findItem(R.id.batch_flag_op).setVisible(newFlagState); - menu.findItem(R.id.batch_unflag_op).setVisible(!newFlagState); - menu.findItem(R.id.batch_mark_read_op).setVisible(newReadState); - menu.findItem(R.id.batch_mark_unread_op).setVisible(!newReadState); - menu.findItem(R.id.batch_deselect_all).setVisible(anySelected); - menu.findItem(R.id.batch_select_all).setEnabled(true); - - return true; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.message_list_option, menu); - - return true; - } - - @Override - public boolean onContextItemSelected(MenuItem item) - { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); - MessageInfoHolder holder = (MessageInfoHolder) mAdapter.getItem(info.position); - - switch (item.getItemId()) - { - case R.id.open: - { - onOpenMessage(holder); - break; - } - case R.id.select: - { - setSelected(holder, true); - break; - } - case R.id.deselect: - { - setSelected(holder, false); - break; - } - case R.id.delete: - { - onDelete(holder, info.position); - break; - } - case R.id.reply: - { - onReply(holder); - break; - } - case R.id.reply_all: - { - onReplyAll(holder); - break; - } - case R.id.forward: - { - onForward(holder); - break; - } - case R.id.mark_as_read: - { - onToggleRead(holder); - break; - } - case R.id.flag: - { - onToggleFlag(holder); - break; - } - case R.id.archive: - { - onArchive(holder); - break; - } - case R.id.spam: - { - onSpam(holder); - break; - } - case R.id.move: - { - onMove(holder); - break; - } - case R.id.copy: - { - onCopy(holder); - break; - } - case R.id.send_alternate: - { - onSendAlternate(mAccount, holder); - break; - } - case R.id.same_sender: - { - MessageList.actionHandle(MessageList.this, - "From "+holder.sender, holder.sender, true, - null, null); - break; - } - } - return super.onContextItemSelected(item); - } - - public void onSendAlternate(Account account, MessageInfoHolder holder) - { - mController.sendAlternate(this, account, holder.message); - } - - public void showProgressIndicator(boolean status) - { - setProgressBarIndeterminateVisibility(status); - ProgressBar bar = (ProgressBar)mListView.findViewById(R.id.message_list_progress); - if (bar == null) - { - return; - } - - bar.setIndeterminate(true); - if (status) - { - bar.setVisibility(ProgressBar.VISIBLE); - } - else - { - bar.setVisibility(ProgressBar.INVISIBLE); - } - } - - class MyGestureDetector extends SimpleOnGestureListener - { - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) - { - if (e2 == null || e1 == null) - return true; - - float deltaX = e2.getX() - e1.getX(), - deltaY = e2.getY() - e1.getY(); - - boolean movedAcross = (Math.abs(deltaX) > Math.abs(deltaY * 4)); - boolean steadyHand = (Math.abs(deltaX / deltaY) > 2); - - if (movedAcross && steadyHand) - { - boolean selected = (deltaX > 0); - int position = mListView.pointToPosition((int)e1.getX(), (int)e1.getY()); - - if (position != AdapterView.INVALID_POSITION) - { - MessageInfoHolder msgInfoHolder = (MessageInfoHolder) mAdapter.getItem(position); - - if (msgInfoHolder != null && msgInfoHolder.selected != selected) - { - msgInfoHolder.selected = selected; - mSelectedCount += (selected ? 1 : -1); - mAdapter.notifyDataSetChanged(); - toggleBatchButtons(); - } - } - } - - return false; - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) - { - super.onCreateContextMenu(menu, v, menuInfo); - - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(info.position); - - if (message == null) - { - return; - } - - getMenuInflater().inflate(R.menu.message_list_context, menu); - - menu.setHeaderTitle((CharSequence) message.subject); - - if (message.read) - { - menu.findItem(R.id.mark_as_read).setTitle(R.string.mark_as_unread_action); - } - - if (message.flagged) - { - menu.findItem(R.id.flag).setTitle(R.string.unflag_action); - } - - Account account = message.message.getFolder().getAccount(); - if (mController.isCopyCapable(account) == false) - { - menu.findItem(R.id.copy).setVisible(false); - } - - if (mController.isMoveCapable(account) == false) - { - menu.findItem(R.id.move).setVisible(false); - menu.findItem(R.id.archive).setVisible(false); - menu.findItem(R.id.spam).setVisible(false); - } - - if (K9.FOLDER_NONE.equalsIgnoreCase(account.getArchiveFolderName())) - { - menu.findItem(R.id.archive).setVisible(false); - } - if (K9.FOLDER_NONE.equalsIgnoreCase(account.getSpamFolderName())) - { - menu.findItem(R.id.spam).setVisible(false); - } - - if (message.selected) - { - menu.findItem(R.id.select).setVisible(false); - menu.findItem(R.id.deselect).setVisible(true); - } - else - { - menu.findItem(R.id.select).setVisible(true); - menu.findItem(R.id.deselect).setVisible(false); - } - } - - class MessageListAdapter extends BaseAdapter - { - private final List messages = java.util.Collections.synchronizedList(new ArrayList()); - - private final ActivityListener mListener = new ActivityListener() - { - @Override - public void synchronizeMailboxStarted(Account account, String folder) - { - super.synchronizeMailboxStarted(account, folder); - - if (updateForMe(account, folder)) - { - mHandler.progress(true); - mHandler.folderLoading(folder, true); - } - mHandler.refreshTitle(); - } - - @Override - public void synchronizeMailboxFinished(Account account, String folder, - int totalMessagesInMailbox, int numNewMessages) - { - super.synchronizeMailboxFinished(account, folder, totalMessagesInMailbox, numNewMessages); - - if (updateForMe(account, folder)) - { - mHandler.progress(false); - mHandler.folderLoading(folder, false); - mHandler.sortMessages(); - } - mHandler.refreshTitle(); - } - - @Override - public void synchronizeMailboxFailed(Account account, String folder, String message) - { - super.synchronizeMailboxFailed(account, folder, message); - - if (updateForMe(account, folder)) - { - mHandler.progress(false); - mHandler.folderLoading(folder, false); - mHandler.sortMessages(); - } - mHandler.refreshTitle(); - } - - @Override - public void sendPendingMessagesStarted(Account account) - { - super.sendPendingMessagesStarted(account); - mHandler.refreshTitle(); - } - - @Override - public void sendPendingMessagesCompleted(Account account) - { - super.sendPendingMessagesCompleted(account); - mHandler.refreshTitle(); - } - - @Override - public void sendPendingMessagesFailed(Account account) - { - super.sendPendingMessagesFailed(account); - mHandler.refreshTitle(); - } - - @Override - public void synchronizeMailboxProgress(Account account, String folder, int completed, int total) - { - super.synchronizeMailboxProgress(account, folder, completed, total); - mHandler.refreshTitle(); - } - - @Override - public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) - { - addOrUpdateMessage(account, folder, message, true); - } - - @Override - public void synchronizeMailboxRemovedMessage(Account account, String folder,Message message) - { - MessageInfoHolder holder = getMessage(message); - if (holder == null) - { - Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " + message.getUid()); - } - else - { - removeMessage(holder); - } - } - - @Override - public void listLocalMessagesStarted(Account account, String folder) - { - if ((mQueryString != null && folder == null) || - (account != null && account.equals(mAccount)) - ) - { - mHandler.progress(true); - if (folder != null) - { - mHandler.folderLoading(folder, true); - } - } - } - - @Override - public void listLocalMessagesFailed(Account account, String folder, String message) - { - if ((mQueryString != null && folder == null) || - (account != null && account.equals(mAccount))) - { - mHandler.sortMessages(); - mHandler.progress(false); - if (folder != null) - { - mHandler.folderLoading(folder, false); - } - } - } - - @Override - public void listLocalMessagesFinished(Account account, String folder) - { - if ((mQueryString != null && folder == null) || - (account != null && account.equals(mAccount))) - { - mHandler.sortMessages(); - mHandler.progress(false); - if (folder != null) - { - mHandler.folderLoading(folder, false); - } - } - } - - @Override - public void listLocalMessagesRemoveMessage(Account account, String folder,Message message) - { - MessageInfoHolder holder = getMessage(message); - if (holder != null) - { - removeMessage(holder); - } - } - - @Override - public void listLocalMessagesAddMessages(Account account, String folder, List messages) - { - addOrUpdateMessages(account, folder, messages, false); - } - - @Override - public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) - { - addOrUpdateMessage(account, folder, message, false); - } - - @Override - public void searchStats(AccountStats stats) - { - mUnreadMessageCount = stats.unreadMessageCount; - mHandler.refreshTitle(); - } - - @Override - public void folderStatusChanged(Account account, String folder, int unreadMessageCount) - { - super.folderStatusChanged(account, folder, unreadMessageCount); - if (updateForMe(account, folder)) - { - mUnreadMessageCount = unreadMessageCount; - mHandler.refreshTitle(); - } - } - - @Override - public void pendingCommandsProcessing(Account account) - { - super.pendingCommandsProcessing(account); - mHandler.refreshTitle(); - } - - @Override - public void pendingCommandsFinished(Account account) - { - super.pendingCommandsFinished(account); - mHandler.refreshTitle(); - } - - @Override - public void pendingCommandStarted(Account account, String commandTitle) - { - super.pendingCommandStarted(account, commandTitle); - mHandler.refreshTitle(); - } - - @Override - public void pendingCommandCompleted(Account account, String commandTitle) - { - super.pendingCommandCompleted(account, commandTitle); - mHandler.refreshTitle(); - } - - @Override - public void messageUidChanged(Account account, String folder, String oldUid, String newUid) - { - MessageReference ref = new MessageReference(); - ref.accountUuid = account.getUuid(); - ref.folderName = folder; - ref.uid = oldUid; - - MessageInfoHolder holder = getMessage(ref); - if (holder != null) - { - holder.uid = newUid; - holder.message.setUid(newUid); - } - } - }; - - private boolean updateForMe(Account account, String folder) - { - if ((account.equals(mAccount) && mFolderName != null && folder.equals(mFolderName))) - { - return true; - } - else - { - return false; - } - } - - private Drawable mAttachmentIcon; - private Drawable mAnsweredIcon; - private View footerView = null; - - MessageListAdapter() - { - mAttachmentIcon = getResources().getDrawable(R.drawable.ic_mms_attachment_small); - mAnsweredIcon = getResources().getDrawable(R.drawable.ic_mms_answered_small); - } - - public void removeMessages(List holders) - { - if (holders != null) - { - mHandler.removeMessage(holders); - } - } - - public void removeMessage(MessageInfoHolder holder) - { - List messages = new ArrayList(); - messages.add(holder); - removeMessages(messages); - } - - private void addOrUpdateMessage(Account account, String folder, Message message, boolean verifyAgainstSearch) - { - List messages = new ArrayList(); - messages.add(message); - addOrUpdateMessages(account, folder, messages, verifyAgainstSearch); - } - - private void addOrUpdateMessages(Account account, String folder, List messages, boolean verifyAgainstSearch) - { - boolean needsSort = false; - final List messagesToAdd = new ArrayList(); - List messagesToRemove = new ArrayList(); - List messagesToSearch = new ArrayList(); - - for (Message message : messages) - { - MessageInfoHolder m = getMessage(message); - if (message.isSet(Flag.DELETED)) - { - if (m != null) - { - messagesToRemove.add(m); - } - } - else if (m == null) - { - if (updateForMe(account, folder)) - { - m = new MessageInfoHolder(message); - messagesToAdd.add(m); - } - else - { - if (mQueryString != null) - { - if (verifyAgainstSearch) - { - messagesToSearch.add(message); - } - else - { - m = new MessageInfoHolder(message); - messagesToAdd.add(m); - } - } - } - } - else - { - m.populate(message, new FolderInfoHolder(message.getFolder(), account), account); - needsSort = true; - } - } - - if (messagesToSearch.size() > 0) - { - mController.searchLocalMessages(mAccountUuids, mFolderNames, messagesToSearch.toArray(new Message[0]), mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, - new MessagingListener() - { - @Override - public void listLocalMessagesAddMessages(Account account, String folder, List messages) - { - addOrUpdateMessages(account, folder, messages, false); - } - }); - } - - if (messagesToRemove.size() > 0) - { - removeMessages(messagesToRemove); - } - - if (messagesToAdd.size() > 0) - { - mHandler.addMessages(messagesToAdd); - } - - if (needsSort) - { - mHandler.sortMessages(); - mHandler.resetUnreadCount(); - } - } - - public MessageInfoHolder getMessage(Message message) - { - return getMessage(message.makeMessageReference()); - } - - // XXX TODO - make this not use a for loop - public MessageInfoHolder getMessage(MessageReference messageReference) - { - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - /* - * 2010-06-21 - cketti - * Added null pointer check. Not sure what's causing 'holder' - * to be null. See log provided in issue 1749, comment #15. +package com.fsck.k9.activity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.text.Spannable; +import android.text.style.TextAppearanceSpan; +import android.util.Config; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.fsck.k9.Account; +import com.fsck.k9.AccountStats; +import com.fsck.k9.FontSizes; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.R; +import com.fsck.k9.SearchSpecification; +import com.fsck.k9.activity.setup.AccountSettings; +import com.fsck.k9.activity.setup.FolderSettings; +import com.fsck.k9.activity.setup.Prefs; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingController.SORT_TYPE; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.LocalStore; +import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; + + +/** + * MessageList is the primary user interface for the program. This Activity + * shows a list of messages. + * From this Activity the user can perform all standard message operations. + */ +public class MessageList + extends K9Activity + implements OnClickListener, AdapterView.OnItemClickListener +{ + private static final int DIALOG_MARK_ALL_AS_READ = 1; + + private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1; + private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2; + private static final int ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH = 3; + private static final int ACTIVITY_CHOOSE_FOLDER_COPY_BATCH = 4; + + private static final String EXTRA_ACCOUNT = "account"; + private static final String EXTRA_FOLDER = "folder"; + private static final String EXTRA_QUERY = "query"; + private static final String EXTRA_QUERY_FLAGS = "queryFlags"; + private static final String EXTRA_FORBIDDEN_FLAGS = "forbiddenFlags"; + private static final String EXTRA_INTEGRATE = "integrate"; + private static final String EXTRA_ACCOUNT_UUIDS = "accountUuids"; + private static final String EXTRA_FOLDER_NAMES = "folderNames"; + private static final String EXTRA_TITLE = "title"; + private static final String EXTRA_LIST_POSITION = "listPosition"; + + private ListView mListView; + + private boolean mTouchView = true; + + private MessageListAdapter mAdapter; + + private FolderInfoHolder mCurrentFolder; + + private LayoutInflater mInflater; + + private MessagingController mController; + + private Account mAccount; + private int mUnreadMessageCount = 0; + + private GestureDetector gestureDetector; + private View.OnTouchListener gestureListener; + /** + * Stores the name of the folder that we want to open as soon as possible + * after load. + */ + private String mFolderName; + + /** + * If we're doing a search, this contains the query string. + */ + private String mQueryString; + private Flag[] mQueryFlags = null; + private Flag[] mForbiddenFlags = null; + private boolean mIntegrate = false; + private String[] mAccountUuids = null; + private String[] mFolderNames = null; + private String mTitle; + + private MessageListHandler mHandler = new MessageListHandler(); + + private SORT_TYPE sortType = SORT_TYPE.SORT_DATE; + + private boolean sortAscending = true; + private boolean sortDateAscending = false; + + private boolean mStars = true; + private boolean mCheckboxes = true; + private int mSelectedCount = 0; + + private View mBatchButtonArea; + private ImageButton mBatchReadButton; + private ImageButton mBatchDeleteButton; + private ImageButton mBatchFlagButton; + private ImageButton mBatchDoneButton; + + private FontSizes mFontSizes = K9.getFontSizes(); + + private Bundle mState = null; + + class MessageListHandler extends Handler + { + public void removeMessage(final List messages) + { + runOnUiThread(new Runnable() + { + public void run() + { + for (MessageInfoHolder message : messages) + { + if (message != null) + { + if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) + { + if (message.selected && mSelectedCount > 0) + { + mSelectedCount--; + } + mAdapter.messages.remove(message); + } + } + } + resetUnreadCountOnThread(); + + mAdapter.notifyDataSetChanged(); + toggleBatchButtons(); + } + }); + } + + public void addMessages(final List messages) + { + final boolean wasEmpty = mAdapter.messages.isEmpty(); + runOnUiThread(new Runnable() + { + public void run() + { + for (final MessageInfoHolder message : messages) + { + if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) + { + int index; + synchronized (mAdapter.messages) + { + index = Collections.binarySearch(mAdapter.messages, message); + } + + if (index < 0) + { + index = (index * -1) - 1; + } + + mAdapter.messages.add(index, message); + } + } + + if (wasEmpty) + { + mListView.setSelection(0); + } + resetUnreadCountOnThread(); + + mAdapter.notifyDataSetChanged(); + } + }); + } + + private void resetUnreadCount() + { + runOnUiThread(new Runnable() + { + public void run() + { + resetUnreadCountOnThread(); + } + }); + } + + private void resetUnreadCountOnThread() + { + if (mQueryString != null) + { + int unreadCount = 0; + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + unreadCount += holder.read ? 0 : 1; + } + } + mUnreadMessageCount = unreadCount; + refreshTitleOnThread(); + } + } + + private void sortMessages() + { + runOnUiThread(new Runnable() + { + public void run() + { + synchronized (mAdapter.messages) + { + Collections.sort(mAdapter.messages); + } + mAdapter.notifyDataSetChanged(); + } + }); + } + + public void folderLoading(String folder, boolean loading) + { + if (mCurrentFolder != null && mCurrentFolder.name.equals(folder)) + { + mCurrentFolder.loading = loading; + } + } + + private void refreshTitle() + { + runOnUiThread(new Runnable() + { + public void run() + { + refreshTitleOnThread(); + } + }); + } + + private void refreshTitleOnThread() + { + setWindowTitle(); + setWindowProgress(); + } + + private void setWindowProgress() + { + int level = Window.PROGRESS_END; + + if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) + { + level = (Window.PROGRESS_END / mAdapter.mListener.getFolderTotal()) * (mAdapter.mListener.getFolderCompleted()) ; + if (level > Window.PROGRESS_END) + { + level = Window.PROGRESS_END; + } + } + + getWindow().setFeatureInt(Window.FEATURE_PROGRESS, level); + } + + private void setWindowTitle() + { + String displayName; + + if (mFolderName != null) + { + displayName = mFolderName; + + if (K9.INBOX.equalsIgnoreCase(displayName)) + { + displayName = getString(R.string.special_mailbox_name_inbox); + } + + String dispString = mAdapter.mListener.formatHeader(MessageList.this, getString(R.string.message_list_title, mAccount.getDescription(), displayName), mUnreadMessageCount, getTimeFormat()); + setTitle(dispString); + } + else if (mQueryString != null) + { + if (mTitle != null) + { + String dispString = mAdapter.mListener.formatHeader(MessageList.this, mTitle, mUnreadMessageCount, getTimeFormat()); + setTitle(dispString); + } + else + { + setTitle(getString(R.string.search_results) + ": "+ mQueryString); + } + } + } + + public void progress(final boolean progress) + { + runOnUiThread(new Runnable() + { + public void run() + { + showProgressIndicator(progress); + } + }); + } + } + + public static void actionHandleFolder(Context context, Account account, String folder) + { + Intent intent = actionHandleFolderIntent(context,account,folder); + context.startActivity(intent); + } + + public static Intent actionHandleFolderIntent(Context context, Account account, String folder) + { + Intent intent = new Intent(context, MessageList.class); + intent.putExtra(EXTRA_ACCOUNT, account.getUuid()); + + if (folder != null) + { + intent.putExtra(EXTRA_FOLDER, folder); + } + return intent; + } + + public static void actionHandle(Context context, String title, String queryString, boolean integrate, Flag[] flags, Flag[] forbiddenFlags) + { + Intent intent = new Intent(context, MessageList.class); + intent.putExtra(EXTRA_QUERY, queryString); + if (flags != null) + { + intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(flags, ',')); + } + if (forbiddenFlags != null) + { + intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(forbiddenFlags, ',')); + } + intent.putExtra(EXTRA_INTEGRATE, integrate); + intent.putExtra(EXTRA_TITLE, title); + context.startActivity(intent); + } + + public static void actionHandle(Context context, String title, SearchSpecification searchSpecification) + { + Intent intent = new Intent(context, MessageList.class); + intent.putExtra(EXTRA_QUERY, searchSpecification.getQuery()); + if (searchSpecification.getRequiredFlags() != null) + { + intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(searchSpecification.getRequiredFlags(), ',')); + } + if (searchSpecification.getForbiddenFlags() != null) + { + intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(searchSpecification.getForbiddenFlags(), ',')); + } + intent.putExtra(EXTRA_INTEGRATE, searchSpecification.isIntegrate()); + intent.putExtra(EXTRA_ACCOUNT_UUIDS, searchSpecification.getAccountUuids()); + intent.putExtra(EXTRA_FOLDER_NAMES, searchSpecification.getFolderNames()); + intent.putExtra(EXTRA_TITLE, title); + context.startActivity(intent); + } + + public void onItemClick(AdapterView parent, View view, int position, long id) + { + if (mCurrentFolder != null && ((position+1) == mAdapter.getCount())) + { + mController.loadMoreMessages(mAccount, mFolderName, mAdapter.mListener); + return; + } + + MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); + if (mSelectedCount > 0) + { + // In multiselect mode make sure that clicking on the item results + // in toggling the 'selected' checkbox. + setSelected(message, !message.selected); + return; + } + else + { + onOpenMessage(message); + return; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + mInflater = getLayoutInflater(); + initializeLayout(); + onNewIntent(getIntent()); + } + + @Override + public void onNewIntent(Intent intent) + { + setIntent(intent); // onNewIntent doesn't autoset our "internal" intent + + // Only set "touchable" when we're first starting up the activity. + // Otherwise we get force closes when the user toggles it midstream. + mTouchView = K9.messageListTouchable(); + + String accountUuid = intent.getStringExtra(EXTRA_ACCOUNT); + mAccount = Preferences.getPreferences(this).getAccount(accountUuid); + mFolderName = intent.getStringExtra(EXTRA_FOLDER); + mQueryString = intent.getStringExtra(EXTRA_QUERY); + + String queryFlags = intent.getStringExtra(EXTRA_QUERY_FLAGS); + if (queryFlags != null) + { + String[] flagStrings = queryFlags.split(","); + mQueryFlags = new Flag[flagStrings.length]; + for (int i = 0; i < flagStrings.length; i++) + { + mQueryFlags[i] = Flag.valueOf(flagStrings[i]); + } + } + String forbiddenFlags = intent.getStringExtra(EXTRA_FORBIDDEN_FLAGS); + if (forbiddenFlags != null) + { + String[] flagStrings = forbiddenFlags.split(","); + mForbiddenFlags = new Flag[flagStrings.length]; + for (int i = 0; i < flagStrings.length; i++) + { + mForbiddenFlags[i] = Flag.valueOf(flagStrings[i]); + } + } + mIntegrate = intent.getBooleanExtra(EXTRA_INTEGRATE, false); + mAccountUuids = intent.getStringArrayExtra(EXTRA_ACCOUNT_UUIDS); + mFolderNames = intent.getStringArrayExtra(EXTRA_FOLDER_NAMES); + mTitle = intent.getStringExtra(EXTRA_TITLE); + + // Take the initial folder into account only if we are *not* restoring + // the activity already. + if (mFolderName == null && mQueryString == null) + { + mFolderName = mAccount.getAutoExpandFolderName(); + } + + mAdapter = new MessageListAdapter(); + final Object previousData = getLastNonConfigurationInstance(); + + if (previousData != null) + { + //noinspection unchecked + mAdapter.messages.addAll((List) previousData); + } + + if (mFolderName != null) + { + mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount); + } + + mController = MessagingController.getInstance(getApplication()); + mListView.setAdapter(mAdapter); + } + + @Override + public void onPause() + { + super.onPause(); + mController.removeListener(mAdapter.mListener); + saveListState(); + } + + public void saveListState() + { + mState = new Bundle(); + mState.putInt(EXTRA_LIST_POSITION, mListView.getSelectedItemPosition()); + } + + public void restoreListState() + { + if (mState == null) + { + return; + } + + int pos = mState.getInt(EXTRA_LIST_POSITION, ListView.INVALID_POSITION); + + if (pos >= mListView.getCount()) + { + pos = mListView.getCount() - 1; + } + + if (pos == ListView.INVALID_POSITION) + { + mListView.setSelected(false); + } + else + { + mListView.setSelection(pos); + } + } + + /** + * On resume we refresh messages for the folder that is currently open. + * This guarantees that things like unread message count and read status + * are updated. + */ + @Override + public void onResume() + { + super.onResume(); + + mStars = K9.messageListStars(); + mCheckboxes = K9.messageListCheckboxes(); + + sortType = mController.getSortType(); + sortAscending = mController.isSortAscending(sortType); + sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); + + mController.addListener(mAdapter.mListener); + mAdapter.messages.clear(); + mAdapter.notifyDataSetChanged(); + + if (mFolderName != null) + { + mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener); + mController.notifyAccountCancel(this, mAccount); + + MessagingController.getInstance(getApplication()).notifyAccountCancel(this, mAccount); + + mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener); + } + else if (mQueryString != null) + { + mController.searchLocalMessages(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener); + } + + mHandler.refreshTitle(); + + restoreListState(); + } + + private void initializeLayout() + { + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + requestWindowFeature(Window.FEATURE_PROGRESS); + setContentView(R.layout.message_list); + + mListView = (ListView) findViewById(R.id.message_list); + mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET); + mListView.setLongClickable(true); + mListView.setFastScrollEnabled(true); + mListView.setScrollingCacheEnabled(true); + mListView.setOnItemClickListener(this); + + registerForContextMenu(mListView); + + mBatchButtonArea = findViewById(R.id.batch_button_area); + mBatchReadButton = (ImageButton) findViewById(R.id.batch_read_button); + mBatchReadButton.setOnClickListener(this); + mBatchDeleteButton = (ImageButton) findViewById(R.id.batch_delete_button); + mBatchDeleteButton.setOnClickListener(this); + mBatchFlagButton = (ImageButton) findViewById(R.id.batch_flag_button); + mBatchFlagButton.setOnClickListener(this); + mBatchDoneButton = (ImageButton) findViewById(R.id.batch_done_button); + + mBatchDoneButton.setOnClickListener(this); + + // Gesture detection + gestureDetector = new GestureDetector(new MyGestureDetector()); + gestureListener = new View.OnTouchListener() + { + public boolean onTouch(View v, MotionEvent event) + { + if (gestureDetector.onTouchEvent(event)) + { + return true; + } + return false; + } + }; + + mListView.setOnTouchListener(gestureListener); + } + + @Override + public Object onRetainNonConfigurationInstance() + { + return mAdapter.messages; + } + + @Override + public void onBackPressed() + { + // This will be called either automatically for you on 2.0 + // or later, or by the code above on earlier versions of the + // platform. + if (K9.manageBack()) + { + if (mQueryString == null) + { + onShowFolderList(); + } + else + { + onAccounts(); + } + } + else + { + finish(); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if ( + // XXX TODO - when we go to android 2.0, uncomment this + // android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR && + keyCode == KeyEvent.KEYCODE_BACK + && event.getRepeatCount() == 0 + ) + { + // Take care of calling this method on earlier versions of + // the platform where it doesn't exist. + onBackPressed(); + return true; + } + + // Shortcuts that work no matter what is selected + switch (keyCode) + { + case KeyEvent.KEYCODE_DPAD_LEFT: + { + if (mBatchButtonArea.hasFocus()) + { + return false; + } + else + { + return true; + } + } + case KeyEvent.KEYCODE_DPAD_RIGHT: + { + if (mBatchButtonArea.hasFocus()) + { + return false; + } + else + { + return true; + } + } + case KeyEvent.KEYCODE_C: + { + onCompose(); + return true; + } + case KeyEvent.KEYCODE_Q: + { + onShowFolderList(); + return true; + } + case KeyEvent.KEYCODE_O: + { + onCycleSort(); + return true; + } + case KeyEvent.KEYCODE_I: + { + onToggleSortAscending(); + return true; + } + case KeyEvent.KEYCODE_H: + { + Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG); + toast.show(); + return true; + } + } + + boolean result; + int position = mListView.getSelectedItemPosition(); + try + { + if (position >= 0) + { + MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); + + if (message != null) + { + switch (keyCode) + { + case KeyEvent.KEYCODE_DEL: + { + onDelete(message, position); + return true; + } + case KeyEvent.KEYCODE_S: + { + setSelected(message, !message.selected); + return true; + } + case KeyEvent.KEYCODE_D: + { + onDelete(message, position); + return true; + } + case KeyEvent.KEYCODE_F: + { + onForward(message); + return true; + } + case KeyEvent.KEYCODE_A: + { + onReplyAll(message); + return true; + } + case KeyEvent.KEYCODE_R: + { + onReply(message); + return true; + } + case KeyEvent.KEYCODE_G: + { + onToggleFlag(message); + return true; + } + case KeyEvent.KEYCODE_M: + { + onMove(message); + return true; + } + case KeyEvent.KEYCODE_V: + { + onArchive(message); + return true; + } + case KeyEvent.KEYCODE_Y: + { + onCopy(message); + return true; + } + case KeyEvent.KEYCODE_Z: + { + onToggleRead(message); + return true; + } + } + } + } + } + finally + { + result = super.onKeyDown(keyCode, event); + } + + return result; + } + + private void onOpenMessage(MessageInfoHolder message) + { + if (message.folder.name.equals(message.message.getFolder().getAccount().getDraftsFolderName())) + { + MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message); + } + else + { + // Need to get the list before the sort starts + ArrayList messageRefs = new ArrayList(); + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + MessageReference ref = holder.message.makeMessageReference(); + messageRefs.add(ref); + } + } + MessageReference ref = message.message.makeMessageReference(); + Log.i(K9.LOG_TAG, "MessageList sending message " + ref); + + MessageView.actionView(this, ref, messageRefs); + } + + /* + * We set read=true here for UI performance reasons. The actual value + * will get picked up on the refresh when the Activity is resumed but + * that may take a second or so and we don't want this to show and + * then go away. I've gone back and forth on this, and this gives a + * better UI experience, so I am putting it back in. + */ + if (!message.read) + { + message.read = true; + mHandler.sortMessages(); + } + } + + private void onAccounts() + { + Accounts.listAccounts(this); + finish(); + } + + private void onShowFolderList() + { + FolderList.actionHandleAccount(this, mAccount); + finish(); + } + + private void onCompose() + { + if (mQueryString != null) + { + /* + * If we have a query string, we don't have an account to let + * compose start the default action. + */ + MessageCompose.actionCompose(this, null); + } + else + { + MessageCompose.actionCompose(this, mAccount); + } + } + + private void onEditPrefs() + { + Prefs.actionPrefs(this); + } + + private void onEditAccount() + { + AccountSettings.actionSettings(this, mAccount); + } + + private void changeSort(SORT_TYPE newSortType) + { + if (sortType == newSortType) + { + onToggleSortAscending(); + } + else + { + sortType = newSortType; + mController.setSortType(sortType); + sortAscending = mController.isSortAscending(sortType); + sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); + reSort(); + } + } + + private void reSort() + { + int toastString = sortType.getToast(sortAscending); + + Toast toast = Toast.makeText(this, toastString, Toast.LENGTH_SHORT); + toast.show(); + + mHandler.sortMessages(); + } + + private void onCycleSort() + { + SORT_TYPE[] sorts = SORT_TYPE.values(); + int curIndex = 0; + + for (int i = 0; i < sorts.length; i++) + { + if (sorts[i] == sortType) + { + curIndex = i; + break; + } + } + + curIndex++; + + if (curIndex == sorts.length) + { + curIndex = 0; + } + + changeSort(sorts[curIndex]); + } + + private void onToggleSortAscending() + { + mController.setSortAscending(sortType, !sortAscending); + + sortAscending = mController.isSortAscending(sortType); + sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); + + reSort(); + } + + private void onDelete(MessageInfoHolder holder, int position) + { + mAdapter.removeMessage(holder); + mController.deleteMessages(new Message[] { holder.message }, null); + } + + private void onMove(MessageInfoHolder holder) + { + if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) + { + return; + } + + if (mController.isMoveCapable(holder.message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + + final Account account = holder.message.getFolder().getAccount(); + + Intent intent = new Intent(this, ChooseFolder.class); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); + intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name); + intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); + intent.putExtra(ChooseFolder.EXTRA_MESSAGE, holder.message.makeMessageReference()); + startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE); + } + + private void onArchive(MessageInfoHolder holder) + { + if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) + { + return; + } + + if (mController.isMoveCapable(holder.message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + + onMoveChosen(holder, holder.message.getFolder().getAccount().getArchiveFolderName()); + } + + private void onSpam(MessageInfoHolder holder) + { + if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == false) + { + return; + } + + if (mController.isMoveCapable(holder.message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + + onMoveChosen(holder, holder.message.getFolder().getAccount().getSpamFolderName()); + } + + private void onCopy(MessageInfoHolder holder) + { + if (mController.isCopyCapable(holder.message.getFolder().getAccount()) == false) + { + return; + } + + if (mController.isCopyCapable(holder.message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + + final Account account = holder.message.getFolder().getAccount(); + + Intent intent = new Intent(this, ChooseFolder.class); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); + intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name); + intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); + intent.putExtra(ChooseFolder.EXTRA_MESSAGE, holder.message.makeMessageReference()); + startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + if (resultCode != RESULT_OK) + return; + + switch (requestCode) + { + case ACTIVITY_CHOOSE_FOLDER_MOVE: + case ACTIVITY_CHOOSE_FOLDER_COPY: + { + if (data == null) + return; + + final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); + final MessageReference ref = (MessageReference)data.getSerializableExtra(ChooseFolder.EXTRA_MESSAGE); + final MessageInfoHolder m = mAdapter.getMessage(ref); + + if ((destFolderName != null) && (m != null)) + { + final Account account = m.message.getFolder().getAccount(); + + account.setLastSelectedFolderName(destFolderName); + + switch (requestCode) + { + case ACTIVITY_CHOOSE_FOLDER_MOVE: + onMoveChosen(m, destFolderName); + break; + + case ACTIVITY_CHOOSE_FOLDER_COPY: + onCopyChosen(m, destFolderName); + break; + } + } + break; + } + case ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH: + case ACTIVITY_CHOOSE_FOLDER_COPY_BATCH: + { + final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); + final String accountUuid = data.getStringExtra(ChooseFolder.EXTRA_ACCOUNT); + final Account account = Preferences.getPreferences(this).getAccount(accountUuid); + + account.setLastSelectedFolderName(destFolderName); + + switch (requestCode) + { + case ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH: + onMoveChosenBatch(destFolderName); + break; + + case ACTIVITY_CHOOSE_FOLDER_COPY_BATCH: + onCopyChosenBatch(destFolderName); + break; + } + } + } + } + + private void onMoveChosen(MessageInfoHolder holder, String folderName) + { + if (mController.isMoveCapable(holder.message.getFolder().getAccount()) == true && folderName != null) + { + if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) + { + return; + } + mAdapter.removeMessage(holder); + mController.moveMessage(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), holder.message, folderName, null); + } + } + + private void onCopyChosen(MessageInfoHolder holder, String folderName) + { + if (mController.isCopyCapable(holder.message.getFolder().getAccount()) == true && folderName != null) + { + mController.copyMessage(holder.message.getFolder().getAccount(), + holder.message.getFolder().getName(), holder.message, folderName, null); + } + } + + private void onReply(MessageInfoHolder holder) + { + MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false); + } + + private void onReplyAll(MessageInfoHolder holder) + { + MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true); + } + + private void onForward(MessageInfoHolder holder) + { + MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message); + } + + private void onMarkAllAsRead(final Account account, final String folder) + { + showDialog(DIALOG_MARK_ALL_AS_READ); + } + + private void onExpunge(final Account account, String folderName) + { + mController.expunge(account, folderName, null); + } + + @Override + public Dialog onCreateDialog(int id) + { + switch (id) + { + case DIALOG_MARK_ALL_AS_READ: + return createMarkAllAsReadDialog(); + } + + return super.onCreateDialog(id); + } + + @Override + public void onPrepareDialog(int id, Dialog dialog) + { + switch (id) + { + case DIALOG_MARK_ALL_AS_READ: + { + if (mCurrentFolder != null) + { + ((AlertDialog)dialog).setMessage(getString(R.string.mark_all_as_read_dlg_instructions_fmt, + mCurrentFolder.displayName)); + } + break; + } + default: + { + super.onPrepareDialog(id, dialog); + } + } + } + + private Dialog createMarkAllAsReadDialog() + { + return new AlertDialog.Builder(this) + .setTitle(R.string.mark_all_as_read_dlg_title) + .setMessage(getString(R.string.mark_all_as_read_dlg_instructions_fmt, + mCurrentFolder.displayName)) + .setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int whichButton) + { + dismissDialog(DIALOG_MARK_ALL_AS_READ); + + try + { + mController.markAllMessagesRead(mAccount, mCurrentFolder.name); + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + holder.read = true; + } + } + mHandler.sortMessages(); + } + catch (Exception e) + { + // Ignore + } + } + }) + .setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int whichButton) + { + dismissDialog(DIALOG_MARK_ALL_AS_READ); + } + }) + .create(); + } + + private void onToggleRead(MessageInfoHolder holder) + { + mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.SEEN, !holder.read); + holder.read = !holder.read; + mHandler.sortMessages(); + } + + private void onToggleFlag(MessageInfoHolder holder) + { + mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.FLAGGED, !holder.flagged); + holder.flagged = !holder.flagged; + mHandler.sortMessages(); + } + + private void checkMail(Account account, String folderName) + { + mController.synchronizeMailbox(account, folderName, mAdapter.mListener, null); + sendMail(account); + } + + private void sendMail(Account account) + { + mController.sendPendingMessages(account, mAdapter.mListener); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + int itemId = item.getItemId(); + switch (itemId) + { + case R.id.compose: + { + onCompose(); + return true; + } + case R.id.accounts: + { + onAccounts(); + return true; + } + case R.id.set_sort_date: + { + changeSort(SORT_TYPE.SORT_DATE); + return true; + } + case R.id.set_sort_subject: + { + changeSort(SORT_TYPE.SORT_SUBJECT); + return true; + } + case R.id.set_sort_sender: + { + changeSort(SORT_TYPE.SORT_SENDER); + return true; + } + case R.id.set_sort_flag: + { + changeSort(SORT_TYPE.SORT_FLAGGED); + return true; + } + case R.id.set_sort_unread: + { + changeSort(SORT_TYPE.SORT_UNREAD); + return true; + } + case R.id.set_sort_attach: + { + changeSort(SORT_TYPE.SORT_ATTACHMENT); + return true; + } + case R.id.select_all: + case R.id.batch_select_all: + { + setAllSelected(true); + toggleBatchButtons(); + return true; + } + case R.id.batch_deselect_all: + { + setAllSelected(false); + toggleBatchButtons(); + return true; + } + case R.id.batch_delete_op: + { + deleteSelected(); + return true; + } + case R.id.batch_mark_read_op: + { + flagSelected(Flag.SEEN, true); + return true; + } + case R.id.batch_mark_unread_op: + { + flagSelected(Flag.SEEN, false); + return true; + } + case R.id.batch_flag_op: + { + flagSelected(Flag.FLAGGED, true); + return true; + } + case R.id.batch_unflag_op: + { + flagSelected(Flag.FLAGGED, false); + return true; + } + case R.id.settings: + { + if (mQueryString == null) + { + break; + } + + /* + * Fall-through in search results view. Otherwise a sub-menu + * with only one option would be opened. + */ + } + case R.id.app_settings: + { + onEditPrefs(); + return true; + } + } + + if (mQueryString != null) + { + // None of the options after this point are "safe" for search results + //TODO: This is not true for "unread" and "starred" searches in regular folders + return false; + } + + switch (itemId) + { + case R.id.check_mail: + { + if (mFolderName != null) + { + checkMail(mAccount, mFolderName); + } + return true; + } + case R.id.send_messages: + { + sendMail(mAccount); + return true; + } + case R.id.list_folders: + { + onShowFolderList(); + return true; + } + case R.id.mark_all_as_read: + { + if (mFolderName != null) + { + onMarkAllAsRead(mAccount, mFolderName); + } + return true; + } + case R.id.folder_settings: + { + if (mFolderName != null) + { + FolderSettings.actionSettings(this, mAccount, mFolderName); + } + return true; + } + case R.id.account_settings: + { + onEditAccount(); + return true; + } + case R.id.batch_copy_op: + { + onCopyBatch(); + return true; + } + case R.id.batch_archive_op: + { + onArchiveBatch(); + return true; + } + case R.id.batch_spam_op: + { + onSpamBatch(); + return true; + } + case R.id.batch_move_op: + { + onMoveBatch(); + return true; + } + case R.id.expunge: + { + if (mCurrentFolder != null) + { + onExpunge(mAccount, mCurrentFolder.name); + } + return true; + } + default: + { + return super.onOptionsItemSelected(item); + } + } + } + + private final int[] batch_ops = { R.id.batch_copy_op, R.id.batch_delete_op, R.id.batch_flag_op, + R.id.batch_unflag_op, R.id.batch_mark_read_op, R.id.batch_mark_unread_op, + R.id.batch_archive_op, R.id.batch_spam_op, R.id.batch_move_op, + R.id.batch_select_all, R.id.batch_deselect_all + }; + + private void setOpsState(Menu menu, boolean state, boolean enabled) + { + for (int id : batch_ops) + { + menu.findItem(id).setVisible(state); + menu.findItem(id).setEnabled(enabled); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + boolean anySelected = anySelected(); + + menu.findItem(R.id.select_all).setVisible(! anySelected); + menu.findItem(R.id.batch_ops).setVisible(anySelected); + + setOpsState(menu, true, anySelected); + + if (mQueryString != null) + { + menu.findItem(R.id.mark_all_as_read).setVisible(false); + menu.findItem(R.id.list_folders).setVisible(false); + menu.findItem(R.id.expunge).setVisible(false); + menu.findItem(R.id.batch_archive_op).setVisible(false); + menu.findItem(R.id.batch_spam_op).setVisible(false); + menu.findItem(R.id.batch_move_op).setVisible(false); + menu.findItem(R.id.batch_copy_op).setVisible(false); + menu.findItem(R.id.check_mail).setVisible(false); + menu.findItem(R.id.send_messages).setVisible(false); + } + else + { + if (mCurrentFolder != null && mCurrentFolder.outbox) + { + menu.findItem(R.id.check_mail).setVisible(false); + } + else + { + menu.findItem(R.id.send_messages).setVisible(false); + } + + if (mCurrentFolder != null && K9.ERROR_FOLDER_NAME.equals(mCurrentFolder.name)) + { + menu.findItem(R.id.expunge).setVisible(false); + } + } + + boolean newFlagState = computeBatchDirection(true); + boolean newReadState = computeBatchDirection(false); + menu.findItem(R.id.batch_flag_op).setVisible(newFlagState); + menu.findItem(R.id.batch_unflag_op).setVisible(!newFlagState); + menu.findItem(R.id.batch_mark_read_op).setVisible(newReadState); + menu.findItem(R.id.batch_mark_unread_op).setVisible(!newReadState); + menu.findItem(R.id.batch_deselect_all).setVisible(anySelected); + menu.findItem(R.id.batch_select_all).setEnabled(true); + + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.message_list_option, menu); + + return true; + } + + @Override + public boolean onContextItemSelected(MenuItem item) + { + AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + MessageInfoHolder holder = (MessageInfoHolder) mAdapter.getItem(info.position); + + switch (item.getItemId()) + { + case R.id.open: + { + onOpenMessage(holder); + break; + } + case R.id.select: + { + setSelected(holder, true); + break; + } + case R.id.deselect: + { + setSelected(holder, false); + break; + } + case R.id.delete: + { + onDelete(holder, info.position); + break; + } + case R.id.reply: + { + onReply(holder); + break; + } + case R.id.reply_all: + { + onReplyAll(holder); + break; + } + case R.id.forward: + { + onForward(holder); + break; + } + case R.id.mark_as_read: + { + onToggleRead(holder); + break; + } + case R.id.flag: + { + onToggleFlag(holder); + break; + } + case R.id.archive: + { + onArchive(holder); + break; + } + case R.id.spam: + { + onSpam(holder); + break; + } + case R.id.move: + { + onMove(holder); + break; + } + case R.id.copy: + { + onCopy(holder); + break; + } + case R.id.send_alternate: + { + onSendAlternate(mAccount, holder); + break; + } + case R.id.same_sender: + { + MessageList.actionHandle(MessageList.this, + "From "+holder.sender, holder.sender, true, + null, null); + break; + } + } + return super.onContextItemSelected(item); + } + + public void onSendAlternate(Account account, MessageInfoHolder holder) + { + mController.sendAlternate(this, account, holder.message); + } + + public void showProgressIndicator(boolean status) + { + setProgressBarIndeterminateVisibility(status); + ProgressBar bar = (ProgressBar)mListView.findViewById(R.id.message_list_progress); + if (bar == null) + { + return; + } + + bar.setIndeterminate(true); + if (status) + { + bar.setVisibility(ProgressBar.VISIBLE); + } + else + { + bar.setVisibility(ProgressBar.INVISIBLE); + } + } + + class MyGestureDetector extends SimpleOnGestureListener + { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) + { + if (e2 == null || e1 == null) + return true; + + float deltaX = e2.getX() - e1.getX(), + deltaY = e2.getY() - e1.getY(); + + boolean movedAcross = (Math.abs(deltaX) > Math.abs(deltaY * 4)); + boolean steadyHand = (Math.abs(deltaX / deltaY) > 2); + + if (movedAcross && steadyHand) + { + boolean selected = (deltaX > 0); + int position = mListView.pointToPosition((int)e1.getX(), (int)e1.getY()); + + if (position != AdapterView.INVALID_POSITION) + { + MessageInfoHolder msgInfoHolder = (MessageInfoHolder) mAdapter.getItem(position); + + if (msgInfoHolder != null && msgInfoHolder.selected != selected) + { + msgInfoHolder.selected = selected; + mSelectedCount += (selected ? 1 : -1); + mAdapter.notifyDataSetChanged(); + toggleBatchButtons(); + } + } + } + + return false; + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + + AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(info.position); + + if (message == null) + { + return; + } + + getMenuInflater().inflate(R.menu.message_list_context, menu); + + menu.setHeaderTitle((CharSequence) message.subject); + + if (message.read) + { + menu.findItem(R.id.mark_as_read).setTitle(R.string.mark_as_unread_action); + } + + if (message.flagged) + { + menu.findItem(R.id.flag).setTitle(R.string.unflag_action); + } + + Account account = message.message.getFolder().getAccount(); + if (mController.isCopyCapable(account) == false) + { + menu.findItem(R.id.copy).setVisible(false); + } + + if (mController.isMoveCapable(account) == false) + { + menu.findItem(R.id.move).setVisible(false); + menu.findItem(R.id.archive).setVisible(false); + menu.findItem(R.id.spam).setVisible(false); + } + + if (K9.FOLDER_NONE.equalsIgnoreCase(account.getArchiveFolderName())) + { + menu.findItem(R.id.archive).setVisible(false); + } + if (K9.FOLDER_NONE.equalsIgnoreCase(account.getSpamFolderName())) + { + menu.findItem(R.id.spam).setVisible(false); + } + + if (message.selected) + { + menu.findItem(R.id.select).setVisible(false); + menu.findItem(R.id.deselect).setVisible(true); + } + else + { + menu.findItem(R.id.select).setVisible(true); + menu.findItem(R.id.deselect).setVisible(false); + } + } + + class MessageListAdapter extends BaseAdapter + { + private final List messages = java.util.Collections.synchronizedList(new ArrayList()); + + private final ActivityListener mListener = new ActivityListener() + { + @Override + public void synchronizeMailboxStarted(Account account, String folder) + { + super.synchronizeMailboxStarted(account, folder); + + if (updateForMe(account, folder)) + { + mHandler.progress(true); + mHandler.folderLoading(folder, true); + } + mHandler.refreshTitle(); + } + + @Override + public void synchronizeMailboxFinished(Account account, String folder, + int totalMessagesInMailbox, int numNewMessages) + { + super.synchronizeMailboxFinished(account, folder, totalMessagesInMailbox, numNewMessages); + + if (updateForMe(account, folder)) + { + mHandler.progress(false); + mHandler.folderLoading(folder, false); + mHandler.sortMessages(); + } + mHandler.refreshTitle(); + } + + @Override + public void synchronizeMailboxFailed(Account account, String folder, String message) + { + super.synchronizeMailboxFailed(account, folder, message); + + if (updateForMe(account, folder)) + { + mHandler.progress(false); + mHandler.folderLoading(folder, false); + mHandler.sortMessages(); + } + mHandler.refreshTitle(); + } + + @Override + public void sendPendingMessagesStarted(Account account) + { + super.sendPendingMessagesStarted(account); + mHandler.refreshTitle(); + } + + @Override + public void sendPendingMessagesCompleted(Account account) + { + super.sendPendingMessagesCompleted(account); + mHandler.refreshTitle(); + } + + @Override + public void sendPendingMessagesFailed(Account account) + { + super.sendPendingMessagesFailed(account); + mHandler.refreshTitle(); + } + + @Override + public void synchronizeMailboxProgress(Account account, String folder, int completed, int total) + { + super.synchronizeMailboxProgress(account, folder, completed, total); + mHandler.refreshTitle(); + } + + @Override + public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) + { + addOrUpdateMessage(account, folder, message, true); + } + + @Override + public void synchronizeMailboxRemovedMessage(Account account, String folder,Message message) + { + MessageInfoHolder holder = getMessage(message); + if (holder == null) + { + Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " + message.getUid()); + } + else + { + removeMessage(holder); + } + } + + @Override + public void listLocalMessagesStarted(Account account, String folder) + { + if ((mQueryString != null && folder == null) || + (account != null && account.equals(mAccount)) + ) + { + mHandler.progress(true); + if (folder != null) + { + mHandler.folderLoading(folder, true); + } + } + } + + @Override + public void listLocalMessagesFailed(Account account, String folder, String message) + { + if ((mQueryString != null && folder == null) || + (account != null && account.equals(mAccount))) + { + mHandler.sortMessages(); + mHandler.progress(false); + if (folder != null) + { + mHandler.folderLoading(folder, false); + } + } + } + + @Override + public void listLocalMessagesFinished(Account account, String folder) + { + if ((mQueryString != null && folder == null) || + (account != null && account.equals(mAccount))) + { + mHandler.sortMessages(); + mHandler.progress(false); + if (folder != null) + { + mHandler.folderLoading(folder, false); + } + } + } + + @Override + public void listLocalMessagesRemoveMessage(Account account, String folder,Message message) + { + MessageInfoHolder holder = getMessage(message); + if (holder != null) + { + removeMessage(holder); + } + } + + @Override + public void listLocalMessagesAddMessages(Account account, String folder, List messages) + { + addOrUpdateMessages(account, folder, messages, false); + } + + @Override + public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) + { + addOrUpdateMessage(account, folder, message, false); + } + + @Override + public void searchStats(AccountStats stats) + { + mUnreadMessageCount = stats.unreadMessageCount; + mHandler.refreshTitle(); + } + + @Override + public void folderStatusChanged(Account account, String folder, int unreadMessageCount) + { + super.folderStatusChanged(account, folder, unreadMessageCount); + if (updateForMe(account, folder)) + { + mUnreadMessageCount = unreadMessageCount; + mHandler.refreshTitle(); + } + } + + @Override + public void pendingCommandsProcessing(Account account) + { + super.pendingCommandsProcessing(account); + mHandler.refreshTitle(); + } + + @Override + public void pendingCommandsFinished(Account account) + { + super.pendingCommandsFinished(account); + mHandler.refreshTitle(); + } + + @Override + public void pendingCommandStarted(Account account, String commandTitle) + { + super.pendingCommandStarted(account, commandTitle); + mHandler.refreshTitle(); + } + + @Override + public void pendingCommandCompleted(Account account, String commandTitle) + { + super.pendingCommandCompleted(account, commandTitle); + mHandler.refreshTitle(); + } + + @Override + public void messageUidChanged(Account account, String folder, String oldUid, String newUid) + { + MessageReference ref = new MessageReference(); + ref.accountUuid = account.getUuid(); + ref.folderName = folder; + ref.uid = oldUid; + + MessageInfoHolder holder = getMessage(ref); + if (holder != null) + { + holder.uid = newUid; + holder.message.setUid(newUid); + } + } + }; + + private boolean updateForMe(Account account, String folder) + { + if ((account.equals(mAccount) && mFolderName != null && folder.equals(mFolderName))) + { + return true; + } + else + { + return false; + } + } + + private Drawable mAttachmentIcon; + private Drawable mAnsweredIcon; + private View footerView = null; + + MessageListAdapter() + { + mAttachmentIcon = getResources().getDrawable(R.drawable.ic_mms_attachment_small); + mAnsweredIcon = getResources().getDrawable(R.drawable.ic_mms_answered_small); + } + + public void removeMessages(List holders) + { + if (holders != null) + { + mHandler.removeMessage(holders); + } + } + + public void removeMessage(MessageInfoHolder holder) + { + List messages = new ArrayList(); + messages.add(holder); + removeMessages(messages); + } + + private void addOrUpdateMessage(Account account, String folder, Message message, boolean verifyAgainstSearch) + { + List messages = new ArrayList(); + messages.add(message); + addOrUpdateMessages(account, folder, messages, verifyAgainstSearch); + } + + private void addOrUpdateMessages(Account account, String folder, List messages, boolean verifyAgainstSearch) + { + boolean needsSort = false; + final List messagesToAdd = new ArrayList(); + List messagesToRemove = new ArrayList(); + List messagesToSearch = new ArrayList(); + + for (Message message : messages) + { + MessageInfoHolder m = getMessage(message); + if (message.isSet(Flag.DELETED)) + { + if (m != null) + { + messagesToRemove.add(m); + } + } + else if (m == null) + { + if (updateForMe(account, folder)) + { + m = new MessageInfoHolder(message); + messagesToAdd.add(m); + } + else + { + if (mQueryString != null) + { + if (verifyAgainstSearch) + { + messagesToSearch.add(message); + } + else + { + m = new MessageInfoHolder(message); + messagesToAdd.add(m); + } + } + } + } + else + { + m.populate(message, new FolderInfoHolder(message.getFolder(), account), account); + needsSort = true; + } + } + + if (messagesToSearch.size() > 0) + { + mController.searchLocalMessages(mAccountUuids, mFolderNames, messagesToSearch.toArray(new Message[0]), mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, + new MessagingListener() + { + @Override + public void listLocalMessagesAddMessages(Account account, String folder, List messages) + { + addOrUpdateMessages(account, folder, messages, false); + } + }); + } + + if (messagesToRemove.size() > 0) + { + removeMessages(messagesToRemove); + } + + if (messagesToAdd.size() > 0) + { + mHandler.addMessages(messagesToAdd); + } + + if (needsSort) + { + mHandler.sortMessages(); + mHandler.resetUnreadCount(); + } + } + + public MessageInfoHolder getMessage(Message message) + { + return getMessage(message.makeMessageReference()); + } + + // XXX TODO - make this not use a for loop + public MessageInfoHolder getMessage(MessageReference messageReference) + { + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + /* + * 2010-06-21 - cketti + * Added null pointer check. Not sure what's causing 'holder' + * to be null. See log provided in issue 1749, comment #15. * - * Please remove this comment once the cause was found and the - * bug(?) fixed. - */ - if ((holder != null) && holder.message.equalsReference(messageReference)) - { - return holder; - } - } - } - return null; - } - - public FolderInfoHolder getFolder(String folder, Account account) - { - LocalFolder local_folder = null; - try - { - LocalStore localStore = account.getLocalStore(); - local_folder = localStore.getFolder(folder); - return new FolderInfoHolder((Folder)local_folder, account); - } - catch (Exception e) - { - Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ",e); - return null; - } - finally - { - if (local_folder != null) - { - local_folder.close(); - } - } - } - - private static final int NON_MESSAGE_ITEMS = 1; - public int getCount() - { - if (mAdapter.messages.size() == 0) - { - return NON_MESSAGE_ITEMS ; - } - - return mAdapter.messages.size() + NON_MESSAGE_ITEMS ; - } - - public long getItemId(int position) - { - try - { - MessageInfoHolder messageHolder =(MessageInfoHolder) getItem(position); - if (messageHolder != null) - { - return ((LocalStore.LocalMessage) messageHolder.message).getId(); - } - } - catch (Exception e) - { - Log.i(K9.LOG_TAG,"getItemId("+position+") ",e); - } - return -1; - } - - public Object getItem(long position) - { - return getItem((int)position); - } - - public Object getItem(int position) - { - try - { - synchronized (mAdapter.messages) - { - if (position < mAdapter.messages.size()) - { - return mAdapter.messages.get(position); - } - } - } - catch (Exception e) - { - Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mAdapter.messages.size(), e); - } - return null; - } - - public View getView(int position, View convertView, ViewGroup parent) - { - - if (position == mAdapter.messages.size()) - { - return getFooterView(position, convertView, parent); - } - else - { - return getItemView(position, convertView, parent); - } - } - - public View getItemView(int position, View convertView, ViewGroup parent) - { - MessageInfoHolder message = (MessageInfoHolder) getItem(position); - View view; - - if ((convertView != null) && (convertView.getId() == R.layout.message_list_item)) - { - view = convertView; - } - else - { - if (mTouchView) - { - view = mInflater.inflate(R.layout.message_list_item_touchable, parent, false); - view.setId(R.layout.message_list_item); - } - else - { - view = mInflater.inflate(R.layout.message_list_item, parent, false); - view.setId(R.layout.message_list_item); - } - } - - MessageViewHolder holder = (MessageViewHolder) view.getTag(); - - if (holder == null) - { - holder = new MessageViewHolder(); - holder.subject = (TextView) view.findViewById(R.id.subject); - holder.from = (TextView) view.findViewById(R.id.from); - holder.date = (TextView) view.findViewById(R.id.date); - holder.chip = view.findViewById(R.id.chip); - holder.preview = (TextView) view.findViewById(R.id.preview); - holder.selected = (CheckBox) view.findViewById(R.id.selected_checkbox); - holder.flagged = (CheckBox) view.findViewById(R.id.flagged); - - // TODO: Don't create an instance of OnClickListener for every message - holder.flagged.setOnClickListener(new OnClickListener() - { - public void onClick(View v) - { - // Perform action on clicks - MessageInfoHolder message = (MessageInfoHolder) getItem((Integer)v.getTag()); - onToggleFlag(message); - } - }); - - if (mStars == false) - { - holder.flagged.setVisibility(View.GONE); - } - - if (mCheckboxes == true) - { - holder.selected.setVisibility(View.VISIBLE); - } - - if (holder.selected != null) - { - holder.selected.setOnCheckedChangeListener(holder); - } - - view.setTag(holder); - } - - if (message != null) - { - holder.subject.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD); - - // XXX TODO there has to be some way to walk our view hierarchy and get this - holder.flagged.setTag((Integer)position); - holder.flagged.setChecked(message.flagged); - - // So that the mSelectedCount is only incremented/decremented - // when a user checks the checkbox (vs code) - holder.position = -1; - holder.selected.setChecked(message.selected); - - if (!mCheckboxes) - { - holder.selected.setVisibility(message.selected ? View.VISIBLE : View.GONE); - } - - holder.chip.setBackgroundColor(message.message.getFolder().getAccount().getChipColor()); - holder.chip.getBackground().setAlpha(message.read ? 127 : 255); - view.getBackground().setAlpha(message.downloaded ? 0 : 127); - - if ((message.subject == null) || message.subject.equals("")) - { - holder.subject.setText(getText(R.string.general_no_subject)); - } - else - { - holder.subject.setText(message.subject); - } - - if (holder.preview != null) - { - /* - * In the touchable UI, we have previews. Otherwise, we - * have just a "from" line. - * Because text views can't wrap around each other(?) we - * compose a custom view containing the preview and the - * from. - */ - holder.preview.setText(message.sender + " " + message.preview, + * Please remove this comment once the cause was found and the + * bug(?) fixed. + */ + if ((holder != null) && holder.message.equalsReference(messageReference)) + { + return holder; + } + } + } + return null; + } + + public FolderInfoHolder getFolder(String folder, Account account) + { + LocalFolder local_folder = null; + try + { + LocalStore localStore = account.getLocalStore(); + local_folder = localStore.getFolder(folder); + return new FolderInfoHolder((Folder)local_folder, account); + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ",e); + return null; + } + finally + { + if (local_folder != null) + { + local_folder.close(); + } + } + } + + private static final int NON_MESSAGE_ITEMS = 1; + public int getCount() + { + if (mAdapter.messages.size() == 0) + { + return NON_MESSAGE_ITEMS ; + } + + return mAdapter.messages.size() + NON_MESSAGE_ITEMS ; + } + + public long getItemId(int position) + { + try + { + MessageInfoHolder messageHolder =(MessageInfoHolder) getItem(position); + if (messageHolder != null) + { + return ((LocalStore.LocalMessage) messageHolder.message).getId(); + } + } + catch (Exception e) + { + Log.i(K9.LOG_TAG,"getItemId("+position+") ",e); + } + return -1; + } + + public Object getItem(long position) + { + return getItem((int)position); + } + + public Object getItem(int position) + { + try + { + synchronized (mAdapter.messages) + { + if (position < mAdapter.messages.size()) + { + return mAdapter.messages.get(position); + } + } + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mAdapter.messages.size(), e); + } + return null; + } + + public View getView(int position, View convertView, ViewGroup parent) + { + + if (position == mAdapter.messages.size()) + { + return getFooterView(position, convertView, parent); + } + else + { + return getItemView(position, convertView, parent); + } + } + + public View getItemView(int position, View convertView, ViewGroup parent) + { + MessageInfoHolder message = (MessageInfoHolder) getItem(position); + View view; + + if ((convertView != null) && (convertView.getId() == R.layout.message_list_item)) + { + view = convertView; + } + else + { + if (mTouchView) + { + view = mInflater.inflate(R.layout.message_list_item_touchable, parent, false); + view.setId(R.layout.message_list_item); + } + else + { + view = mInflater.inflate(R.layout.message_list_item, parent, false); + view.setId(R.layout.message_list_item); + } + } + + MessageViewHolder holder = (MessageViewHolder) view.getTag(); + + if (holder == null) + { + holder = new MessageViewHolder(); + holder.subject = (TextView) view.findViewById(R.id.subject); + holder.from = (TextView) view.findViewById(R.id.from); + holder.date = (TextView) view.findViewById(R.id.date); + holder.chip = view.findViewById(R.id.chip); + holder.preview = (TextView) view.findViewById(R.id.preview); + holder.selected = (CheckBox) view.findViewById(R.id.selected_checkbox); + holder.flagged = (CheckBox) view.findViewById(R.id.flagged); + + // TODO: Don't create an instance of OnClickListener for every message + holder.flagged.setOnClickListener(new OnClickListener() + { + public void onClick(View v) + { + // Perform action on clicks + MessageInfoHolder message = (MessageInfoHolder) getItem((Integer)v.getTag()); + onToggleFlag(message); + } + }); + + if (mStars == false) + { + holder.flagged.setVisibility(View.GONE); + } + + if (mCheckboxes == true) + { + holder.selected.setVisibility(View.VISIBLE); + } + + if (holder.selected != null) + { + holder.selected.setOnCheckedChangeListener(holder); + } + + view.setTag(holder); + } + + if (message != null) + { + holder.subject.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD); + + // XXX TODO there has to be some way to walk our view hierarchy and get this + holder.flagged.setTag((Integer)position); + holder.flagged.setChecked(message.flagged); + + // So that the mSelectedCount is only incremented/decremented + // when a user checks the checkbox (vs code) + holder.position = -1; + holder.selected.setChecked(message.selected); + + if (!mCheckboxes) + { + holder.selected.setVisibility(message.selected ? View.VISIBLE : View.GONE); + } + + holder.chip.setBackgroundColor(message.message.getFolder().getAccount().getChipColor()); + holder.chip.getBackground().setAlpha(message.read ? 127 : 255); + view.getBackground().setAlpha(message.downloaded ? 0 : 127); + + if ((message.subject == null) || message.subject.equals("")) + { + holder.subject.setText(getText(R.string.general_no_subject)); + } + else + { + holder.subject.setText(message.subject); + } + + if (holder.preview != null) + { + /* + * In the touchable UI, we have previews. Otherwise, we + * have just a "from" line. + * Because text views can't wrap around each other(?) we + * compose a custom view containing the preview and the + * from. + */ + holder.preview.setText(message.sender + " " + message.preview, TextView.BufferType.SPANNABLE); - Spannable str = (Spannable)holder.preview.getText(); - - // Create our span sections, and assign a format to each. - str.setSpan( + Spannable str = (Spannable)holder.preview.getText(); + + // Create our span sections, and assign a format to each. + str.setSpan( new TextAppearanceSpan( null, Typeface.BOLD, @@ -2282,882 +2282,882 @@ public class MessageList 0, message.sender.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ); - } - else - { - holder.from.setText(message.sender); - holder.from.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD); - } - - holder.date.setText(message.date); - holder.subject.setCompoundDrawablesWithIntrinsicBounds( - message.answered ? mAnsweredIcon : null, // left - null, // top - message.hasAttachments ? mAttachmentIcon : null, // right - null); // bottom - holder.position = position; - } - else - { - // TODO is this branch ever reached/executed? - - holder.chip.getBackground().setAlpha(0); - holder.subject.setText("No subject"); - holder.subject.setTypeface(null, Typeface.NORMAL); - if (holder.preview != null) - { - holder.preview.setText("No sender"); - holder.preview.setTypeface(null, Typeface.NORMAL); - holder.preview.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - } - else - { - holder.from.setText("No sender"); - holder.from.setTypeface(null, Typeface.NORMAL); - holder.from.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - } - - holder.date.setText("No date"); - - //WARNING: Order of the next 2 lines matter - holder.position = -1; - holder.selected.setChecked(false); - - if (!mCheckboxes) - { - holder.selected.setVisibility(View.GONE); - } - holder.flagged.setChecked(false); - } - - holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSubject()); - holder.date.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListDate()); - - if (mTouchView) - { - holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSender()); - } - else - { - holder.from.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSender()); - } - - return view; - } - - public View getFooterView(int position, View convertView, ViewGroup parent) - { - if (footerView == null) - { - footerView = mInflater.inflate(R.layout.message_list_item_footer, parent, false); - if (mQueryString != null) - { - footerView.setVisibility(View.GONE); - } - footerView.setId(R.layout.message_list_item_footer); - FooterViewHolder holder = new FooterViewHolder(); - holder.progress = (ProgressBar)footerView.findViewById(R.id.message_list_progress); - holder.progress.setIndeterminate(true); - holder.main = (TextView)footerView.findViewById(R.id.main_text); - footerView.setTag(holder); - } - - FooterViewHolder holder = (FooterViewHolder)footerView.getTag(); - - if (mCurrentFolder != null && mAccount != null) - { - if (mCurrentFolder.loading) - { - holder.main.setText(getString(R.string.status_loading_more)); - holder.progress.setVisibility(ProgressBar.VISIBLE); - } - else - { - if (mCurrentFolder.lastCheckFailed == false) - { - holder.main.setText(String.format(getString(R.string.load_more_messages_fmt), mAccount.getDisplayCount())); - } - else - { - holder.main.setText(getString(R.string.status_loading_more_failed)); - } - holder.progress.setVisibility(ProgressBar.INVISIBLE); - } - } - else - { - holder.progress.setVisibility(ProgressBar.INVISIBLE); - } - - return footerView; - } - - @Override - public boolean hasStableIds() - { - return true; - } - - public boolean isItemSelectable(int position) - { - if (position < mAdapter.messages.size()) - { - return true; - } - else - { - return false; - } - } - } - - public class MessageInfoHolder implements Comparable - { - public String subject; - public String date; - public Date compareDate; - public String compareSubject; - public String sender; - public String compareCounterparty; - public String preview; - public String[] recipients; - public boolean hasAttachments; - public String uid; - public boolean read; - public boolean answered; - public boolean flagged; - public boolean downloaded; - public boolean partially_downloaded; - public Message message; - public FolderInfoHolder folder; - public boolean selected; - - // Empty constructor for comparison - public MessageInfoHolder() - { - this.selected = false; - } - - public MessageInfoHolder(Message m) - { - this(); - Account account = m.getFolder().getAccount(); - populate(m, new FolderInfoHolder(m.getFolder(), m.getFolder().getAccount()), account); - } - - public MessageInfoHolder(Message m, FolderInfoHolder folder, Account account) - { - this(); - populate(m, folder, account); - } - - public void populate(Message m, FolderInfoHolder folder, Account account) - { - try - { - LocalMessage message = (LocalMessage) m; - Date date = message.getSentDate(); - this.compareDate = message.getInternalDate(); - if (this.compareDate == null) - { - this.compareDate = message.getSentDate(); - } - - this.folder = folder; - - if (Utility.isDateToday(date)) - { - this.date = getTimeFormat().format(date); - } - else - { - this.date = getDateFormat().format(date); - } - - this.hasAttachments = message.getAttachmentCount() > 0; - - this.read = message.isSet(Flag.SEEN); - this.answered = message.isSet(Flag.ANSWERED); - this.flagged = message.isSet(Flag.FLAGGED); - this.downloaded = message.isSet(Flag.X_DOWNLOADED_FULL); - this.partially_downloaded = message.isSet(Flag.X_DOWNLOADED_PARTIAL); - - Address[] addrs = message.getFrom(); - - if (addrs.length > 0 && account.isAnIdentity(addrs[0])) - { - this.compareCounterparty = Address.toFriendly(message .getRecipients(RecipientType.TO)); - this.sender = String.format(getString(R.string.message_list_to_fmt), this.compareCounterparty); - } - else - { - this.sender = Address.toFriendly(addrs); - this.compareCounterparty = this.sender; - } - - this.subject = message.getSubject(); - - this.uid = message.getUid(); - this.message = m; - this.preview = message.getPreview(); - } - catch (MessagingException me) - { - if (Config.LOGV) - { - Log.v(K9.LOG_TAG, "Unable to load message info", me); - } - } - } - - @Override - public boolean equals(Object o) - { - if (o instanceof MessageInfoHolder == false) - { - return false; - } - MessageInfoHolder other = (MessageInfoHolder)o; - return message.equals(other.message); - } - - @Override - public int hashCode() - { - return uid.hashCode(); - } - - public int compareTo(MessageInfoHolder o) - { - int ascender = (sortAscending ? 1 : -1); - int comparison = 0; - - if (sortType == SORT_TYPE.SORT_SUBJECT) - { - if (compareSubject == null) - { - compareSubject = stripPrefixes(subject).toLowerCase(); - } - - if (o.compareSubject == null) - { - o.compareSubject = stripPrefixes(o.subject).toLowerCase(); - } - - comparison = this.compareSubject.compareTo(o.compareSubject); - } - else if (sortType == SORT_TYPE.SORT_SENDER) - { - comparison = this.compareCounterparty.toLowerCase().compareTo(o.compareCounterparty.toLowerCase()); - } - else if (sortType == SORT_TYPE.SORT_FLAGGED) - { - comparison = (this.flagged ? 0 : 1) - (o.flagged ? 0 : 1); - } - else if (sortType == SORT_TYPE.SORT_UNREAD) - { - comparison = (this.read ? 1 : 0) - (o.read ? 1 : 0); - } - else if (sortType == SORT_TYPE.SORT_ATTACHMENT) - { - comparison = (this.hasAttachments ? 0 : 1) - (o.hasAttachments ? 0 : 1); - } - - if (comparison != 0) - { - return comparison * ascender; - } - - int dateAscender = (sortDateAscending ? 1 : -1); - - return this.compareDate.compareTo(o.compareDate) * dateAscender; - } - - Pattern pattern = null; - String patternString = "^ *(re|aw|fw|fwd): *"; - private String stripPrefixes(String in) - { - synchronized (patternString) - { - if (pattern == null) - { - pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); - } - } - - Matcher matcher = pattern.matcher(in); - - int lastPrefix = -1; - - while (matcher.find()) - { - lastPrefix = matcher.end(); - } - - if (lastPrefix > -1 && lastPrefix < in.length() - 1) - { - return in.substring(lastPrefix); - } - else - { - return in; - } - } - } - - class MessageViewHolder - implements OnCheckedChangeListener - { - public TextView subject; - public TextView preview; - public TextView from; - public TextView time; - public TextView date; - public CheckBox flagged; - public View chip; - public CheckBox selected; - public int position = -1; - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) - { - if (position!=-1) - { - MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); - if (message.selected!=isChecked) - { - if (isChecked) - { - mSelectedCount++; - } - else if (mSelectedCount > 0) - { - mSelectedCount--; - } - - // We must set the flag before showing the buttons as the - // buttons text depends on what is selected. - message.selected = isChecked; - if (!mCheckboxes) - { - if (isChecked == true) - { - selected.setVisibility(View.VISIBLE); - } - else - { - selected.setVisibility(View.GONE); - } - } - toggleBatchButtons(); - } - } - } - } - - private void hideBatchButtons() - { - //TODO: Fade out animation - mBatchButtonArea.setVisibility(View.GONE); - } - - private void showBatchButtons() - { - //TODO: Fade in animation - mBatchButtonArea.setVisibility(View.VISIBLE); - } - - private void toggleBatchButtons() - { - if (mSelectedCount < 0) - { - mSelectedCount = 0; - } - - int readButtonIconId; - int flagButtonIconId; - - if (mSelectedCount==0) - { - readButtonIconId = R.drawable.ic_button_mark_read; - flagButtonIconId = R.drawable.ic_button_flag; - hideBatchButtons(); - } - else - { - boolean newReadState = computeBatchDirection(false); - if (newReadState) - { - readButtonIconId = R.drawable.ic_button_mark_read; - } - else - { - readButtonIconId = R.drawable.ic_button_mark_unread; - } - boolean newFlagState = computeBatchDirection(true); - if (newFlagState) - { - flagButtonIconId = R.drawable.ic_button_flag; - } - else - { - flagButtonIconId = R.drawable.ic_button_unflag; - } - showBatchButtons(); - } - - mBatchReadButton.setImageResource(readButtonIconId); - mBatchFlagButton.setImageResource(flagButtonIconId); - } - - class FooterViewHolder - { - public ProgressBar progress; - public TextView main; - } - - public class FolderInfoHolder - { - public String name; - public String displayName; - public boolean loading; - public boolean lastCheckFailed; - public Folder folder; - - /** - * Outbox is handled differently from any other folder. - */ - public boolean outbox; - - public FolderInfoHolder(Folder folder, Account account) - { - populate(folder, account); - } - - public void populate(Folder folder, Account account) - { - this.folder = folder; - this.name = folder.getName(); - - if (this.name.equalsIgnoreCase(K9.INBOX)) - { - this.displayName = getString(R.string.special_mailbox_name_inbox); - } - else - { - this.displayName = this.name; - } - - if (this.name.equals(account.getOutboxFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_outbox_fmt), this.name); - this.outbox = true; - } - - if (this.name.equals(account.getDraftsFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_drafts_fmt), this.name); - } - - if (this.name.equals(account.getTrashFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_trash_fmt), this.name); - } - - if (this.name.equals(account.getSentFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_sent_fmt), this.name); - } - - if (this.name.equals(account.getArchiveFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_archive_fmt), this.name); - } - - if (this.name.equals(account.getSpamFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_spam_fmt), this.name); - } - } - } - - private boolean computeBatchDirection(boolean flagged) - { - boolean newState = false; - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - if (flagged) - { - if (!holder.flagged) - { - newState = true; - break; - } - } - else - { - if (!holder.read) - { - newState = true; - break; - } - } - } - } - } - return newState; - } - - private boolean anySelected() - { - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - return true; - } - } - } - return false; - } - - public void onClick(View v) - { - boolean newState = false; - List messageList = new ArrayList(); - List removeHolderList = new ArrayList(); - - if (v == mBatchDoneButton) - { - setAllSelected(false); - return; - } - - if (v == mBatchFlagButton) - { - newState = computeBatchDirection(true); - } - else - { - newState = computeBatchDirection(false); - } - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - if (v == mBatchDeleteButton) - { - removeHolderList.add(holder); - } - else if (v == mBatchFlagButton) - { - holder.flagged = newState; - } - else if (v == mBatchReadButton) - { - holder.read = newState; - } - messageList.add(holder.message); - } - } - } - mAdapter.removeMessages(removeHolderList); - - if (!messageList.isEmpty()) - { - if (v == mBatchDeleteButton) - { - mController.deleteMessages(messageList.toArray(new Message[0]), null); - mSelectedCount = 0; - toggleBatchButtons(); - } - else - { - mController.setFlag(messageList.toArray(new Message[0]), (v == mBatchReadButton ? Flag.SEEN : Flag.FLAGGED), newState); - } - } - else - { - // Should not happen - Toast.makeText(this, R.string.no_message_seletected_toast, Toast.LENGTH_SHORT).show(); - } - mHandler.sortMessages(); - } - - private void setAllSelected(boolean isSelected) - { - mSelectedCount = 0; - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - holder.selected = isSelected; - mSelectedCount += (isSelected ? 1 : 0); - } - } - mAdapter.notifyDataSetChanged(); - toggleBatchButtons(); - } - - private void setSelected(MessageInfoHolder holder, boolean newState) - { - if (holder.selected != newState) - { - holder.selected = newState; - mSelectedCount += (newState ? 1 : -1); - } - mAdapter.notifyDataSetChanged(); - toggleBatchButtons(); - } - - private void flagSelected(Flag flag, boolean newState) - { - List messageList = new ArrayList(); - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - messageList.add(holder.message); - if (flag == Flag.SEEN) - { - holder.read = newState; - } - else if (flag == Flag.FLAGGED) - { - holder.flagged = newState; - } - } - } - } - mController.setFlag(messageList.toArray(new Message[0]), flag, newState); - mHandler.sortMessages(); - } - - private void deleteSelected() - { - List messageList = new ArrayList(); - List removeHolderList = new ArrayList(); - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - removeHolderList.add(holder); - messageList.add(holder.message); - } - } - } - mAdapter.removeMessages(removeHolderList); - - mController.deleteMessages(messageList.toArray(new Message[0]), null); - mSelectedCount = 0; - toggleBatchButtons(); - } - - private void onMoveBatch() - { - if (mController.isMoveCapable(mAccount) == false) - { - return; - } - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isMoveCapable(message) == false) - { - Toast toast = Toast.makeText(this, + ); + } + else + { + holder.from.setText(message.sender); + holder.from.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD); + } + + holder.date.setText(message.date); + holder.subject.setCompoundDrawablesWithIntrinsicBounds( + message.answered ? mAnsweredIcon : null, // left + null, // top + message.hasAttachments ? mAttachmentIcon : null, // right + null); // bottom + holder.position = position; + } + else + { + // TODO is this branch ever reached/executed? + + holder.chip.getBackground().setAlpha(0); + holder.subject.setText("No subject"); + holder.subject.setTypeface(null, Typeface.NORMAL); + if (holder.preview != null) + { + holder.preview.setText("No sender"); + holder.preview.setTypeface(null, Typeface.NORMAL); + holder.preview.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } + else + { + holder.from.setText("No sender"); + holder.from.setTypeface(null, Typeface.NORMAL); + holder.from.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } + + holder.date.setText("No date"); + + //WARNING: Order of the next 2 lines matter + holder.position = -1; + holder.selected.setChecked(false); + + if (!mCheckboxes) + { + holder.selected.setVisibility(View.GONE); + } + holder.flagged.setChecked(false); + } + + holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSubject()); + holder.date.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListDate()); + + if (mTouchView) + { + holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSender()); + } + else + { + holder.from.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizes.getMessageListSender()); + } + + return view; + } + + public View getFooterView(int position, View convertView, ViewGroup parent) + { + if (footerView == null) + { + footerView = mInflater.inflate(R.layout.message_list_item_footer, parent, false); + if (mQueryString != null) + { + footerView.setVisibility(View.GONE); + } + footerView.setId(R.layout.message_list_item_footer); + FooterViewHolder holder = new FooterViewHolder(); + holder.progress = (ProgressBar)footerView.findViewById(R.id.message_list_progress); + holder.progress.setIndeterminate(true); + holder.main = (TextView)footerView.findViewById(R.id.main_text); + footerView.setTag(holder); + } + + FooterViewHolder holder = (FooterViewHolder)footerView.getTag(); + + if (mCurrentFolder != null && mAccount != null) + { + if (mCurrentFolder.loading) + { + holder.main.setText(getString(R.string.status_loading_more)); + holder.progress.setVisibility(ProgressBar.VISIBLE); + } + else + { + if (mCurrentFolder.lastCheckFailed == false) + { + holder.main.setText(String.format(getString(R.string.load_more_messages_fmt), mAccount.getDisplayCount())); + } + else + { + holder.main.setText(getString(R.string.status_loading_more_failed)); + } + holder.progress.setVisibility(ProgressBar.INVISIBLE); + } + } + else + { + holder.progress.setVisibility(ProgressBar.INVISIBLE); + } + + return footerView; + } + + @Override + public boolean hasStableIds() + { + return true; + } + + public boolean isItemSelectable(int position) + { + if (position < mAdapter.messages.size()) + { + return true; + } + else + { + return false; + } + } + } + + public class MessageInfoHolder implements Comparable + { + public String subject; + public String date; + public Date compareDate; + public String compareSubject; + public String sender; + public String compareCounterparty; + public String preview; + public String[] recipients; + public boolean hasAttachments; + public String uid; + public boolean read; + public boolean answered; + public boolean flagged; + public boolean downloaded; + public boolean partially_downloaded; + public Message message; + public FolderInfoHolder folder; + public boolean selected; + + // Empty constructor for comparison + public MessageInfoHolder() + { + this.selected = false; + } + + public MessageInfoHolder(Message m) + { + this(); + Account account = m.getFolder().getAccount(); + populate(m, new FolderInfoHolder(m.getFolder(), m.getFolder().getAccount()), account); + } + + public MessageInfoHolder(Message m, FolderInfoHolder folder, Account account) + { + this(); + populate(m, folder, account); + } + + public void populate(Message m, FolderInfoHolder folder, Account account) + { + try + { + LocalMessage message = (LocalMessage) m; + Date date = message.getSentDate(); + this.compareDate = message.getInternalDate(); + if (this.compareDate == null) + { + this.compareDate = message.getSentDate(); + } + + this.folder = folder; + + if (Utility.isDateToday(date)) + { + this.date = getTimeFormat().format(date); + } + else + { + this.date = getDateFormat().format(date); + } + + this.hasAttachments = message.getAttachmentCount() > 0; + + this.read = message.isSet(Flag.SEEN); + this.answered = message.isSet(Flag.ANSWERED); + this.flagged = message.isSet(Flag.FLAGGED); + this.downloaded = message.isSet(Flag.X_DOWNLOADED_FULL); + this.partially_downloaded = message.isSet(Flag.X_DOWNLOADED_PARTIAL); + + Address[] addrs = message.getFrom(); + + if (addrs.length > 0 && account.isAnIdentity(addrs[0])) + { + this.compareCounterparty = Address.toFriendly(message .getRecipients(RecipientType.TO)); + this.sender = String.format(getString(R.string.message_list_to_fmt), this.compareCounterparty); + } + else + { + this.sender = Address.toFriendly(addrs); + this.compareCounterparty = this.sender; + } + + this.subject = message.getSubject(); + + this.uid = message.getUid(); + this.message = m; + this.preview = message.getPreview(); + } + catch (MessagingException me) + { + if (Config.LOGV) + { + Log.v(K9.LOG_TAG, "Unable to load message info", me); + } + } + } + + @Override + public boolean equals(Object o) + { + if (o instanceof MessageInfoHolder == false) + { + return false; + } + MessageInfoHolder other = (MessageInfoHolder)o; + return message.equals(other.message); + } + + @Override + public int hashCode() + { + return uid.hashCode(); + } + + public int compareTo(MessageInfoHolder o) + { + int ascender = (sortAscending ? 1 : -1); + int comparison = 0; + + if (sortType == SORT_TYPE.SORT_SUBJECT) + { + if (compareSubject == null) + { + compareSubject = stripPrefixes(subject).toLowerCase(); + } + + if (o.compareSubject == null) + { + o.compareSubject = stripPrefixes(o.subject).toLowerCase(); + } + + comparison = this.compareSubject.compareTo(o.compareSubject); + } + else if (sortType == SORT_TYPE.SORT_SENDER) + { + comparison = this.compareCounterparty.toLowerCase().compareTo(o.compareCounterparty.toLowerCase()); + } + else if (sortType == SORT_TYPE.SORT_FLAGGED) + { + comparison = (this.flagged ? 0 : 1) - (o.flagged ? 0 : 1); + } + else if (sortType == SORT_TYPE.SORT_UNREAD) + { + comparison = (this.read ? 1 : 0) - (o.read ? 1 : 0); + } + else if (sortType == SORT_TYPE.SORT_ATTACHMENT) + { + comparison = (this.hasAttachments ? 0 : 1) - (o.hasAttachments ? 0 : 1); + } + + if (comparison != 0) + { + return comparison * ascender; + } + + int dateAscender = (sortDateAscending ? 1 : -1); + + return this.compareDate.compareTo(o.compareDate) * dateAscender; + } + + Pattern pattern = null; + String patternString = "^ *(re|aw|fw|fwd): *"; + private String stripPrefixes(String in) + { + synchronized (patternString) + { + if (pattern == null) + { + pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); + } + } + + Matcher matcher = pattern.matcher(in); + + int lastPrefix = -1; + + while (matcher.find()) + { + lastPrefix = matcher.end(); + } + + if (lastPrefix > -1 && lastPrefix < in.length() - 1) + { + return in.substring(lastPrefix); + } + else + { + return in; + } + } + } + + class MessageViewHolder + implements OnCheckedChangeListener + { + public TextView subject; + public TextView preview; + public TextView from; + public TextView time; + public TextView date; + public CheckBox flagged; + public View chip; + public CheckBox selected; + public int position = -1; + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) + { + if (position!=-1) + { + MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position); + if (message.selected!=isChecked) + { + if (isChecked) + { + mSelectedCount++; + } + else if (mSelectedCount > 0) + { + mSelectedCount--; + } + + // We must set the flag before showing the buttons as the + // buttons text depends on what is selected. + message.selected = isChecked; + if (!mCheckboxes) + { + if (isChecked == true) + { + selected.setVisibility(View.VISIBLE); + } + else + { + selected.setVisibility(View.GONE); + } + } + toggleBatchButtons(); + } + } + } + } + + private void hideBatchButtons() + { + //TODO: Fade out animation + mBatchButtonArea.setVisibility(View.GONE); + } + + private void showBatchButtons() + { + //TODO: Fade in animation + mBatchButtonArea.setVisibility(View.VISIBLE); + } + + private void toggleBatchButtons() + { + if (mSelectedCount < 0) + { + mSelectedCount = 0; + } + + int readButtonIconId; + int flagButtonIconId; + + if (mSelectedCount==0) + { + readButtonIconId = R.drawable.ic_button_mark_read; + flagButtonIconId = R.drawable.ic_button_flag; + hideBatchButtons(); + } + else + { + boolean newReadState = computeBatchDirection(false); + if (newReadState) + { + readButtonIconId = R.drawable.ic_button_mark_read; + } + else + { + readButtonIconId = R.drawable.ic_button_mark_unread; + } + boolean newFlagState = computeBatchDirection(true); + if (newFlagState) + { + flagButtonIconId = R.drawable.ic_button_flag; + } + else + { + flagButtonIconId = R.drawable.ic_button_unflag; + } + showBatchButtons(); + } + + mBatchReadButton.setImageResource(readButtonIconId); + mBatchFlagButton.setImageResource(flagButtonIconId); + } + + class FooterViewHolder + { + public ProgressBar progress; + public TextView main; + } + + public class FolderInfoHolder + { + public String name; + public String displayName; + public boolean loading; + public boolean lastCheckFailed; + public Folder folder; + + /** + * Outbox is handled differently from any other folder. + */ + public boolean outbox; + + public FolderInfoHolder(Folder folder, Account account) + { + populate(folder, account); + } + + public void populate(Folder folder, Account account) + { + this.folder = folder; + this.name = folder.getName(); + + if (this.name.equalsIgnoreCase(K9.INBOX)) + { + this.displayName = getString(R.string.special_mailbox_name_inbox); + } + else + { + this.displayName = this.name; + } + + if (this.name.equals(account.getOutboxFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_outbox_fmt), this.name); + this.outbox = true; + } + + if (this.name.equals(account.getDraftsFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_drafts_fmt), this.name); + } + + if (this.name.equals(account.getTrashFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_trash_fmt), this.name); + } + + if (this.name.equals(account.getSentFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_sent_fmt), this.name); + } + + if (this.name.equals(account.getArchiveFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_archive_fmt), this.name); + } + + if (this.name.equals(account.getSpamFolderName())) + { + this.displayName = String.format(getString(R.string.special_mailbox_name_spam_fmt), this.name); + } + } + } + + private boolean computeBatchDirection(boolean flagged) + { + boolean newState = false; + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + if (flagged) + { + if (!holder.flagged) + { + newState = true; + break; + } + } + else + { + if (!holder.read) + { + newState = true; + break; + } + } + } + } + } + return newState; + } + + private boolean anySelected() + { + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + return true; + } + } + } + return false; + } + + public void onClick(View v) + { + boolean newState = false; + List messageList = new ArrayList(); + List removeHolderList = new ArrayList(); + + if (v == mBatchDoneButton) + { + setAllSelected(false); + return; + } + + if (v == mBatchFlagButton) + { + newState = computeBatchDirection(true); + } + else + { + newState = computeBatchDirection(false); + } + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + if (v == mBatchDeleteButton) + { + removeHolderList.add(holder); + } + else if (v == mBatchFlagButton) + { + holder.flagged = newState; + } + else if (v == mBatchReadButton) + { + holder.read = newState; + } + messageList.add(holder.message); + } + } + } + mAdapter.removeMessages(removeHolderList); + + if (!messageList.isEmpty()) + { + if (v == mBatchDeleteButton) + { + mController.deleteMessages(messageList.toArray(new Message[0]), null); + mSelectedCount = 0; + toggleBatchButtons(); + } + else + { + mController.setFlag(messageList.toArray(new Message[0]), (v == mBatchReadButton ? Flag.SEEN : Flag.FLAGGED), newState); + } + } + else + { + // Should not happen + Toast.makeText(this, R.string.no_message_seletected_toast, Toast.LENGTH_SHORT).show(); + } + mHandler.sortMessages(); + } + + private void setAllSelected(boolean isSelected) + { + mSelectedCount = 0; + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + holder.selected = isSelected; + mSelectedCount += (isSelected ? 1 : 0); + } + } + mAdapter.notifyDataSetChanged(); + toggleBatchButtons(); + } + + private void setSelected(MessageInfoHolder holder, boolean newState) + { + if (holder.selected != newState) + { + holder.selected = newState; + mSelectedCount += (newState ? 1 : -1); + } + mAdapter.notifyDataSetChanged(); + toggleBatchButtons(); + } + + private void flagSelected(Flag flag, boolean newState) + { + List messageList = new ArrayList(); + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + messageList.add(holder.message); + if (flag == Flag.SEEN) + { + holder.read = newState; + } + else if (flag == Flag.FLAGGED) + { + holder.flagged = newState; + } + } + } + } + mController.setFlag(messageList.toArray(new Message[0]), flag, newState); + mHandler.sortMessages(); + } + + private void deleteSelected() + { + List messageList = new ArrayList(); + List removeHolderList = new ArrayList(); + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + removeHolderList.add(holder); + messageList.add(holder.message); + } + } + } + mAdapter.removeMessages(removeHolderList); + + mController.deleteMessages(messageList.toArray(new Message[0]), null); + mSelectedCount = 0; + toggleBatchButtons(); + } + + private void onMoveBatch() + { + if (mController.isMoveCapable(mAccount) == false) + { + return; + } + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isMoveCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - } - } - } - - final Folder folder = mCurrentFolder.folder; - final Intent intent = new Intent(this, ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount.getUuid()); - intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName()); - intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName()); - startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH); - } - - private void onMoveChosenBatch(String folderName) - { - if (mController.isMoveCapable(mAccount) == false) - { - return; - } - List messageList = new ArrayList(); - - List removeHolderList = new ArrayList(); - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isMoveCapable(message) == false) - { - Toast toast = Toast.makeText(this, + toast.show(); + return; + } + } + } + } + + final Folder folder = mCurrentFolder.folder; + final Intent intent = new Intent(this, ChooseFolder.class); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount.getUuid()); + intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName()); + intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName()); + startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE_BATCH); + } + + private void onMoveChosenBatch(String folderName) + { + if (mController.isMoveCapable(mAccount) == false) + { + return; + } + List messageList = new ArrayList(); + + List removeHolderList = new ArrayList(); + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isMoveCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - messageList.add(holder.message); - removeHolderList.add(holder); - } - } - } - mAdapter.removeMessages(removeHolderList); - - mController.moveMessages(mAccount, mCurrentFolder.name, messageList.toArray(new Message[0]), folderName, null); - mSelectedCount = 0; - toggleBatchButtons(); - } - - private void onArchiveBatch() - { - if (mController.isMoveCapable(mAccount) == false) - { - return; - } - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isMoveCapable(message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - } - } - } - - String folderName = mAccount.getArchiveFolderName(); - if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) - { - return; - } - onMoveChosenBatch(folderName); - } - - private void onSpamBatch() - { - if (mController.isMoveCapable(mAccount) == false) - { - return; - } - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isMoveCapable(message) == false) - { - Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - } - } - } - - String folderName = mAccount.getSpamFolderName(); - if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) - { - return; - } - onMoveChosenBatch(folderName); - } - - private void onCopyBatch() - { - if (mController.isCopyCapable(mAccount) == false) - { - return; - } - - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isCopyCapable(message) == false) - { - Toast toast = Toast.makeText(this, + toast.show(); + return; + } + messageList.add(holder.message); + removeHolderList.add(holder); + } + } + } + mAdapter.removeMessages(removeHolderList); + + mController.moveMessages(mAccount, mCurrentFolder.name, messageList.toArray(new Message[0]), folderName, null); + mSelectedCount = 0; + toggleBatchButtons(); + } + + private void onArchiveBatch() + { + if (mController.isMoveCapable(mAccount) == false) + { + return; + } + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isMoveCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + } + } + } + + String folderName = mAccount.getArchiveFolderName(); + if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) + { + return; + } + onMoveChosenBatch(folderName); + } + + private void onSpamBatch() + { + if (mController.isMoveCapable(mAccount) == false) + { + return; + } + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isMoveCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); + toast.show(); + return; + } + } + } + } + + String folderName = mAccount.getSpamFolderName(); + if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) + { + return; + } + onMoveChosenBatch(folderName); + } + + private void onCopyBatch() + { + if (mController.isCopyCapable(mAccount) == false) + { + return; + } + + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isCopyCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - } - } - } - - final Folder folder = mCurrentFolder.folder; - final Intent intent = new Intent(this, ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount.getUuid()); - intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName()); - intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName()); - startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY_BATCH); - } - - private void onCopyChosenBatch(String folderName) - { - if (mController.isCopyCapable(mAccount) == false) - { - return; - } - - List messageList = new ArrayList(); - synchronized (mAdapter.messages) - { - for (MessageInfoHolder holder : mAdapter.messages) - { - if (holder.selected) - { - Message message = holder.message; - if (mController.isCopyCapable(message) == false) - { - Toast toast = Toast.makeText(this, + toast.show(); + return; + } + } + } + } + + final Folder folder = mCurrentFolder.folder; + final Intent intent = new Intent(this, ChooseFolder.class); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount.getUuid()); + intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName()); + intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName()); + startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY_BATCH); + } + + private void onCopyChosenBatch(String folderName) + { + if (mController.isCopyCapable(mAccount) == false) + { + return; + } + + List messageList = new ArrayList(); + synchronized (mAdapter.messages) + { + for (MessageInfoHolder holder : mAdapter.messages) + { + if (holder.selected) + { + Message message = holder.message; + if (mController.isCopyCapable(message) == false) + { + Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG); - toast.show(); - return; - } - messageList.add(holder.message); - } - } - } - mController.copyMessages(mAccount, mCurrentFolder.name, messageList.toArray(new Message[0]), folderName, null); - } -} + toast.show(); + return; + } + messageList.add(holder.message); + } + } + } + mController.copyMessages(mAccount, mCurrentFolder.name, messageList.toArray(new Message[0]), folderName, null); + } +} diff --git a/src/com/fsck/k9/activity/ToggleScrollView.java b/src/com/fsck/k9/activity/ToggleScrollView.java index 46af9c77f0..6ca6d40634 100644 --- a/src/com/fsck/k9/activity/ToggleScrollView.java +++ b/src/com/fsck/k9/activity/ToggleScrollView.java @@ -1,33 +1,33 @@ -package com.fsck.k9.activity; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.ScrollView; - -public class ToggleScrollView extends ScrollView -{ - private boolean mScrolling = true; - - public ToggleScrollView(Context context, AttributeSet attrs) - { - super(context, attrs); - } - - public void setScrolling(boolean enable) - { - mScrolling = enable; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) - { - return (mScrolling) ? super.onTouchEvent(ev) : true; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) - { - return (mScrolling) ? super.onInterceptTouchEvent(ev) : false; - } -} +package com.fsck.k9.activity; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.ScrollView; + +public class ToggleScrollView extends ScrollView +{ + private boolean mScrolling = true; + + public ToggleScrollView(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + public void setScrolling(boolean enable) + { + mScrolling = enable; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + return (mScrolling) ? super.onTouchEvent(ev) : true; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) + { + return (mScrolling) ? super.onInterceptTouchEvent(ev) : false; + } +} diff --git a/src/com/fsck/k9/helper/AutoSyncSdk5.java b/src/com/fsck/k9/helper/AutoSyncSdk5.java index 88ee6ff8fb..1468cdcf02 100644 --- a/src/com/fsck/k9/helper/AutoSyncSdk5.java +++ b/src/com/fsck/k9/helper/AutoSyncSdk5.java @@ -1,21 +1,21 @@ -package com.fsck.k9.helper; - -import android.content.ContentResolver; -import android.content.Context; - -public class AutoSyncSdk5 implements IAutoSync -{ - public void initialize(Context context) throws NoSuchMethodException - { - // Nothing to do here - } - - public boolean getMasterSyncAutomatically() - { - /* - * SDK 2.0/API 5 introduced an official method to query the auto-sync - * state. - */ - return ContentResolver.getMasterSyncAutomatically(); - } -} +package com.fsck.k9.helper; + +import android.content.ContentResolver; +import android.content.Context; + +public class AutoSyncSdk5 implements IAutoSync +{ + public void initialize(Context context) throws NoSuchMethodException + { + // Nothing to do here + } + + public boolean getMasterSyncAutomatically() + { + /* + * SDK 2.0/API 5 introduced an official method to query the auto-sync + * state. + */ + return ContentResolver.getMasterSyncAutomatically(); + } +} diff --git a/src/com/fsck/k9/mail/BodyPart.java b/src/com/fsck/k9/mail/BodyPart.java index 0e43f71174..b0c1b3e817 100644 --- a/src/com/fsck/k9/mail/BodyPart.java +++ b/src/com/fsck/k9/mail/BodyPart.java @@ -1,12 +1,12 @@ - -package com.fsck.k9.mail; - -public abstract class BodyPart implements Part -{ - protected Multipart mParent; - - public Multipart getParent() - { - return mParent; - } -} + +package com.fsck.k9.mail; + +public abstract class BodyPart implements Part +{ + protected Multipart mParent; + + public Multipart getParent() + { + return mParent; + } +} diff --git a/src/com/fsck/k9/mail/FetchProfile.java b/src/com/fsck/k9/mail/FetchProfile.java index 7dcf514019..3aceeb94fd 100644 --- a/src/com/fsck/k9/mail/FetchProfile.java +++ b/src/com/fsck/k9/mail/FetchProfile.java @@ -1,59 +1,59 @@ - -package com.fsck.k9.mail; - -import java.util.ArrayList; - -/** - *
- * A FetchProfile is a list of items that should be downloaded in bulk for a set of messages.
- * FetchProfile can contain the following objects:
- *      FetchProfile.Item:      Described below.
- *      Message:                Indicates that the body of the entire message should be fetched.
- *                              Synonymous with FetchProfile.Item.BODY.
- *      Part:                   Indicates that the given Part should be fetched. The provider
- *                              is expected have previously created the given BodyPart and stored
- *                              any information it needs to download the content.
- * 
- */ -public class FetchProfile extends ArrayList -{ - /** - * Default items available for pre-fetching. It should be expected that any - * item fetched by using these items could potentially include all of the - * previous items. - */ - public enum Item - { - /** - * Download the flags of the message. - */ - FLAGS, - - /** - * Download the envelope of the message. This should include at minimum - * the size and the following headers: date, subject, from, content-type, to, cc - */ - ENVELOPE, - - /** - * Download the structure of the message. This maps directly to IMAP's BODYSTRUCTURE - * and may map to other providers. - * The provider should, if possible, fill in a properly formatted MIME structure in - * the message without actually downloading any message data. If the provider is not - * capable of this operation it should specifically set the body of the message to null - * so that upper levels can detect that a full body download is needed. - */ - STRUCTURE, - - /** - * A sane portion of the entire message, cut off at a provider determined limit. - * This should generaly be around 50kB. - */ - BODY_SANE, - - /** - * The entire message. - */ - BODY, - } -} + +package com.fsck.k9.mail; + +import java.util.ArrayList; + +/** + *
+ * A FetchProfile is a list of items that should be downloaded in bulk for a set of messages.
+ * FetchProfile can contain the following objects:
+ *      FetchProfile.Item:      Described below.
+ *      Message:                Indicates that the body of the entire message should be fetched.
+ *                              Synonymous with FetchProfile.Item.BODY.
+ *      Part:                   Indicates that the given Part should be fetched. The provider
+ *                              is expected have previously created the given BodyPart and stored
+ *                              any information it needs to download the content.
+ * 
+ */ +public class FetchProfile extends ArrayList +{ + /** + * Default items available for pre-fetching. It should be expected that any + * item fetched by using these items could potentially include all of the + * previous items. + */ + public enum Item + { + /** + * Download the flags of the message. + */ + FLAGS, + + /** + * Download the envelope of the message. This should include at minimum + * the size and the following headers: date, subject, from, content-type, to, cc + */ + ENVELOPE, + + /** + * Download the structure of the message. This maps directly to IMAP's BODYSTRUCTURE + * and may map to other providers. + * The provider should, if possible, fill in a properly formatted MIME structure in + * the message without actually downloading any message data. If the provider is not + * capable of this operation it should specifically set the body of the message to null + * so that upper levels can detect that a full body download is needed. + */ + STRUCTURE, + + /** + * A sane portion of the entire message, cut off at a provider determined limit. + * This should generaly be around 50kB. + */ + BODY_SANE, + + /** + * The entire message. + */ + BODY, + } +} diff --git a/src/com/fsck/k9/mail/Multipart.java b/src/com/fsck/k9/mail/Multipart.java index 3c91a107ff..e12dd69c06 100644 --- a/src/com/fsck/k9/mail/Multipart.java +++ b/src/com/fsck/k9/mail/Multipart.java @@ -1,82 +1,82 @@ - -package com.fsck.k9.mail; - -import java.util.ArrayList; - -import com.fsck.k9.mail.internet.MimeHeader; -import com.fsck.k9.mail.internet.TextBody; - -public abstract class Multipart implements Body -{ - protected Part mParent; - - protected ArrayList mParts = new ArrayList(); - - protected String mContentType; - - public void addBodyPart(BodyPart part) throws MessagingException - { - mParts.add(part); - } - - public void addBodyPart(BodyPart part, int index) throws MessagingException - { - mParts.add(index, part); - } - - public BodyPart getBodyPart(int index) throws MessagingException - { - return mParts.get(index); - } - - public String getContentType() throws MessagingException - { - return mContentType; - } - - public int getCount() throws MessagingException - { - return mParts.size(); - } - - public boolean removeBodyPart(BodyPart part) throws MessagingException - { - return mParts.remove(part); - } - - public void removeBodyPart(int index) throws MessagingException - { - mParts.remove(index); - } - - public Part getParent() throws MessagingException - { - return mParent; - } - - public void setParent(Part parent) throws MessagingException - { - this.mParent = parent; - } - - public void setEncoding(String encoding) - { - for (BodyPart part : mParts) - { - try - { - Body body = part.getBody(); - if (body instanceof TextBody) - { - part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding); - ((TextBody)body).setEncoding(encoding); - } - } - catch (MessagingException e) - { - // Ignore - } - } - - } -} + +package com.fsck.k9.mail; + +import java.util.ArrayList; + +import com.fsck.k9.mail.internet.MimeHeader; +import com.fsck.k9.mail.internet.TextBody; + +public abstract class Multipart implements Body +{ + protected Part mParent; + + protected ArrayList mParts = new ArrayList(); + + protected String mContentType; + + public void addBodyPart(BodyPart part) throws MessagingException + { + mParts.add(part); + } + + public void addBodyPart(BodyPart part, int index) throws MessagingException + { + mParts.add(index, part); + } + + public BodyPart getBodyPart(int index) throws MessagingException + { + return mParts.get(index); + } + + public String getContentType() throws MessagingException + { + return mContentType; + } + + public int getCount() throws MessagingException + { + return mParts.size(); + } + + public boolean removeBodyPart(BodyPart part) throws MessagingException + { + return mParts.remove(part); + } + + public void removeBodyPart(int index) throws MessagingException + { + mParts.remove(index); + } + + public Part getParent() throws MessagingException + { + return mParent; + } + + public void setParent(Part parent) throws MessagingException + { + this.mParent = parent; + } + + public void setEncoding(String encoding) + { + for (BodyPart part : mParts) + { + try + { + Body body = part.getBody(); + if (body instanceof TextBody) + { + part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding); + ((TextBody)body).setEncoding(encoding); + } + } + catch (MessagingException e) + { + // Ignore + } + } + + } +} diff --git a/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java b/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java index 4fc3a9ad34..d4c179601f 100644 --- a/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java +++ b/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java @@ -1,71 +1,71 @@ -package com.fsck.k9.mail.transport; - -import com.fsck.k9.mail.store.TrustManagerFactory; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.LayeredSocketFactory; -import org.apache.http.params.HttpParams; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -public class TrustedSocketFactory implements LayeredSocketFactory -{ - private SSLSocketFactory mSocketFactory; - private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory; - - public TrustedSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException - { - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, new TrustManager[] - { - TrustManagerFactory.get(host, secure) - }, new SecureRandom()); - mSocketFactory = sslContext.getSocketFactory(); - mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); - mSchemeSocketFactory.setHostnameVerifier( - org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - } - - public Socket connectSocket(Socket sock, String host, int port, - InetAddress localAddress, int localPort, HttpParams params) - throws IOException, UnknownHostException, ConnectTimeoutException - { - return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params); - } - - public Socket createSocket() throws IOException - { - return mSocketFactory.createSocket(); - } - - public boolean isSecure(Socket sock) throws IllegalArgumentException - { - return mSchemeSocketFactory.isSecure(sock); - } - public Socket createSocket( - final Socket socket, - final String host, - final int port, - final boolean autoClose - ) throws IOException, UnknownHostException - { - SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket( - socket, - host, - port, - autoClose - ); - //hostnameVerifier.verify(host, sslSocket); - // verifyHostName() didn't blowup - good! - return sslSocket; - } -} +package com.fsck.k9.mail.transport; + +import com.fsck.k9.mail.store.TrustManagerFactory; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.scheme.LayeredSocketFactory; +import org.apache.http.params.HttpParams; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class TrustedSocketFactory implements LayeredSocketFactory +{ + private SSLSocketFactory mSocketFactory; + private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory; + + public TrustedSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException + { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] + { + TrustManagerFactory.get(host, secure) + }, new SecureRandom()); + mSocketFactory = sslContext.getSocketFactory(); + mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); + mSchemeSocketFactory.setHostnameVerifier( + org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + } + + public Socket connectSocket(Socket sock, String host, int port, + InetAddress localAddress, int localPort, HttpParams params) + throws IOException, UnknownHostException, ConnectTimeoutException + { + return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params); + } + + public Socket createSocket() throws IOException + { + return mSocketFactory.createSocket(); + } + + public boolean isSecure(Socket sock) throws IllegalArgumentException + { + return mSchemeSocketFactory.isSecure(sock); + } + public Socket createSocket( + final Socket socket, + final String host, + final int port, + final boolean autoClose + ) throws IOException, UnknownHostException + { + SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket( + socket, + host, + port, + autoClose + ); + //hostnameVerifier.verify(host, sslSocket); + // verifyHostName() didn't blowup - good! + return sslSocket; + } +} diff --git a/src/com/fsck/k9/service/MailService.java b/src/com/fsck/k9/service/MailService.java index 23e053be40..7404074d88 100644 --- a/src/com/fsck/k9/service/MailService.java +++ b/src/com/fsck/k9/service/MailService.java @@ -1,536 +1,536 @@ - -package com.fsck.k9.service; - -import java.util.Collection; -import java.util.Date; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.NetworkInfo.State; -import android.os.IBinder; -import android.util.Log; - -import com.fsck.k9.Account; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; -import com.fsck.k9.R; -import com.fsck.k9.Account.FolderMode; -import com.fsck.k9.controller.MessagingController; -import com.fsck.k9.helper.AutoSyncHelper; -import com.fsck.k9.mail.Pusher; - -/** - */ -public class MailService extends CoreService -{ - private static final String ACTION_CHECK_MAIL = "com.fsck.k9.intent.action.MAIL_SERVICE_WAKEUP"; - private static final String ACTION_RESET = "com.fsck.k9.intent.action.MAIL_SERVICE_RESET"; - private static final String ACTION_RESCHEDULE_POLL = "com.fsck.k9.intent.action.MAIL_SERVICE_RESCHEDULE_POLL"; - private static final String ACTION_CANCEL = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL"; - private static final String ACTION_REFRESH_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_REFRESH_PUSHERS"; - private static final String ACTION_RESTART_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_RESTART_PUSHERS"; - private static final String CONNECTIVITY_CHANGE = "com.fsck.k9.intent.action.MAIL_SERVICE_CONNECTIVITY_CHANGE"; - private static final String CANCEL_CONNECTIVITY_NOTICE = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL_CONNECTIVITY_NOTICE"; - - private static long nextCheck = -1; - - public static void actionReset(Context context, Integer wakeLockId) - { - Intent i = new Intent(); - i.setClass(context, MailService.class); - i.setAction(MailService.ACTION_RESET); - addWakeLockId(i, wakeLockId); - if (wakeLockId == null) - { - addWakeLock(context, i); - } - context.startService(i); - } - - public static void actionRestartPushers(Context context, Integer wakeLockId) - { - Intent i = new Intent(); - i.setClass(context, MailService.class); - i.setAction(MailService.ACTION_RESTART_PUSHERS); - addWakeLockId(i, wakeLockId); - if (wakeLockId == null) - { - addWakeLock(context, i); - } - context.startService(i); - } - - public static void actionReschedulePoll(Context context, Integer wakeLockId) - { - Intent i = new Intent(); - i.setClass(context, MailService.class); - i.setAction(MailService.ACTION_RESCHEDULE_POLL); - addWakeLockId(i, wakeLockId); - if (wakeLockId == null) - { - addWakeLock(context, i); - } - context.startService(i); - } - - public static void actionCancel(Context context, Integer wakeLockId) - { - Intent i = new Intent(); - i.setClass(context, MailService.class); - i.setAction(MailService.ACTION_CANCEL); - addWakeLockId(i, wakeLockId); - context.startService(i); - } - - public static void connectivityChange(Context context, Integer wakeLockId) - { - Intent i = new Intent(); - i.setClass(context, MailService.class); - i.setAction(MailService.CONNECTIVITY_CHANGE); - addWakeLockId(i, wakeLockId); - context.startService(i); - } - - @Override - public void onCreate() - { - super.onCreate(); - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: onCreate"); - } - - @Override - public void startService(Intent intent, int startId) - { - Integer startIdObj = startId; - long startTime = System.currentTimeMillis(); - try - { - ConnectivityManager connectivityManager = (ConnectivityManager)getApplication().getSystemService(Context.CONNECTIVITY_SERVICE); - boolean doBackground = true; - boolean hasConnectivity = false; - - if (connectivityManager != null) - { - NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); - if (netInfo != null) - { - State state = netInfo.getState(); - hasConnectivity = state == State.CONNECTED; - } - boolean backgroundData = connectivityManager.getBackgroundDataSetting(); - boolean autoSync = true; - if (AutoSyncHelper.isAvailable()) - { - autoSync = AutoSyncHelper.getMasterSyncAutomatically(); - - Log.i(K9.LOG_TAG, "AutoSync help is available, autoSync = " + autoSync); - } - - K9.BACKGROUND_OPS bOps = K9.getBackgroundOps(); - - switch (bOps) - { - case NEVER: - doBackground = false; - break; - case ALWAYS: - doBackground = true; - break; - case WHEN_CHECKED: - doBackground = backgroundData; - break; - case WHEN_CHECKED_AUTO_SYNC: - doBackground = backgroundData & autoSync; - break; - } - - } - - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "MailService.onStart(" + intent + ", " + startId - + "), hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground); - - // MessagingController.getInstance(getApplication()).addListener(mListener); - if (ACTION_CHECK_MAIL.equals(intent.getAction())) - { - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "***** MailService *****: checking mail"); - - if (hasConnectivity && doBackground) - { - PollService.startService(this); - } - reschedulePoll(hasConnectivity, doBackground, startIdObj, false); - startIdObj = null; - } - else if (ACTION_CANCEL.equals(intent.getAction())) - { - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: cancel"); - - cancel(); - } - else if (ACTION_RESET.equals(intent.getAction())) - { - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: reschedule"); - - rescheduleAll(hasConnectivity, doBackground, startIdObj); - startIdObj = null; - - } - else if (ACTION_RESTART_PUSHERS.equals(intent.getAction())) - { - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: restarting pushers"); - reschedulePushers(hasConnectivity, doBackground, startIdObj); - startIdObj = null; - - } - else if (ACTION_RESCHEDULE_POLL.equals(intent.getAction())) - { - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: rescheduling poll"); - reschedulePoll(hasConnectivity, doBackground, startIdObj, true); - startIdObj = null; - - } - else if (ACTION_REFRESH_PUSHERS.equals(intent.getAction())) - { - if (hasConnectivity && doBackground) - { - refreshPushers(null); - schedulePushers(startIdObj); - startIdObj = null; - } - } - else if (CONNECTIVITY_CHANGE.equals(intent.getAction())) - { - notifyConnectionStatus(hasConnectivity); - rescheduleAll(hasConnectivity, doBackground, startIdObj); - startIdObj = null; - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Got connectivity action with hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground); - } - else if (CANCEL_CONNECTIVITY_NOTICE.equals(intent.getAction())) - { - notifyConnectionStatus(true); - } - } - finally - { - if (startIdObj != null) - { - stopSelf(startId); - } - } - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "MailService.onStart took " + (System.currentTimeMillis() - startTime) + "ms"); - } - - private void rescheduleAll(final boolean hasConnectivity, final boolean doBackground, final Integer startId) - { - reschedulePoll(hasConnectivity, doBackground, null, true); - reschedulePushers(hasConnectivity, doBackground, startId); - - } - - private void notifyConnectionStatus(boolean hasConnectivity) - { - if (true) return; - NotificationManager notifMgr = - (NotificationManager)getApplication().getSystemService(Context.NOTIFICATION_SERVICE); - if (hasConnectivity == false) - { - String notice = getApplication().getString(R.string.no_connection_alert); - String header = getApplication().getString(R.string.alert_header); - - - Notification notif = new Notification(R.drawable.stat_notify_email_generic, - header, System.currentTimeMillis()); - - Intent i = new Intent(); - i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); - i.setAction(MailService.CANCEL_CONNECTIVITY_NOTICE); - - PendingIntent pi = PendingIntent.getService(this, 0, i, 0); - - notif.setLatestEventInfo(getApplication(), header, notice, pi); - notif.flags = Notification.FLAG_ONGOING_EVENT; - - notifMgr.notify(K9.CONNECTIVITY_ID, notif); - } - else - { - notifMgr.cancel(K9.CONNECTIVITY_ID); - } - } - - @Override - public void onDestroy() - { - if (K9.DEBUG) - Log.v(K9.LOG_TAG, "***** MailService *****: onDestroy()"); - super.onDestroy(); - // MessagingController.getInstance(getApplication()).removeListener(mListener); - } - - private void cancel() - { - Intent i = new Intent(); - i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); - i.setAction(ACTION_CHECK_MAIL); - BootReceiver.cancelIntent(this, i); - } - - private final static String PREVIOUS_INTERVAL = "MailService.previousInterval"; - private final static String LAST_CHECK_END = "MailService.lastCheckEnd"; - - public static void saveLastCheckEnd(Context context) - { - - long lastCheckEnd = System.currentTimeMillis(); - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Saving lastCheckEnd = " + new Date(lastCheckEnd)); - Preferences prefs = Preferences.getPreferences(context); - SharedPreferences sPrefs = prefs.getPreferences(); - SharedPreferences.Editor editor = sPrefs.edit(); - editor.putLong(LAST_CHECK_END, lastCheckEnd); - editor.commit(); - } - - private void reschedulePoll(final boolean hasConnectivity, final boolean doBackground, Integer startId, final boolean considerLastCheckEnd) - { - if (hasConnectivity && doBackground) - { - execute(getApplication(), new Runnable() - { - public void run() - { - int shortestInterval = -1; - - Preferences prefs = Preferences.getPreferences(MailService.this); - SharedPreferences sPrefs = prefs.getPreferences(); - int previousInterval = sPrefs.getInt(PREVIOUS_INTERVAL, -1); - long lastCheckEnd = sPrefs.getLong(LAST_CHECK_END, -1); - for (Account account : prefs.getAccounts()) - { - if (account.getAutomaticCheckIntervalMinutes() != -1 - && account.getFolderSyncMode() != FolderMode.NONE - && (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1)) - { - shortestInterval = account.getAutomaticCheckIntervalMinutes(); - } - } - SharedPreferences.Editor editor = sPrefs.edit(); - editor.putInt(PREVIOUS_INTERVAL, shortestInterval); - editor.commit(); - - if (shortestInterval == -1) - { - nextCheck = -1; - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "No next check scheduled for package " + getApplication().getPackageName()); - cancel(); - } - else - { - long delay = (shortestInterval * (60 * 1000)); - long base = (previousInterval == -1 || lastCheckEnd == -1 || considerLastCheckEnd == false ? System.currentTimeMillis() : lastCheckEnd); - long nextTime = base + delay; - if (K9.DEBUG) - Log.i(K9.LOG_TAG, - "previousInterval = " + previousInterval - + ", shortestInterval = " + shortestInterval - + ", lastCheckEnd = " + new Date(lastCheckEnd) - + ", considerLastCheckEnd = " + considerLastCheckEnd); - nextCheck = nextTime; - try - { - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Next check for package " + getApplication().getPackageName() + " scheduled for " + new Date(nextTime)); - } - catch (Exception e) - { - // I once got a NullPointerException deep in new Date(); - Log.e(K9.LOG_TAG, "Exception while logging", e); - } - - Intent i = new Intent(); - i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); - i.setAction(ACTION_CHECK_MAIL); - BootReceiver.scheduleIntent(MailService.this, nextTime, i); - - } - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); - } - else - { - nextCheck = -1; - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "No connectivity, canceling check for " + getApplication().getPackageName()); - cancel(); - } - } - - private void stopPushers(final Integer startId) - { - execute(getApplication(), new Runnable() - { - public void run() - { - MessagingController.getInstance(getApplication()).stopAllPushing(); - PushService.stopService(MailService.this); - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); - } - - private void reschedulePushers(final boolean hasConnectivity, final boolean doBackground, final Integer startId) - { - execute(getApplication(), new Runnable() - { - public void run() - { - - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Rescheduling pushers"); - stopPushers(null); - if (hasConnectivity && doBackground) - { - setupPushers(null); - schedulePushers(startId); - } - - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, null); - } - - private void setupPushers(final Integer startId) - { - execute(getApplication(), new Runnable() - { - public void run() - { - boolean pushing = false; - for (Account account : Preferences.getPreferences(MailService.this).getAccounts()) - { - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Setting up pushers for account " + account.getDescription()); - pushing |= MessagingController.getInstance(getApplication()).setupPushing(account); - } - if (pushing) - { - PushService.startService(MailService.this); - } - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); - } - - private void refreshPushers(final Integer startId) - { - execute(getApplication(), new Runnable() - { - public void run() - { - try - { - long nowTime = System.currentTimeMillis(); - if (K9.DEBUG) - Log.i(K9.LOG_TAG, "Refreshing pushers"); - Collection pushers = MessagingController.getInstance(getApplication()).getPushers(); - for (Pusher pusher : pushers) - { - long lastRefresh = pusher.getLastRefresh(); - int refreshInterval = pusher.getRefreshInterval(); - long sinceLast = nowTime - lastRefresh; - if (sinceLast + 10000 > refreshInterval) // Add 10 seconds to keep pushers in sync, avoid drift - { - if (K9.DEBUG) - { - Log.d(K9.LOG_TAG, "PUSHREFRESH: refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval - + ", nowTime = " + nowTime + ", sinceLast = " + sinceLast); - } - pusher.refresh(); - pusher.setLastRefresh(nowTime); - } - else - { - if (K9.DEBUG) - { - Log.d(K9.LOG_TAG, "PUSHREFRESH: NOT refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval - + ", nowTime = " + nowTime + ", sinceLast = " + sinceLast); - } - } - } - } - catch (Exception e) - { - Log.e(K9.LOG_TAG, "Exception while refreshing pushers", e); - } - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); - } - - private void schedulePushers(final Integer startId) - { - execute(getApplication(), new Runnable() - { - public void run() - { - int minInterval = -1; - - Collection pushers = MessagingController.getInstance(getApplication()).getPushers(); - for (Pusher pusher : pushers) - { - int interval = pusher.getRefreshInterval(); - if (interval > 0 && (interval < minInterval || minInterval == -1)) - { - minInterval = interval; - } - } - if (K9.DEBUG) - { - Log.v(K9.LOG_TAG, "Pusher refresh interval = " + minInterval); - } - if (minInterval > 0) - { - long nextTime = System.currentTimeMillis() + minInterval; - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "Next pusher refresh scheduled for " + new Date(nextTime)); - Intent i = new Intent(); - i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); - i.setAction(ACTION_REFRESH_PUSHERS); - BootReceiver.scheduleIntent(MailService.this, nextTime, i); - } - } - } - , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); - } - - - @Override - public IBinder onBind(Intent intent) - { - return null; - } - - public static long getNextPollTime() - { - return nextCheck; - } - - -} + +package com.fsck.k9.service; + +import java.util.Collection; +import java.util.Date; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.NetworkInfo.State; +import android.os.IBinder; +import android.util.Log; + +import com.fsck.k9.Account; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.R; +import com.fsck.k9.Account.FolderMode; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.helper.AutoSyncHelper; +import com.fsck.k9.mail.Pusher; + +/** + */ +public class MailService extends CoreService +{ + private static final String ACTION_CHECK_MAIL = "com.fsck.k9.intent.action.MAIL_SERVICE_WAKEUP"; + private static final String ACTION_RESET = "com.fsck.k9.intent.action.MAIL_SERVICE_RESET"; + private static final String ACTION_RESCHEDULE_POLL = "com.fsck.k9.intent.action.MAIL_SERVICE_RESCHEDULE_POLL"; + private static final String ACTION_CANCEL = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL"; + private static final String ACTION_REFRESH_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_REFRESH_PUSHERS"; + private static final String ACTION_RESTART_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_RESTART_PUSHERS"; + private static final String CONNECTIVITY_CHANGE = "com.fsck.k9.intent.action.MAIL_SERVICE_CONNECTIVITY_CHANGE"; + private static final String CANCEL_CONNECTIVITY_NOTICE = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL_CONNECTIVITY_NOTICE"; + + private static long nextCheck = -1; + + public static void actionReset(Context context, Integer wakeLockId) + { + Intent i = new Intent(); + i.setClass(context, MailService.class); + i.setAction(MailService.ACTION_RESET); + addWakeLockId(i, wakeLockId); + if (wakeLockId == null) + { + addWakeLock(context, i); + } + context.startService(i); + } + + public static void actionRestartPushers(Context context, Integer wakeLockId) + { + Intent i = new Intent(); + i.setClass(context, MailService.class); + i.setAction(MailService.ACTION_RESTART_PUSHERS); + addWakeLockId(i, wakeLockId); + if (wakeLockId == null) + { + addWakeLock(context, i); + } + context.startService(i); + } + + public static void actionReschedulePoll(Context context, Integer wakeLockId) + { + Intent i = new Intent(); + i.setClass(context, MailService.class); + i.setAction(MailService.ACTION_RESCHEDULE_POLL); + addWakeLockId(i, wakeLockId); + if (wakeLockId == null) + { + addWakeLock(context, i); + } + context.startService(i); + } + + public static void actionCancel(Context context, Integer wakeLockId) + { + Intent i = new Intent(); + i.setClass(context, MailService.class); + i.setAction(MailService.ACTION_CANCEL); + addWakeLockId(i, wakeLockId); + context.startService(i); + } + + public static void connectivityChange(Context context, Integer wakeLockId) + { + Intent i = new Intent(); + i.setClass(context, MailService.class); + i.setAction(MailService.CONNECTIVITY_CHANGE); + addWakeLockId(i, wakeLockId); + context.startService(i); + } + + @Override + public void onCreate() + { + super.onCreate(); + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: onCreate"); + } + + @Override + public void startService(Intent intent, int startId) + { + Integer startIdObj = startId; + long startTime = System.currentTimeMillis(); + try + { + ConnectivityManager connectivityManager = (ConnectivityManager)getApplication().getSystemService(Context.CONNECTIVITY_SERVICE); + boolean doBackground = true; + boolean hasConnectivity = false; + + if (connectivityManager != null) + { + NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); + if (netInfo != null) + { + State state = netInfo.getState(); + hasConnectivity = state == State.CONNECTED; + } + boolean backgroundData = connectivityManager.getBackgroundDataSetting(); + boolean autoSync = true; + if (AutoSyncHelper.isAvailable()) + { + autoSync = AutoSyncHelper.getMasterSyncAutomatically(); + + Log.i(K9.LOG_TAG, "AutoSync help is available, autoSync = " + autoSync); + } + + K9.BACKGROUND_OPS bOps = K9.getBackgroundOps(); + + switch (bOps) + { + case NEVER: + doBackground = false; + break; + case ALWAYS: + doBackground = true; + break; + case WHEN_CHECKED: + doBackground = backgroundData; + break; + case WHEN_CHECKED_AUTO_SYNC: + doBackground = backgroundData & autoSync; + break; + } + + } + + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "MailService.onStart(" + intent + ", " + startId + + "), hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground); + + // MessagingController.getInstance(getApplication()).addListener(mListener); + if (ACTION_CHECK_MAIL.equals(intent.getAction())) + { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "***** MailService *****: checking mail"); + + if (hasConnectivity && doBackground) + { + PollService.startService(this); + } + reschedulePoll(hasConnectivity, doBackground, startIdObj, false); + startIdObj = null; + } + else if (ACTION_CANCEL.equals(intent.getAction())) + { + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: cancel"); + + cancel(); + } + else if (ACTION_RESET.equals(intent.getAction())) + { + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: reschedule"); + + rescheduleAll(hasConnectivity, doBackground, startIdObj); + startIdObj = null; + + } + else if (ACTION_RESTART_PUSHERS.equals(intent.getAction())) + { + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: restarting pushers"); + reschedulePushers(hasConnectivity, doBackground, startIdObj); + startIdObj = null; + + } + else if (ACTION_RESCHEDULE_POLL.equals(intent.getAction())) + { + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: rescheduling poll"); + reschedulePoll(hasConnectivity, doBackground, startIdObj, true); + startIdObj = null; + + } + else if (ACTION_REFRESH_PUSHERS.equals(intent.getAction())) + { + if (hasConnectivity && doBackground) + { + refreshPushers(null); + schedulePushers(startIdObj); + startIdObj = null; + } + } + else if (CONNECTIVITY_CHANGE.equals(intent.getAction())) + { + notifyConnectionStatus(hasConnectivity); + rescheduleAll(hasConnectivity, doBackground, startIdObj); + startIdObj = null; + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Got connectivity action with hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground); + } + else if (CANCEL_CONNECTIVITY_NOTICE.equals(intent.getAction())) + { + notifyConnectionStatus(true); + } + } + finally + { + if (startIdObj != null) + { + stopSelf(startId); + } + } + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "MailService.onStart took " + (System.currentTimeMillis() - startTime) + "ms"); + } + + private void rescheduleAll(final boolean hasConnectivity, final boolean doBackground, final Integer startId) + { + reschedulePoll(hasConnectivity, doBackground, null, true); + reschedulePushers(hasConnectivity, doBackground, startId); + + } + + private void notifyConnectionStatus(boolean hasConnectivity) + { + if (true) return; + NotificationManager notifMgr = + (NotificationManager)getApplication().getSystemService(Context.NOTIFICATION_SERVICE); + if (hasConnectivity == false) + { + String notice = getApplication().getString(R.string.no_connection_alert); + String header = getApplication().getString(R.string.alert_header); + + + Notification notif = new Notification(R.drawable.stat_notify_email_generic, + header, System.currentTimeMillis()); + + Intent i = new Intent(); + i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); + i.setAction(MailService.CANCEL_CONNECTIVITY_NOTICE); + + PendingIntent pi = PendingIntent.getService(this, 0, i, 0); + + notif.setLatestEventInfo(getApplication(), header, notice, pi); + notif.flags = Notification.FLAG_ONGOING_EVENT; + + notifMgr.notify(K9.CONNECTIVITY_ID, notif); + } + else + { + notifMgr.cancel(K9.CONNECTIVITY_ID); + } + } + + @Override + public void onDestroy() + { + if (K9.DEBUG) + Log.v(K9.LOG_TAG, "***** MailService *****: onDestroy()"); + super.onDestroy(); + // MessagingController.getInstance(getApplication()).removeListener(mListener); + } + + private void cancel() + { + Intent i = new Intent(); + i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); + i.setAction(ACTION_CHECK_MAIL); + BootReceiver.cancelIntent(this, i); + } + + private final static String PREVIOUS_INTERVAL = "MailService.previousInterval"; + private final static String LAST_CHECK_END = "MailService.lastCheckEnd"; + + public static void saveLastCheckEnd(Context context) + { + + long lastCheckEnd = System.currentTimeMillis(); + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Saving lastCheckEnd = " + new Date(lastCheckEnd)); + Preferences prefs = Preferences.getPreferences(context); + SharedPreferences sPrefs = prefs.getPreferences(); + SharedPreferences.Editor editor = sPrefs.edit(); + editor.putLong(LAST_CHECK_END, lastCheckEnd); + editor.commit(); + } + + private void reschedulePoll(final boolean hasConnectivity, final boolean doBackground, Integer startId, final boolean considerLastCheckEnd) + { + if (hasConnectivity && doBackground) + { + execute(getApplication(), new Runnable() + { + public void run() + { + int shortestInterval = -1; + + Preferences prefs = Preferences.getPreferences(MailService.this); + SharedPreferences sPrefs = prefs.getPreferences(); + int previousInterval = sPrefs.getInt(PREVIOUS_INTERVAL, -1); + long lastCheckEnd = sPrefs.getLong(LAST_CHECK_END, -1); + for (Account account : prefs.getAccounts()) + { + if (account.getAutomaticCheckIntervalMinutes() != -1 + && account.getFolderSyncMode() != FolderMode.NONE + && (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1)) + { + shortestInterval = account.getAutomaticCheckIntervalMinutes(); + } + } + SharedPreferences.Editor editor = sPrefs.edit(); + editor.putInt(PREVIOUS_INTERVAL, shortestInterval); + editor.commit(); + + if (shortestInterval == -1) + { + nextCheck = -1; + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "No next check scheduled for package " + getApplication().getPackageName()); + cancel(); + } + else + { + long delay = (shortestInterval * (60 * 1000)); + long base = (previousInterval == -1 || lastCheckEnd == -1 || considerLastCheckEnd == false ? System.currentTimeMillis() : lastCheckEnd); + long nextTime = base + delay; + if (K9.DEBUG) + Log.i(K9.LOG_TAG, + "previousInterval = " + previousInterval + + ", shortestInterval = " + shortestInterval + + ", lastCheckEnd = " + new Date(lastCheckEnd) + + ", considerLastCheckEnd = " + considerLastCheckEnd); + nextCheck = nextTime; + try + { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Next check for package " + getApplication().getPackageName() + " scheduled for " + new Date(nextTime)); + } + catch (Exception e) + { + // I once got a NullPointerException deep in new Date(); + Log.e(K9.LOG_TAG, "Exception while logging", e); + } + + Intent i = new Intent(); + i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); + i.setAction(ACTION_CHECK_MAIL); + BootReceiver.scheduleIntent(MailService.this, nextTime, i); + + } + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); + } + else + { + nextCheck = -1; + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "No connectivity, canceling check for " + getApplication().getPackageName()); + cancel(); + } + } + + private void stopPushers(final Integer startId) + { + execute(getApplication(), new Runnable() + { + public void run() + { + MessagingController.getInstance(getApplication()).stopAllPushing(); + PushService.stopService(MailService.this); + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); + } + + private void reschedulePushers(final boolean hasConnectivity, final boolean doBackground, final Integer startId) + { + execute(getApplication(), new Runnable() + { + public void run() + { + + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Rescheduling pushers"); + stopPushers(null); + if (hasConnectivity && doBackground) + { + setupPushers(null); + schedulePushers(startId); + } + + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, null); + } + + private void setupPushers(final Integer startId) + { + execute(getApplication(), new Runnable() + { + public void run() + { + boolean pushing = false; + for (Account account : Preferences.getPreferences(MailService.this).getAccounts()) + { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Setting up pushers for account " + account.getDescription()); + pushing |= MessagingController.getInstance(getApplication()).setupPushing(account); + } + if (pushing) + { + PushService.startService(MailService.this); + } + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); + } + + private void refreshPushers(final Integer startId) + { + execute(getApplication(), new Runnable() + { + public void run() + { + try + { + long nowTime = System.currentTimeMillis(); + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "Refreshing pushers"); + Collection pushers = MessagingController.getInstance(getApplication()).getPushers(); + for (Pusher pusher : pushers) + { + long lastRefresh = pusher.getLastRefresh(); + int refreshInterval = pusher.getRefreshInterval(); + long sinceLast = nowTime - lastRefresh; + if (sinceLast + 10000 > refreshInterval) // Add 10 seconds to keep pushers in sync, avoid drift + { + if (K9.DEBUG) + { + Log.d(K9.LOG_TAG, "PUSHREFRESH: refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval + + ", nowTime = " + nowTime + ", sinceLast = " + sinceLast); + } + pusher.refresh(); + pusher.setLastRefresh(nowTime); + } + else + { + if (K9.DEBUG) + { + Log.d(K9.LOG_TAG, "PUSHREFRESH: NOT refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval + + ", nowTime = " + nowTime + ", sinceLast = " + sinceLast); + } + } + } + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "Exception while refreshing pushers", e); + } + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); + } + + private void schedulePushers(final Integer startId) + { + execute(getApplication(), new Runnable() + { + public void run() + { + int minInterval = -1; + + Collection pushers = MessagingController.getInstance(getApplication()).getPushers(); + for (Pusher pusher : pushers) + { + int interval = pusher.getRefreshInterval(); + if (interval > 0 && (interval < minInterval || minInterval == -1)) + { + minInterval = interval; + } + } + if (K9.DEBUG) + { + Log.v(K9.LOG_TAG, "Pusher refresh interval = " + minInterval); + } + if (minInterval > 0) + { + long nextTime = System.currentTimeMillis() + minInterval; + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "Next pusher refresh scheduled for " + new Date(nextTime)); + Intent i = new Intent(); + i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService"); + i.setAction(ACTION_REFRESH_PUSHERS); + BootReceiver.scheduleIntent(MailService.this, nextTime, i); + } + } + } + , K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId); + } + + + @Override + public IBinder onBind(Intent intent) + { + return null; + } + + public static long getNextPollTime() + { + return nextCheck; + } + + +} diff --git a/src/com/jcraft/jzlib/ZStreamException.java b/src/com/jcraft/jzlib/ZStreamException.java index 424b74b78e..308bb8a1e5 100644 --- a/src/com/jcraft/jzlib/ZStreamException.java +++ b/src/com/jcraft/jzlib/ZStreamException.java @@ -1,44 +1,44 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -public class ZStreamException extends java.io.IOException { - public ZStreamException() { - super(); - } - public ZStreamException(String s) { - super(s); - } -} +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +public class ZStreamException extends java.io.IOException { + public ZStreamException() { + super(); + } + public ZStreamException(String s) { + super(s); + } +} diff --git a/src/org/apache/commons/io/CopyUtils.java b/src/org/apache/commons/io/CopyUtils.java index eab8307e71..22e1270b71 100644 --- a/src/org/apache/commons/io/CopyUtils.java +++ b/src/org/apache/commons/io/CopyUtils.java @@ -1,332 +1,332 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 (InputStream, Reader, - * String and byte[]) and destinations - * (OutputStream, Writer, String and - * byte[]). - *

- * Unless otherwise noted, these copy methods do not - * 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' close() 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 - * this - * UnixReview article. - *

- * For byte-to-char methods, a copy 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. - *

copy 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. - *

- * The copy methods use an internal buffer when copying. It is - * therefore advisable not to deliberately wrap the stream arguments - * to the copy methods in Buffered* streams. For - * example, don't do the following: - *

- *  copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );
- *  
- * The rationale is as follows: - *

- * 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 - * read requests can inexpensively get their data (until the buffer - * runs out). - *

- * However, the copy 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). - *

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

- *       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)
- * 
- *

- * 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. - *

- * 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). - */ -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 byte[] to an OutputStream. - * @param input the byte array to read from - * @param output the OutputStream 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 byte[] to chars on a - * Writer. - * The platform's default encoding is used for the byte-to-char conversion. - * @param input the byte array to read from - * @param output the Writer 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 byte[] to chars on a - * Writer, using the specified encoding. - * @param input the byte array to read from - * @param output the Writer to write to - * @param encoding The name of a supported character encoding. See the - * IANA - * Charset Registry 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 InputStream to an - * OutputStream. - * @param input the InputStream to read from - * @param output the OutputStream 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 Reader to a Writer. - * @param input the Reader to read from - * @param output the Writer 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 InputStream to chars on a - * Writer. - * The platform's default encoding is used for the byte-to-char conversion. - * @param input the InputStream to read from - * @param output the Writer 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 InputStream to chars on a - * Writer, using the specified encoding. - * @param input the InputStream to read from - * @param output the Writer to write to - * @param encoding The name of a supported character encoding. See the - * IANA - * Charset Registry 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 Reader to bytes on an - * OutputStream, and flush the OutputStream. - * @param input the Reader to read from - * @param output the OutputStream 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 String to bytes on an - * OutputStream, and - * flush the OutputStream. - * @param input the String to read from - * @param output the OutputStream 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 String to a Writer. - * @param input the String to read from - * @param output the Writer 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 (InputStream, Reader, + * String and byte[]) and destinations + * (OutputStream, Writer, String and + * byte[]). + *

+ * Unless otherwise noted, these copy methods do not + * 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' close() 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 + * this + * UnixReview article. + *

+ * For byte-to-char methods, a copy 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. + *

copy 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. + *

+ * The copy methods use an internal buffer when copying. It is + * therefore advisable not to deliberately wrap the stream arguments + * to the copy methods in Buffered* streams. For + * example, don't do the following: + *

+ *  copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );
+ *  
+ * The rationale is as follows: + *

+ * 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 + * read requests can inexpensively get their data (until the buffer + * runs out). + *

+ * However, the copy 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). + *

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

+ *       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)
+ * 
+ *

+ * 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. + *

+ * 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). + */ +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 byte[] to an OutputStream. + * @param input the byte array to read from + * @param output the OutputStream 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 byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the byte array to read from + * @param output the Writer 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 byte[] to chars on a + * Writer, using the specified encoding. + * @param input the byte array to read from + * @param output the Writer to write to + * @param encoding The name of a supported character encoding. See the + * IANA + * Charset Registry 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 InputStream to an + * OutputStream. + * @param input the InputStream to read from + * @param output the OutputStream 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 Reader to a Writer. + * @param input the Reader to read from + * @param output the Writer 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 InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the InputStream to read from + * @param output the Writer 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 InputStream to chars on a + * Writer, using the specified encoding. + * @param input the InputStream to read from + * @param output the Writer to write to + * @param encoding The name of a supported character encoding. See the + * IANA + * Charset Registry 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 Reader to bytes on an + * OutputStream, and flush the OutputStream. + * @param input the Reader to read from + * @param output the OutputStream 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 String to bytes on an + * OutputStream, and + * flush the OutputStream. + * @param input the String to read from + * @param output the OutputStream 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 String to a Writer. + * @param input the String to read from + * @param output the Writer 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 9e564ae86c..71a27fbba2 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. - *

- * 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 - * filefilter package. - *

- * The following sections describe: - *

- * - * - *

1. Example Implementation

- * - * There are many possible extensions, for example, to delete all - * files and '.svn' directories, and return a list of deleted files: - *
- *  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);
- *    }
- *  }
- * 
- * - * - *

2. Filter Example

- * - * 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. - *

- * The first option is to visit all directories and files. - * This is achieved via the no-args constructor. - *

- * 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. - *

- * For example, if you wanted all directories which are not hidden - * and files which end in ".txt": - *

- *  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);
- * 
- *

- * The third constructor option is to specify separate filters, one for - * directories and one for files. These are combined internally to form - * the correct FileFilter, something which is very easy to - * get wrong when attempted manually, particularly when trying to - * express constructs like 'any file in directories named docs'. - *

- * For example, if you wanted all directories which are not hidden - * and files which end in ".txt": - *

- *  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"),
- *  );
- * 
- * This is much simpler than the previous example, and is why it is the preferred - * option for filtering. - * - * - *

3. Cancellation

- * - * The DirectoryWalker contains some of the logic required for cancel processing. - * Subclasses must complete the implementation. - *

- * What DirectoryWalker does provide for cancellation is: - *

- *

- * Implementations need to provide: - *

- *

- * Two possible scenarios are envisaged for cancellation: - *

- *

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

3.1 External / Multi-threaded

- * - * This example provides a public cancel() 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 - * - * volatile flag to ensure it will work properly in a multi-threaded environment. - * The flag is returned by the handleIsCancelled() method, which - * will cause the walk to stop immediately. The handleCancelled() - * method will be the next, and last, callback method received once cancellation - * has occurred. - * - *
- *  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
- *    }
- *  }
- * 
- * - * - *

3.2 Internal

- * - * This shows an example of how internal cancellation processing could be implemented. - * Note the decision logic and throwing a {@link CancelException} could be implemented - * in any of the lifecycle methods. - * - *
- *  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
- *    }
- *  }
- * 
- * - * @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 depth. - */ - protected DirectoryWalker() { - this(null, -1); - } - - /** - * Construct an instance with a filter and limit the depth navigated to. - *

- * 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 null 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 deep 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 depth navigated to. - *

- * 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 null 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 deep 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. - *

- * 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. - *

- * 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 handle. - * - * @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 CancelException if it has. - *

- * 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. - *

- * This method should be implemented by those subclasses that want to - * provide a public cancel() method available from another - * thread. The design pattern for the subclass should be as follows: - *

-     *  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
-     *    }
-     *  }
-     * 
- *

- * If this method returns true, then the directory walk is immediately - * cancelled. The next callback method will be {@link #handleCancelled}. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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 CancelException 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 CancelException 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. + *

+ * 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 + * filefilter package. + *

+ * The following sections describe: + *

+ * + * + *

1. Example Implementation

+ * + * There are many possible extensions, for example, to delete all + * files and '.svn' directories, and return a list of deleted files: + *
+ *  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);
+ *    }
+ *  }
+ * 
+ * + * + *

2. Filter Example

+ * + * 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. + *

+ * The first option is to visit all directories and files. + * This is achieved via the no-args constructor. + *

+ * 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. + *

+ * For example, if you wanted all directories which are not hidden + * and files which end in ".txt": + *

+ *  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);
+ * 
+ *

+ * The third constructor option is to specify separate filters, one for + * directories and one for files. These are combined internally to form + * the correct FileFilter, something which is very easy to + * get wrong when attempted manually, particularly when trying to + * express constructs like 'any file in directories named docs'. + *

+ * For example, if you wanted all directories which are not hidden + * and files which end in ".txt": + *

+ *  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"),
+ *  );
+ * 
+ * This is much simpler than the previous example, and is why it is the preferred + * option for filtering. + * + * + *

3. Cancellation

+ * + * The DirectoryWalker contains some of the logic required for cancel processing. + * Subclasses must complete the implementation. + *

+ * What DirectoryWalker does provide for cancellation is: + *

+ *

+ * Implementations need to provide: + *

+ *

+ * Two possible scenarios are envisaged for cancellation: + *

+ *

+ * The following sections provide example implementations for these two different + * scenarios. + * + * + *

3.1 External / Multi-threaded

+ * + * This example provides a public cancel() 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 + * + * volatile flag to ensure it will work properly in a multi-threaded environment. + * The flag is returned by the handleIsCancelled() method, which + * will cause the walk to stop immediately. The handleCancelled() + * method will be the next, and last, callback method received once cancellation + * has occurred. + * + *
+ *  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
+ *    }
+ *  }
+ * 
+ * + * + *

3.2 Internal

+ * + * This shows an example of how internal cancellation processing could be implemented. + * Note the decision logic and throwing a {@link CancelException} could be implemented + * in any of the lifecycle methods. + * + *
+ *  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
+ *    }
+ *  }
+ * 
+ * + * @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 depth. + */ + protected DirectoryWalker() { + this(null, -1); + } + + /** + * Construct an instance with a filter and limit the depth navigated to. + *

+ * 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 null 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 deep 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 depth navigated to. + *

+ * 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 null 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 deep 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. + *

+ * 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. + *

+ * 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 handle. + * + * @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 CancelException if it has. + *

+ * 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. + *

+ * This method should be implemented by those subclasses that want to + * provide a public cancel() method available from another + * thread. The design pattern for the subclass should be as follows: + *

+     *  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
+     *    }
+     *  }
+     * 
+ *

+ * If this method returns true, then the directory walk is immediately + * cancelled. The next callback method will be {@link #handleCancelled}. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 CancelException 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 CancelException 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 810feac04d..e7f1eee99d 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. - *

- * 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. - *

- * Origin of code: Excalibur - * - * @author Peter Donald - * @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. + *

+ * 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. + *

+ * Origin of code: Excalibur + * + * @author Peter Donald + * @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 59c2f41094..ead4f8fad9 100644 --- a/src/org/apache/commons/io/FileCleaner.java +++ b/src/org/apache/commons/io/FileCleaner.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; - -import java.io.File; - -/** - * Keeps track of files awaiting deletion, and deletes them when an associated - * marker object is reclaimed by the garbage collector. - *

- * 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. - *

- * 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} - */ -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)}. - */ - 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)}. - */ - 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)}. - */ - 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)}. - */ - 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()}. - */ - 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. - *

- * 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. - *

- * 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. - *

- * 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()}. - */ - 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. + *

+ * 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. + *

+ * 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} + */ +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)}. + */ + 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)}. + */ + 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)}. + */ + 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)}. + */ + 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()}. + */ + 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. + *

+ * 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. + *

+ * 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. + *

+ * 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()}. + */ + 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 ea976d60a7..7f4a8ee879 100644 --- a/src/org/apache/commons/io/FileCleaningTracker.java +++ b/src/org/apache/commons/io/FileCleaningTracker.java @@ -1,258 +1,258 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * 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. - *

- * 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 Tracker instances being watched. - */ - ReferenceQueue /* Tracker */ q = new ReferenceQueue(); - /** - * Collection of Tracker instances in existence. - */ - final Collection /* Tracker */ trackers = new Vector(); // 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. - *

- * 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. - *

- * 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. - *

- * 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. - */ - 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 { - - /** - * 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 queue) { - super(marker, queue); - this.path = path; - this.deleteStrategy = (deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy); - } - - /** - * Deletes the file associated with this tracker instance. - * - * @return true if the file was deleted successfully; - * false 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. + *

+ * 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. + *

+ * 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 Tracker instances being watched. + */ + ReferenceQueue /* Tracker */ q = new ReferenceQueue(); + /** + * Collection of Tracker instances in existence. + */ + final Collection /* Tracker */ trackers = new Vector(); // 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + */ + 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 { + + /** + * 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 queue) { + super(marker, queue); + this.path = path; + this.deleteStrategy = (deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy); + } + + /** + * Deletes the file associated with this tracker instance. + * + * @return true if the file was deleted successfully; + * false 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 8b6b4b9aad..3518df190e 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. - *

- * 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. - *

- * 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 IOExceptions are caught and false returned instead. - * If the file does not exist or is null, true is returned. - *

- * 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. - *

- * 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. - *

- * This method is designed for subclasses to override. - * The implementation may return either false or an IOException - * 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. - *

- * 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. - *

- * This implementation uses FileUtils.forceDelete() - * if the file exists. - * - * @param fileToDelete the file to delete, not null - * @return Always returns true - * @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. + *

+ * 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. + *

+ * 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 IOExceptions are caught and false returned instead. + * If the file does not exist or is null, true is returned. + *

+ * 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. + *

+ * 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. + *

+ * This method is designed for subclasses to override. + * The implementation may return either false or an IOException + * 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. + *

+ * 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. + *

+ * This implementation uses FileUtils.forceDelete() + * if the file exists. + * + * @param fileToDelete the file to delete, not null + * @return Always returns true + * @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 a29dba4390..4c0957256b 100644 --- a/src/org/apache/commons/io/FileSystemUtils.java +++ b/src/org/apache/commons/io/FileSystemUtils.java @@ -1,457 +1,457 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * This class provides static utility methods for general file system - * functions not provided via the JDK {@link java.io.File File} class. - *

- * The current functions provided are: - *

- * - * @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. - *

- * Note that some OS's are NOT currently supported, including OS/390, - * OpenVMS and and SunOS 5. (SunOS is supported by freeSpaceKb.) - *

-     * FileSystemUtils.freeSpace("C:");       // Windows
-     * FileSystemUtils.freeSpace("/volume");  // *nix
-     * 
- * 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 - */ - 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. - *
-     * FileSystemUtils.freeSpaceKb("C:");       // Windows
-     * FileSystemUtils.freeSpaceKb("/volume");  // *nix
-     * 
- * 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. - *

- * 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. - *

-     * FileSystemUtils.freeSpace("C:");  // Windows
-     * FileSystemUtils.freeSpace("/volume");  // *nix
-     * 
- * 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 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 = (String) 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 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 = (String) 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 = (String) 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 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 lines = new ArrayList(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. + *

+ * This class provides static utility methods for general file system + * functions not provided via the JDK {@link java.io.File File} class. + *

+ * The current functions provided are: + *

    + *
  • Get the free space on a drive + *
+ * + * @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. + *

+ * Note that some OS's are NOT currently supported, including OS/390, + * OpenVMS and and SunOS 5. (SunOS is supported by freeSpaceKb.) + *

+     * FileSystemUtils.freeSpace("C:");       // Windows
+     * FileSystemUtils.freeSpace("/volume");  // *nix
+     * 
+ * 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 + */ + 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. + *
+     * FileSystemUtils.freeSpaceKb("C:");       // Windows
+     * FileSystemUtils.freeSpaceKb("/volume");  // *nix
+     * 
+ * 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. + *

+ * 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. + *

+     * FileSystemUtils.freeSpace("C:");  // Windows
+     * FileSystemUtils.freeSpace("/volume");  // *nix
+     * 
+ * 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 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 = (String) 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 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 = (String) 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 = (String) 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 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 lines = new ArrayList(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 254800cd19..cbc789f80b 100644 --- a/src/org/apache/commons/io/FileUtils.java +++ b/src/org/apache/commons/io/FileUtils.java @@ -1,1890 +1,1890 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.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. - *

- * Facilities are provided in the following areas: - *

    - *
  • writing to a file - *
  • reading from a file - *
  • make a directory including parent directories - *
  • copying files and directories - *
  • deleting files and directories - *
  • converting to and from a URL - *
  • listing files and directories by filter and extension - *
  • comparing file content - *
  • file last changed date - *
  • calculating a checksum - *
- *

- * Origin of code: Excalibur, Alexandria, Commons-Utils - * - * @author Kevin A. Burton - * @author Scott Sanders - * @author Daniel Rall - * @author Christoph.Reck - * @author Peter Donald - * @author Jeff Turner - * @author Matthew Hawthorne - * @author Jeremias Maerki - * @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 $ - */ -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 File. - */ - 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 new FileInputStream(file). - *

- * At the end of the method either the stream will be successfully opened, - * or an exception will have been thrown. - *

- * 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 null - * @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. - *

- * At the end of the method either the stream will be successfully opened, - * or an exception will have been thrown. - *

- * 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 null - * @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. - *

- * 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 files) { - return (File[]) 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 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. - *

- * 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. - *

- * An example: If you want to search through all directories called - * "temp" you pass in FileFilterUtils.NameFileFilter("temp") - *

- * Another common usage of this method is find files in a directory - * tree but ignoring the directories generated CVS. You can simply pass - * in FileFilterUtils.makeCVSAware(null). - * - * @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 null, 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 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 files = new java.util.LinkedList(); - innerListFiles(files, directory, - FileFilterUtils.orFileFilter(effFileFilter, effDirFilter)); - return files; - } - - /** - * Allows iteration over the files in given directory (and optionally - * its subdirectories). - *

- * 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 null, 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 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 null, 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 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 null, 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 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. - *

- * 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. - *

- * 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 URL to a File. - *

- * From version 1.1 this method will decode the URL. - * Syntax such as file:///my%20docs/file.txt will be - * correctly decoded to /my docs/file.txt. - * - * @param url the file URL to convert, null returns null - * @return the equivalent File object, or null - * if the URL's protocol is not file - * @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 URL to a File. - *

- * Returns an array of the same size as the input. - * If the input is null, an empty array is returned. - * If the input contains null, the output array contains null at the same - * index. - *

- * This method will decode the URL. - * Syntax such as file:///my%20docs/file.txt will be - * correctly decoded to /my docs/file.txt. - * - * @param urls the file URLs to convert, null returns empty array - * @return a non-null array of Files matching the input, with a null item - * if there was a null 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 File to a URL. - *

- * 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].toURL(); - } - - return urls; - } - - //----------------------------------------------------------------------- - /** - * Copies a file to a directory preserving the file date. - *

- * 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 null - * @param destDir the directory to place the copy in, must not be null - * - * @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. - *

- * 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 null - * @param destDir the directory to place the copy in, must not be null - * @param preserveFileDate true if the file date of the copy - * should be the same as the original - * - * @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) - * @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. - *

- * 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 null - * @param destFile the new file, must not be null - * - * @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 #copyFileToDirectory(File, File) - */ - public static void copyFile(File srcFile, File destFile) throws IOException { - copyFile(srcFile, destFile, true); - } - - /** - * Copies a file to a new location. - *

- * 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 null - * @param destFile the new file, must not be null - * @param preserveFileDate true if the file date of the copy - * should be the same as the original - * - * @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 #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 null - * @param destFile the validated destination file, must not be null - * @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. - *

- * This method copies the source directory and all its contents to a - * directory of the same name in the specified destination directory. - *

- * 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 null - * @param destDir the directory to place the copy in, must not be null - * - * @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 - * @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. - *

- * 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. - *

- * 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 null - * @param destDir the new directory, must not be null - * - * @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 - * @since Commons IO 1.1 - */ - public static void copyDirectory(File srcDir, File destDir) throws IOException { - copyDirectory(srcDir, destDir, true); - } - - /** - * Copies a whole directory to a new location. - *

- * This method copies the contents of the specified source directory - * to within the specified destination directory. - *

- * 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 null - * @param destDir the new directory, must not be null - * @param preserveFileDate true if the file date of the copy - * should be the same as the original - * - * @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 - * @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. - *

- * This method copies the contents of the specified source directory - * to within the specified destination directory. - *

- * 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. - * - *

Example: Copy directories only

- *
-     *  // only copy the directory structure
-     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
-     *  
- * - *

Example: Copy directories and txt files

- *
-     *  // 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);
-     *  
- * - * @param srcDir an existing directory to copy, must not be null - * @param destDir the new directory, must not be null - * @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 null - * @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. - *

- * This method copies the contents of the specified source directory - * to within the specified destination directory. - *

- * 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. - * - *

Example: Copy directories only

- *
-     *  // only copy the directory structure
-     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
-     *  
- * - *

Example: Copy directories and txt files

- *
-     *  // 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);
-     *  
- * - * @param srcDir an existing directory to copy, must not be null - * @param destDir the new directory, must not be null - * @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 null - * @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 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(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 null - * @param destDir the validated destination directory, must not be null - * @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 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 source to a file - * destination. The directories up to destination - * will be created if they don't already exist. destination - * will be overwritten if it already exists. - * - * @param source the URL to copy bytes from, must not be null - * @param destination the non-directory File to write bytes to - * (possibly overwriting), must not be null - * @throws IOException if source URL cannot be opened - * @throws IOException if destination is a directory - * @throws IOException if destination cannot be written - * @throws IOException if destination 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. - *

- * The difference between File.delete() and this method are: - *

    - *
  • A directory to be deleted does not have to be empty.
  • - *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • - *
- * - * @param file file or directory to delete, can be null - * @return true if the file or directory was deleted, otherwise - * false - * - * @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. - *

- * 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 null - * @param seconds the maximum time in seconds to wait - * @return true if file exists - * @throws NullPointerException if the file is null - */ - 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 null - * @param encoding the encoding to use, null means platform default - * @return the file contents, never null - * @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 null - * @return the file contents, never null - * @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 null - * @return the file contents, never null - * @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 null - * @param encoding the encoding to use, null means platform default - * @return the list of Strings representing each line in the file, never null - * @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 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 null - * @return the list of Strings representing each line in the file, never null - * @throws IOException in case of an I/O error - * @since Commons IO 1.3 - */ - public static List readLines(File file) throws IOException { - return readLines(file, null); - } - - /** - * Returns an Iterator for the lines in a File. - *

- * This method opens an InputStream 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. - *

- * The recommended usage pattern is: - *

-     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
-     * try {
-     *   while (it.hasNext()) {
-     *     String line = it.nextLine();
-     *     /// do something with line
-     *   }
-     * } finally {
-     *   LineIterator.closeQuietly(iterator);
-     * }
-     * 
- *

- * 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 null - * @param encoding the encoding to use, null means platform default - * @return an Iterator of the lines in the file, never null - * @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 File using the default encoding for the VM. - * - * @param file the file to open for input, must not be null - * @return an Iterator of the lines in the file, never null - * @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, null 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. - *

- * 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 toString() value of each item in a collection to - * the specified File line by line. - * The specified character encoding and the default line ending will be used. - *

- * 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, null means platform default - * @param lines the lines to write, null 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 lines) throws IOException { - writeLines(file, encoding, lines, null); - } - - /** - * Writes the toString() value of each item in a collection to - * the specified File 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, null 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 lines) throws IOException { - writeLines(file, null, lines, null); - } - - /** - * Writes the toString() value of each item in a collection to - * the specified File line by line. - * The specified character encoding and the line ending will be used. - *

- * 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, null means platform default - * @param lines the lines to write, null entries produce blank lines - * @param lineEnding the line separator to use, null 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 lines, String lineEnding) throws IOException { - OutputStream out = null; - try { - out = openOutputStream(file); - IOUtils.writeLines(lines, lineEnding, out, encoding); - } finally { - IOUtils.closeQuietly(out); - } - } - - /** - * Writes the toString() value of each item in a collection to - * the specified File 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, null entries produce blank lines - * @param lineEnding the line separator to use, null is system default - * @throws IOException in case of an I/O error - * @since Commons IO 1.3 - */ - public static void writeLines(File file, Collection lines, String lineEnding) throws IOException { - writeLines(file, null, lines, lineEnding); - } - - //----------------------------------------------------------------------- - /** - * Deletes a file. If file is a directory, delete it and all sub-directories. - *

- * The difference between File.delete() and this method are: - *

    - *
  • A directory to be deleted does not have to be empty.
  • - *
  • You get exceptions when a file or directory cannot be deleted. - * (java.io.File methods returns a boolean)
  • - *
- * - * @param file file or directory to delete, must not be null - * @throws NullPointerException if the directory is null - * @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 null - * @throws NullPointerException if the file is null - * @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 null - * @throws NullPointerException if the directory is null - * @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 null - * @throws NullPointerException if the directory is null - * @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 null - * @throws NullPointerException if the directory is null - * @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 null - * @return size of directory in bytes, 0 if directory is security restricted - * @throws NullPointerException if the directory is null - */ - 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 File is newer than the reference - * File. - * - * @param file the File of which the modification date must - * be compared, must not be null - * @param reference the File of which the modification date - * is used, must not be null - * @return true if the File exists and has been modified more - * recently than the reference File - * @throws IllegalArgumentException if the file is null - * @throws IllegalArgumentException if the reference file is null 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 File is newer than the specified - * Date. - * - * @param file the File of which the modification date - * must be compared, must not be null - * @param date the date reference, must not be null - * @return true if the File exists and has been modified - * after the given Date. - * @throws IllegalArgumentException if the file is null - * @throws IllegalArgumentException if the date is null - */ - 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 File is newer than the specified - * time reference. - * - * @param file the File of which the modification date must - * be compared, must not be null - * @param timeMillis the time reference measured in milliseconds since the - * epoch (00:00:00 GMT, January 1, 1970) - * @return true if the File exists and has been modified after - * the given time reference. - * @throws IllegalArgumentException if the file is null - */ - 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 File is older than the reference - * File. - * - * @param file the File of which the modification date must - * be compared, must not be null - * @param reference the File of which the modification date - * is used, must not be null - * @return true if the File exists and has been modified before - * the reference File - * @throws IllegalArgumentException if the file is null - * @throws IllegalArgumentException if the reference file is null 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 File is older than the specified - * Date. - * - * @param file the File of which the modification date - * must be compared, must not be null - * @param date the date reference, must not be null - * @return true if the File exists and has been modified - * before the given Date. - * @throws IllegalArgumentException if the file is null - * @throws IllegalArgumentException if the date is null - */ - 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 File is older than the specified - * time reference. - * - * @param file the File of which the modification date must - * be compared, must not be null - * @param timeMillis the time reference measured in milliseconds since the - * epoch (00:00:00 GMT, January 1, 1970) - * @return true if the File exists and has been modified before - * the given time reference. - * @throws IllegalArgumentException if the file is null - */ - 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 null - * @return the checksum value - * @throws NullPointerException if the file or checksum is null - * @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 Checksum instance - * if desired simply by reusing the same checksum object. - * For example: - *
-     *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
-     * 
- * - * @param file the file to checksum, must not be null - * @param checksum the checksum object to be used, must not be null - * @return the checksum specified, updated with the content of the file - * @throws NullPointerException if the file or checksum is null - * @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. - *

- * 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 null - * @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 true create the destination directory, - * otherwise if false throw an IOException - * @throws NullPointerException if source or destination is null - * @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. - *

- * 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 null - * @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 true create the destination directory, - * otherwise if false throw an IOException - * @throws NullPointerException if source or destination is null - * @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. - *

- * 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 true create the destination directory, - * otherwise if false throw an IOException - * @throws NullPointerException if source or destination is null - * @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 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. + *

+ * Facilities are provided in the following areas: + *

    + *
  • writing to a file + *
  • reading from a file + *
  • make a directory including parent directories + *
  • copying files and directories + *
  • deleting files and directories + *
  • converting to and from a URL + *
  • listing files and directories by filter and extension + *
  • comparing file content + *
  • file last changed date + *
  • calculating a checksum + *
+ *

+ * Origin of code: Excalibur, Alexandria, Commons-Utils + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * @author Matthew Hawthorne + * @author Jeremias Maerki + * @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 $ + */ +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 File. + */ + 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 new FileInputStream(file). + *

+ * At the end of the method either the stream will be successfully opened, + * or an exception will have been thrown. + *

+ * 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 null + * @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. + *

+ * At the end of the method either the stream will be successfully opened, + * or an exception will have been thrown. + *

+ * 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 null + * @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. + *

+ * 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 files) { + return (File[]) 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 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. + *

+ * 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. + *

+ * An example: If you want to search through all directories called + * "temp" you pass in FileFilterUtils.NameFileFilter("temp") + *

+ * Another common usage of this method is find files in a directory + * tree but ignoring the directories generated CVS. You can simply pass + * in FileFilterUtils.makeCVSAware(null). + * + * @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 null, 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 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 files = new java.util.LinkedList(); + innerListFiles(files, directory, + FileFilterUtils.orFileFilter(effFileFilter, effDirFilter)); + return files; + } + + /** + * Allows iteration over the files in given directory (and optionally + * its subdirectories). + *

+ * 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 null, 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 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 null, 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 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 null, 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 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. + *

+ * 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. + *

+ * 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 URL to a File. + *

+ * From version 1.1 this method will decode the URL. + * Syntax such as file:///my%20docs/file.txt will be + * correctly decoded to /my docs/file.txt. + * + * @param url the file URL to convert, null returns null + * @return the equivalent File object, or null + * if the URL's protocol is not file + * @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 URL to a File. + *

+ * Returns an array of the same size as the input. + * If the input is null, an empty array is returned. + * If the input contains null, the output array contains null at the same + * index. + *

+ * This method will decode the URL. + * Syntax such as file:///my%20docs/file.txt will be + * correctly decoded to /my docs/file.txt. + * + * @param urls the file URLs to convert, null returns empty array + * @return a non-null array of Files matching the input, with a null item + * if there was a null 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 File to a URL. + *

+ * 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].toURL(); + } + + return urls; + } + + //----------------------------------------------------------------------- + /** + * Copies a file to a directory preserving the file date. + *

+ * 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 null + * @param destDir the directory to place the copy in, must not be null + * + * @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. + *

+ * 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 null + * @param destDir the directory to place the copy in, must not be null + * @param preserveFileDate true if the file date of the copy + * should be the same as the original + * + * @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) + * @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. + *

+ * 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 null + * @param destFile the new file, must not be null + * + * @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 #copyFileToDirectory(File, File) + */ + public static void copyFile(File srcFile, File destFile) throws IOException { + copyFile(srcFile, destFile, true); + } + + /** + * Copies a file to a new location. + *

+ * 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 null + * @param destFile the new file, must not be null + * @param preserveFileDate true if the file date of the copy + * should be the same as the original + * + * @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 #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 null + * @param destFile the validated destination file, must not be null + * @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. + *

+ * This method copies the source directory and all its contents to a + * directory of the same name in the specified destination directory. + *

+ * 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 null + * @param destDir the directory to place the copy in, must not be null + * + * @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 + * @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. + *

+ * 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. + *

+ * 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 null + * @param destDir the new directory, must not be null + * + * @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 + * @since Commons IO 1.1 + */ + public static void copyDirectory(File srcDir, File destDir) throws IOException { + copyDirectory(srcDir, destDir, true); + } + + /** + * Copies a whole directory to a new location. + *

+ * This method copies the contents of the specified source directory + * to within the specified destination directory. + *

+ * 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 null + * @param destDir the new directory, must not be null + * @param preserveFileDate true if the file date of the copy + * should be the same as the original + * + * @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 + * @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. + *

+ * This method copies the contents of the specified source directory + * to within the specified destination directory. + *

+ * 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. + * + *

Example: Copy directories only

+ *
+     *  // only copy the directory structure
+     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
+     *  
+ * + *

Example: Copy directories and txt files

+ *
+     *  // 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);
+     *  
+ * + * @param srcDir an existing directory to copy, must not be null + * @param destDir the new directory, must not be null + * @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 null + * @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. + *

+ * This method copies the contents of the specified source directory + * to within the specified destination directory. + *

+ * 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. + * + *

Example: Copy directories only

+ *
+     *  // only copy the directory structure
+     *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
+     *  
+ * + *

Example: Copy directories and txt files

+ *
+     *  // 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);
+     *  
+ * + * @param srcDir an existing directory to copy, must not be null + * @param destDir the new directory, must not be null + * @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 null + * @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 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(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 null + * @param destDir the validated destination directory, must not be null + * @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 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 source to a file + * destination. The directories up to destination + * will be created if they don't already exist. destination + * will be overwritten if it already exists. + * + * @param source the URL to copy bytes from, must not be null + * @param destination the non-directory File to write bytes to + * (possibly overwriting), must not be null + * @throws IOException if source URL cannot be opened + * @throws IOException if destination is a directory + * @throws IOException if destination cannot be written + * @throws IOException if destination 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. + *

+ * The difference between File.delete() and this method are: + *

    + *
  • A directory to be deleted does not have to be empty.
  • + *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • + *
+ * + * @param file file or directory to delete, can be null + * @return true if the file or directory was deleted, otherwise + * false + * + * @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. + *

+ * 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 null + * @param seconds the maximum time in seconds to wait + * @return true if file exists + * @throws NullPointerException if the file is null + */ + 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 null + * @param encoding the encoding to use, null means platform default + * @return the file contents, never null + * @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 null + * @return the file contents, never null + * @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 null + * @return the file contents, never null + * @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 null + * @param encoding the encoding to use, null means platform default + * @return the list of Strings representing each line in the file, never null + * @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 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 null + * @return the list of Strings representing each line in the file, never null + * @throws IOException in case of an I/O error + * @since Commons IO 1.3 + */ + public static List readLines(File file) throws IOException { + return readLines(file, null); + } + + /** + * Returns an Iterator for the lines in a File. + *

+ * This method opens an InputStream 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. + *

+ * The recommended usage pattern is: + *

+     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
+     * try {
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   LineIterator.closeQuietly(iterator);
+     * }
+     * 
+ *

+ * 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 null + * @param encoding the encoding to use, null means platform default + * @return an Iterator of the lines in the file, never null + * @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 File using the default encoding for the VM. + * + * @param file the file to open for input, must not be null + * @return an Iterator of the lines in the file, never null + * @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, null 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. + *

+ * 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 toString() value of each item in a collection to + * the specified File line by line. + * The specified character encoding and the default line ending will be used. + *

+ * 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, null means platform default + * @param lines the lines to write, null 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 lines) throws IOException { + writeLines(file, encoding, lines, null); + } + + /** + * Writes the toString() value of each item in a collection to + * the specified File 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, null 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 lines) throws IOException { + writeLines(file, null, lines, null); + } + + /** + * Writes the toString() value of each item in a collection to + * the specified File line by line. + * The specified character encoding and the line ending will be used. + *

+ * 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, null means platform default + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null 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 lines, String lineEnding) throws IOException { + OutputStream out = null; + try { + out = openOutputStream(file); + IOUtils.writeLines(lines, lineEnding, out, encoding); + } finally { + IOUtils.closeQuietly(out); + } + } + + /** + * Writes the toString() value of each item in a collection to + * the specified File 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, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @throws IOException in case of an I/O error + * @since Commons IO 1.3 + */ + public static void writeLines(File file, Collection lines, String lineEnding) throws IOException { + writeLines(file, null, lines, lineEnding); + } + + //----------------------------------------------------------------------- + /** + * Deletes a file. If file is a directory, delete it and all sub-directories. + *

+ * The difference between File.delete() and this method are: + *

    + *
  • A directory to be deleted does not have to be empty.
  • + *
  • You get exceptions when a file or directory cannot be deleted. + * (java.io.File methods returns a boolean)
  • + *
+ * + * @param file file or directory to delete, must not be null + * @throws NullPointerException if the directory is null + * @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 null + * @throws NullPointerException if the file is null + * @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 null + * @throws NullPointerException if the directory is null + * @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 null + * @throws NullPointerException if the directory is null + * @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 null + * @throws NullPointerException if the directory is null + * @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 null + * @return size of directory in bytes, 0 if directory is security restricted + * @throws NullPointerException if the directory is null + */ + 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 File is newer than the reference + * File. + * + * @param file the File of which the modification date must + * be compared, must not be null + * @param reference the File of which the modification date + * is used, must not be null + * @return true if the File exists and has been modified more + * recently than the reference File + * @throws IllegalArgumentException if the file is null + * @throws IllegalArgumentException if the reference file is null 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 File is newer than the specified + * Date. + * + * @param file the File of which the modification date + * must be compared, must not be null + * @param date the date reference, must not be null + * @return true if the File exists and has been modified + * after the given Date. + * @throws IllegalArgumentException if the file is null + * @throws IllegalArgumentException if the date is null + */ + 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 File is newer than the specified + * time reference. + * + * @param file the File of which the modification date must + * be compared, must not be null + * @param timeMillis the time reference measured in milliseconds since the + * epoch (00:00:00 GMT, January 1, 1970) + * @return true if the File exists and has been modified after + * the given time reference. + * @throws IllegalArgumentException if the file is null + */ + 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 File is older than the reference + * File. + * + * @param file the File of which the modification date must + * be compared, must not be null + * @param reference the File of which the modification date + * is used, must not be null + * @return true if the File exists and has been modified before + * the reference File + * @throws IllegalArgumentException if the file is null + * @throws IllegalArgumentException if the reference file is null 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 File is older than the specified + * Date. + * + * @param file the File of which the modification date + * must be compared, must not be null + * @param date the date reference, must not be null + * @return true if the File exists and has been modified + * before the given Date. + * @throws IllegalArgumentException if the file is null + * @throws IllegalArgumentException if the date is null + */ + 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 File is older than the specified + * time reference. + * + * @param file the File of which the modification date must + * be compared, must not be null + * @param timeMillis the time reference measured in milliseconds since the + * epoch (00:00:00 GMT, January 1, 1970) + * @return true if the File exists and has been modified before + * the given time reference. + * @throws IllegalArgumentException if the file is null + */ + 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 null + * @return the checksum value + * @throws NullPointerException if the file or checksum is null + * @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 Checksum instance + * if desired simply by reusing the same checksum object. + * For example: + *
+     *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
+     * 
+ * + * @param file the file to checksum, must not be null + * @param checksum the checksum object to be used, must not be null + * @return the checksum specified, updated with the content of the file + * @throws NullPointerException if the file or checksum is null + * @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. + *

+ * 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 null + * @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 true create the destination directory, + * otherwise if false throw an IOException + * @throws NullPointerException if source or destination is null + * @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. + *

+ * 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 null + * @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 true create the destination directory, + * otherwise if false throw an IOException + * @throws NullPointerException if source or destination is null + * @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. + *

+ * 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 true create the destination directory, + * otherwise if false throw an IOException + * @throws NullPointerException if source or destination is null + * @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 8e170b1470..2f16ad0b25 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. - *

- * 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. - *

- * NOTE: 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)}. - *

- * 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. - *

- * Most methods recognise both separators (forward and back), and both - * sets of prefixes. See the javadoc of each method for details. - *

- * This class defines six components within a filename - * (example C:\dev\project\file.txt): - *

    - *
  • the prefix - C:\
  • - *
  • the path - dev\project\
  • - *
  • the full path - C:\dev\project\
  • - *
  • the name - file.txt
  • - *
  • the base name - file
  • - *
  • the extension - txt
  • - *
- * 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. - *

- * This class only supports Unix and Windows style names. - * Prefixes are matched as follows: - *

- * 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)
- * 
- * Both prefix styles are matched always, irrespective of the machine that you are - * currently running on. - *

- * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils. - * - * @author Kevin A. Burton - * @author Scott Sanders - * @author Daniel Rall - * @author Christoph.Reck - * @author Peter Donald - * @author Jeff Turner - * @author Matthew Hawthorne - * @author Martin Cooper - * @author Jeremias Maerki - * @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. - *

- * 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. - *

- * 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, null - * is returned. - *

- * The output will be the same on both Unix and Windows except - * for the separator character. - *

-     * /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
-     * 
- * (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. - *

- * 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. - *

- * 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, null - * is returned. - *

- * The output will be the same on both Unix and Windows except - * for the separator character. - *

-     * /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
-     * 
- * (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. - *

- * The effect is equivalent to resultant directory after changing - * directory to the first argument, followed by changing directory to - * the second argument. - *

- * 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 .. is handled. - *

- * If pathToAdd is absolute (has an absolute prefix), then - * it will be normalized and returned. - * Otherwise, the paths will be joined, normalized and returned. - *

- * The output will be the same on both Unix and Windows except - * for the separator character. - *

-     * /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 (!)
-     * 
- * (*) 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 C:/ or ~/. - *

- * This method will handle a file in either Unix or Windows format. - *

- * 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. - *

-     * 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)
-     * 
- *

- * 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. - *

- * This method will handle a file in either Unix or Windows format. - * The position of the last forward or backslash is returned. - *

- * 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. - *

- * 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. - *

- * 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 C:/ - * or ~/. - *

- * This method will handle a file in either Unix or Windows format. - * The prefix includes the first slash in the full filename where applicable. - *

-     * 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)
-     * 
- *

- * 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. - *

- * 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. - *

-     * 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/
-     * 
- *

- * The output will be the same irrespective of the machine that the code is running on. - *

- * 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. - *

- * 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. - *

-     * 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
-     * 
- *

- * The output will be the same irrespective of the machine that the code is running on. - *

- * 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. - *

- * 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. - *

-     * 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/
-     * 
- *

- * 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. - *

- * 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. - *

-     * 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
-     * 
- *

- * 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. - *

- * This method will handle a file in either Unix or Windows format. - * The text after the last forward or backslash is returned. - *

-     * a/b/c.txt --> c.txt
-     * a.txt     --> a.txt
-     * a/b/c     --> c
-     * a/b/c/    --> ""
-     * 
- *

- * 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. - *

- * 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. - *

-     * a/b/c.txt --> c
-     * a.txt     --> a
-     * a/b/c     --> c
-     * a/b/c/    --> ""
-     * 
- *

- * 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. - *

- * This method returns the textual part of the filename after the last dot. - * There must be no directory separator after the dot. - *

-     * foo.txt      --> "txt"
-     * a/b/c.jpg    --> "jpg"
-     * a/b.txt/c    --> ""
-     * a/b/c        --> ""
-     * 
- *

- * 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. - *

- * This method returns the textual part of the filename before the last dot. - * There must be no directory separator after the dot. - *

-     * foo.txt    --> foo
-     * a\b\c.jpg  --> a\b\c
-     * a\b\c      --> a\b\c
-     * a.b\c      --> a.b\c
-     * 
- *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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 extensions) { - if (filename == null) { - return false; - } - if (extensions == null || extensions.isEmpty()) { - return (indexOfExtension(filename) == -1); - } - String fileExt = getExtension(filename); - for (Iterator 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. - *

- * 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. - *

-     * 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
-     * 
- * - * @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. - *

- * 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. - *

-     * 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
-     * 
- * - * @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. - *

- * 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 backtrack = new Stack(); - - // loop around a backtrack stack, to handle complex * matching - do { - if (backtrack.size() > 0) { - int[] array = (int[]) 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 list = new ArrayList(); - 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 (String[]) 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. + *

+ * 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. + *

+ * NOTE: 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)}. + *

+ * 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. + *

+ * Most methods recognise both separators (forward and back), and both + * sets of prefixes. See the javadoc of each method for details. + *

+ * This class defines six components within a filename + * (example C:\dev\project\file.txt): + *

    + *
  • the prefix - C:\
  • + *
  • the path - dev\project\
  • + *
  • the full path - C:\dev\project\
  • + *
  • the name - file.txt
  • + *
  • the base name - file
  • + *
  • the extension - txt
  • + *
+ * 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. + *

+ * This class only supports Unix and Windows style names. + * Prefixes are matched as follows: + *

+ * 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)
+ * 
+ * Both prefix styles are matched always, irrespective of the machine that you are + * currently running on. + *

+ * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * @author Matthew Hawthorne + * @author Martin Cooper + * @author Jeremias Maerki + * @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. + *

+ * 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. + *

+ * 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, null + * is returned. + *

+ * The output will be the same on both Unix and Windows except + * for the separator character. + *

+     * /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
+     * 
+ * (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. + *

+ * 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. + *

+ * 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, null + * is returned. + *

+ * The output will be the same on both Unix and Windows except + * for the separator character. + *

+     * /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
+     * 
+ * (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. + *

+ * The effect is equivalent to resultant directory after changing + * directory to the first argument, followed by changing directory to + * the second argument. + *

+ * 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 .. is handled. + *

+ * If pathToAdd is absolute (has an absolute prefix), then + * it will be normalized and returned. + * Otherwise, the paths will be joined, normalized and returned. + *

+ * The output will be the same on both Unix and Windows except + * for the separator character. + *

+     * /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 (!)
+     * 
+ * (*) 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 C:/ or ~/. + *

+ * This method will handle a file in either Unix or Windows format. + *

+ * 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. + *

+     * 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)
+     * 
+ *

+ * 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. + *

+ * This method will handle a file in either Unix or Windows format. + * The position of the last forward or backslash is returned. + *

+ * 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. + *

+ * 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. + *

+ * 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 C:/ + * or ~/. + *

+ * This method will handle a file in either Unix or Windows format. + * The prefix includes the first slash in the full filename where applicable. + *

+     * 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)
+     * 
+ *

+ * 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. + *

+ * 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. + *

+     * 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/
+     * 
+ *

+ * The output will be the same irrespective of the machine that the code is running on. + *

+ * 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. + *

+ * 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. + *

+     * 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
+     * 
+ *

+ * The output will be the same irrespective of the machine that the code is running on. + *

+ * 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. + *

+ * 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. + *

+     * 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/
+     * 
+ *

+ * 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. + *

+ * 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. + *

+     * 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
+     * 
+ *

+ * 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. + *

+ * This method will handle a file in either Unix or Windows format. + * The text after the last forward or backslash is returned. + *

+     * a/b/c.txt --> c.txt
+     * a.txt     --> a.txt
+     * a/b/c     --> c
+     * a/b/c/    --> ""
+     * 
+ *

+ * 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. + *

+ * 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. + *

+     * a/b/c.txt --> c
+     * a.txt     --> a
+     * a/b/c     --> c
+     * a/b/c/    --> ""
+     * 
+ *

+ * 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. + *

+ * This method returns the textual part of the filename after the last dot. + * There must be no directory separator after the dot. + *

+     * foo.txt      --> "txt"
+     * a/b/c.jpg    --> "jpg"
+     * a/b.txt/c    --> ""
+     * a/b/c        --> ""
+     * 
+ *

+ * 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. + *

+ * This method returns the textual part of the filename before the last dot. + * There must be no directory separator after the dot. + *

+     * foo.txt    --> foo
+     * a\b\c.jpg  --> a\b\c
+     * a\b\c      --> a\b\c
+     * a.b\c      --> a.b\c
+     * 
+ *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 extensions) { + if (filename == null) { + return false; + } + if (extensions == null || extensions.isEmpty()) { + return (indexOfExtension(filename) == -1); + } + String fileExt = getExtension(filename); + for (Iterator 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. + *

+ * 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. + *

+     * 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
+     * 
+ * + * @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. + *

+ * 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. + *

+     * 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
+     * 
+ * + * @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. + *

+ * 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 backtrack = new Stack(); + + // loop around a backtrack stack, to handle complex * matching + do { + if (backtrack.size() > 0) { + int[] array = (int[]) 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 list = new ArrayList(); + 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 (String[]) 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 b0d468d185..8293430bcc 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. - *

- * Provides a single function to take an array of bytes and display it - * in hexadecimal form. - *

- * 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. + *

+ * Provides a single function to take an array of bytes and display it + * in hexadecimal form. + *

+ * 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 4230f450d9..2f9bf18f3b 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. - *

- * Different filing systems have different rules for case-sensitivity. - * Windows is case-insensitive, Unix is case-sensitive. - *

- * 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. - *

- * Wherever possible, you should use the check 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. + *

+ * Different filing systems have different rules for case-sensitivity. + * Windows is case-insensitive, Unix is case-sensitive. + *

+ * 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. + *

+ * Wherever possible, you should use the check 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 a15815a220..0d81d31f46 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 Apache Commons IO - * @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. - *

- * As specified in {@link Throwable}, the message in the given cause is not used in this instance's - * message. - *

- * - * @param message - * the message (see {@link #getMessage()}) - * @param cause - * the cause (see {@link #getCause()}). A null value is allowed. - */ - public IOExceptionWithCause(String message, Throwable cause) { - super(message); - this.initCause(cause); - } - - /** - * Constructs a new instance with the given cause. - *

- * The message is set to cause==null ? null : cause.toString(), which by default contains the class - * and message of cause. This constructor is useful for call sites that just wrap another throwable. - *

- * - * @param cause - * the cause (see {@link #getCause()}). A null 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 Apache Commons IO + * @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. + *

+ * As specified in {@link Throwable}, the message in the given cause is not used in this instance's + * message. + *

+ * + * @param message + * the message (see {@link #getMessage()}) + * @param cause + * the cause (see {@link #getCause()}). A null value is allowed. + */ + public IOExceptionWithCause(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + /** + * Constructs a new instance with the given cause. + *

+ * The message is set to cause==null ? null : cause.toString(), which by default contains the class + * and message of cause. This constructor is useful for call sites that just wrap another throwable. + *

+ * + * @param cause + * the cause (see {@link #getCause()}). A null 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 1f91e25627..dfbd465f7e 100644 --- a/src/org/apache/commons/io/IOUtils.java +++ b/src/org/apache/commons/io/IOUtils.java @@ -1,1274 +1,1274 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * This class provides static utility methods for input/output operations. - *

    - *
  • closeQuietly - these methods close a stream ignoring nulls and exceptions - *
  • toXxx/read - these methods read data from a stream - *
  • write - these methods write data to a stream - *
  • copy - these methods copy all the data from one stream to another - *
  • contentEquals - these methods compare the content of two streams - *
- *

- * 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. - *

- * All the methods in this class that read a stream are buffered internally. - * This means that there is no cause to use a BufferedInputStream - * or BufferedReader. The default buffer size of 4K has been shown - * to be efficient in tests. - *

- * Wherever possible, the methods in this class do not 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. - *

- * 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 Reader. - *

- * 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 Writer. - *

- * 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 InputStream. - *

- * 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 OutputStream. - *

- * 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 InputStream as a byte[]. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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 Reader as a byte[] - * using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader 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 Reader as a byte[] - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader 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 String as a byte[] - * using the default character encoding of the platform. - *

- * This is the same as {@link String#getBytes()}. - * - * @param input the String 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()} - */ - public static byte[] toByteArray(String input) throws IOException { - return input.getBytes(); - } - - // read char[] - //----------------------------------------------------------------------- - /** - * Get the contents of an InputStream as a character array - * using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param is the InputStream 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 InputStream as a character array - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param is the InputStream 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 Reader as a character array. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader 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 InputStream as a String - * using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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 InputStream as a String - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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 Reader as a String. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader 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 byte[] 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[])} - */ - public static String toString(byte[] input) throws IOException { - return new String(input); - } - - /** - * Get the contents of a byte[] as a String - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - * - * @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)} - */ - 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 InputStream as a list of Strings, - * one entry per line, using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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 readLines(InputStream input) throws IOException { - InputStreamReader reader = new InputStreamReader(input); - return readLines(reader); - } - - /** - * Get the contents of an InputStream as a list of Strings, - * one entry per line, using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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 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 Reader as a list of Strings, - * one entry per line. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader 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 readLines(Reader input) throws IOException { - BufferedReader reader = new BufferedReader(input); - List list = new ArrayList(); - String line = reader.readLine(); - while (line != null) { - list.add(line); - line = reader.readLine(); - } - return list; - } - - // lineIterator - //----------------------------------------------------------------------- - /** - * Return an Iterator for the lines in a Reader. - *

- * LineIterator holds a reference to the open - * Reader 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)}. - *

- * The recommended usage pattern is: - *

-     * try {
-     *   LineIterator it = IOUtils.lineIterator(reader);
-     *   while (it.hasNext()) {
-     *     String line = it.nextLine();
-     *     /// do something with line
-     *   }
-     * } finally {
-     *   IOUtils.closeQuietly(reader);
-     * }
-     * 
- * - * @param reader the Reader 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 InputStream, using - * the character encoding specified (or default encoding if null). - *

- * LineIterator holds a reference to the open - * InputStream 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)}. - *

- * The recommended usage pattern is: - *

-     * try {
-     *   LineIterator it = IOUtils.lineIterator(stream, "UTF-8");
-     *   while (it.hasNext()) {
-     *     String line = it.nextLine();
-     *     /// do something with line
-     *   }
-     * } finally {
-     *   IOUtils.closeQuietly(stream);
-     * }
-     * 
- * - * @param input the InputStream 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. - *

- * Character encoding names can be found at - * IANA. - * - * @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 byte[] to an OutputStream. - * - * @param data the byte array to write, do not modify during output, - * null ignored - * @param output the OutputStream 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 byte[] to chars on a Writer - * using the default character encoding of the platform. - *

- * This method uses {@link String#String(byte[])}. - * - * @param data the byte array to write, do not modify during output, - * null ignored - * @param output the Writer 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 byte[] to chars on a Writer - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * 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 Writer 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 char[] to a Writer - * 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 Writer 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 char[] to bytes on an - * OutputStream. - *

- * 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 OutputStream 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 char[] to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * 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 OutputStream 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 String to a Writer. - * - * @param data the String to write, null ignored - * @param output the Writer 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 String to bytes on an - * OutputStream using the default character encoding of the - * platform. - *

- * This method uses {@link String#getBytes()}. - * - * @param data the String to write, null ignored - * @param output the OutputStream 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 String to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#getBytes(String)}. - * - * @param data the String to write, null ignored - * @param output the OutputStream 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 StringBuffer to a Writer. - * - * @param data the StringBuffer to write, null ignored - * @param output the Writer 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 StringBuffer to bytes on an - * OutputStream using the default character encoding of the - * platform. - *

- * This method uses {@link String#getBytes()}. - * - * @param data the StringBuffer to write, null ignored - * @param output the OutputStream 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 StringBuffer to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#getBytes(String)}. - * - * @param data the StringBuffer to write, null ignored - * @param output the OutputStream 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 toString() value of each item in a collection to - * an OutputStream 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 OutputStream 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 lines, String lineEnding, - OutputStream output) throws IOException { - if (lines == null) { - return; - } - if (lineEnding == null) { - lineEnding = LINE_SEPARATOR; - } - for (Iterator it = lines.iterator(); it.hasNext(); ) { - Object line = it.next(); - if (line != null) { - output.write(line.toString().getBytes()); - } - output.write(lineEnding.getBytes()); - } - } - - /** - * Writes the toString() value of each item in a collection to - * an OutputStream line by line, using the specified character - * encoding and the specified line ending. - *

- * Character encoding names can be found at - * IANA. - * - * @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 OutputStream 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 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 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 toString() value of each item in a collection to - * a Writer 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 Writer 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 lines, String lineEnding, - Writer writer) throws IOException { - if (lines == null) { - return; - } - if (lineEnding == null) { - lineEnding = LINE_SEPARATOR; - } - for (Iterator 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 InputStream to an - * OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * Large streams (over 2GB) will return a bytes copied value of - * -1 after the copy has completed since the correct - * number of bytes cannot be returned as an int. For large streams - * use the copyLarge(InputStream, OutputStream) method. - * - * @param input the InputStream to read from - * @param output the OutputStream 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) InputStream to an - * OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @param output the OutputStream 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 InputStream to chars on a - * Writer using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * This method uses {@link InputStreamReader}. - * - * @param input the InputStream to read from - * @param output the Writer 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 InputStream to chars on a - * Writer using the specified character encoding. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link InputStreamReader}. - * - * @param input the InputStream to read from - * @param output the Writer 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 Reader to a Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Large streams (over 2GB) will return a chars copied value of - * -1 after the copy has completed since the correct - * number of chars cannot be returned as an int. For large streams - * use the copyLarge(Reader, Writer) method. - * - * @param input the Reader to read from - * @param output the Writer 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) Reader to a Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @param output the Writer 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 Reader to bytes on an - * OutputStream using the default character encoding of the - * platform, and calling flush. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Due to the implementation of OutputStreamWriter, this method performs a - * flush. - *

- * This method uses {@link OutputStreamWriter}. - * - * @param input the Reader to read from - * @param output the OutputStream 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 Reader to bytes on an - * OutputStream using the specified character encoding, and - * calling flush. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Character encoding names can be found at - * IANA. - *

- * Due to the implementation of OutputStreamWriter, this method performs a - * flush. - *

- * This method uses {@link OutputStreamWriter}. - * - * @param input the Reader to read from - * @param output the OutputStream 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. - *

- * This method buffers the input internally using - * BufferedInputStream 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. - *

- * This method buffers the input internally using - * BufferedReader 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. + *

+ * This class provides static utility methods for input/output operations. + *

    + *
  • closeQuietly - these methods close a stream ignoring nulls and exceptions + *
  • toXxx/read - these methods read data from a stream + *
  • write - these methods write data to a stream + *
  • copy - these methods copy all the data from one stream to another + *
  • contentEquals - these methods compare the content of two streams + *
+ *

+ * 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. + *

+ * All the methods in this class that read a stream are buffered internally. + * This means that there is no cause to use a BufferedInputStream + * or BufferedReader. The default buffer size of 4K has been shown + * to be efficient in tests. + *

+ * Wherever possible, the methods in this class do not 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. + *

+ * 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 Reader. + *

+ * 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 Writer. + *

+ * 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 InputStream. + *

+ * 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 OutputStream. + *

+ * 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 InputStream as a byte[]. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream 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 Reader as a byte[] + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader 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 Reader as a byte[] + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader 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 String as a byte[] + * using the default character encoding of the platform. + *

+ * This is the same as {@link String#getBytes()}. + * + * @param input the String 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()} + */ + public static byte[] toByteArray(String input) throws IOException { + return input.getBytes(); + } + + // read char[] + //----------------------------------------------------------------------- + /** + * Get the contents of an InputStream as a character array + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream 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 InputStream as a character array + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream 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 Reader as a character array. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader 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 InputStream as a String + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream 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 InputStream as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream 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 Reader as a String. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader 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 byte[] 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[])} + */ + public static String toString(byte[] input) throws IOException { + return new String(input); + } + + /** + * Get the contents of a byte[] as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @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)} + */ + 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 InputStream as a list of Strings, + * one entry per line, using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream 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 readLines(InputStream input) throws IOException { + InputStreamReader reader = new InputStreamReader(input); + return readLines(reader); + } + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream 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 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 Reader as a list of Strings, + * one entry per line. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader 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 readLines(Reader input) throws IOException { + BufferedReader reader = new BufferedReader(input); + List list = new ArrayList(); + String line = reader.readLine(); + while (line != null) { + list.add(line); + line = reader.readLine(); + } + return list; + } + + // lineIterator + //----------------------------------------------------------------------- + /** + * Return an Iterator for the lines in a Reader. + *

+ * LineIterator holds a reference to the open + * Reader 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)}. + *

+ * The recommended usage pattern is: + *

+     * try {
+     *   LineIterator it = IOUtils.lineIterator(reader);
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   IOUtils.closeQuietly(reader);
+     * }
+     * 
+ * + * @param reader the Reader 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 InputStream, using + * the character encoding specified (or default encoding if null). + *

+ * LineIterator holds a reference to the open + * InputStream 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)}. + *

+ * The recommended usage pattern is: + *

+     * try {
+     *   LineIterator it = IOUtils.lineIterator(stream, "UTF-8");
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   IOUtils.closeQuietly(stream);
+     * }
+     * 
+ * + * @param input the InputStream 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. + *

+ * Character encoding names can be found at + * IANA. + * + * @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 byte[] to an OutputStream. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the OutputStream 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 byte[] to chars on a Writer + * using the default character encoding of the platform. + *

+ * This method uses {@link String#String(byte[])}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer 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 byte[] to chars on a Writer + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * 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 Writer 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 char[] to a Writer + * 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 Writer 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 char[] to bytes on an + * OutputStream. + *

+ * 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 OutputStream 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 char[] to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * 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 OutputStream 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 String to a Writer. + * + * @param data the String to write, null ignored + * @param output the Writer 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 String to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the String to write, null ignored + * @param output the OutputStream 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 String to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream 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 StringBuffer to a Writer. + * + * @param data the StringBuffer to write, null ignored + * @param output the Writer 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 StringBuffer to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the StringBuffer to write, null ignored + * @param output the OutputStream 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 StringBuffer to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the StringBuffer to write, null ignored + * @param output the OutputStream 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 toString() value of each item in a collection to + * an OutputStream 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 OutputStream 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 lines, String lineEnding, + OutputStream output) throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + for (Iterator it = lines.iterator(); it.hasNext(); ) { + Object line = it.next(); + if (line != null) { + output.write(line.toString().getBytes()); + } + output.write(lineEnding.getBytes()); + } + } + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the specified character + * encoding and the specified line ending. + *

+ * Character encoding names can be found at + * IANA. + * + * @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 OutputStream 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 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 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 toString() value of each item in a collection to + * a Writer 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 Writer 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 lines, String lineEnding, + Writer writer) throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + for (Iterator 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 InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Large streams (over 2GB) will return a bytes copied value of + * -1 after the copy has completed since the correct + * number of bytes cannot be returned as an int. For large streams + * use the copyLarge(InputStream, OutputStream) method. + * + * @param input the InputStream to read from + * @param output the OutputStream 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) InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @param output the OutputStream 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 InputStream to chars on a + * Writer using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer 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 InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer 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 Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Large streams (over 2GB) will return a chars copied value of + * -1 after the copy has completed since the correct + * number of chars cannot be returned as an int. For large streams + * use the copyLarge(Reader, Writer) method. + * + * @param input the Reader to read from + * @param output the Writer 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) Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @param output the Writer 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 Reader to bytes on an + * OutputStream using the default character encoding of the + * platform, and calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream 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 Reader to bytes on an + * OutputStream using the specified character encoding, and + * calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Character encoding names can be found at + * IANA. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream 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. + *

+ * This method buffers the input internally using + * BufferedInputStream 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. + *

+ * This method buffers the input internally using + * BufferedReader 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 eac47d23a3..2bec43b977 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 Reader. - *

- * LineIterator holds a reference to an open Reader. - * 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. - *

- * The recommended usage pattern is: - *

- * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
- * try {
- *   while (it.hasNext()) {
- *     String line = it.nextLine();
- *     /// do something with line
- *   }
- * } finally {
- *   LineIterator.closeQuietly(iterator);
- * }
- * 
- * - * @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 Reader. - * - * @param reader the Reader 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 Reader has more lines. - * If there is an IOException then {@link #close()} will - * be called on this instance. - * - * @return true 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 Reader. - * - * @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 Reader. - * - * @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 Reader 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 Reader 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 Reader. + *

+ * LineIterator holds a reference to an open Reader. + * 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. + *

+ * The recommended usage pattern is: + *

+ * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
+ * try {
+ *   while (it.hasNext()) {
+ *     String line = it.nextLine();
+ *     /// do something with line
+ *   }
+ * } finally {
+ *   LineIterator.closeQuietly(iterator);
+ * }
+ * 
+ * + * @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 Reader. + * + * @param reader the Reader 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 Reader has more lines. + * If there is an IOException then {@link #close()} will + * be called on this instance. + * + * @return true 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 Reader. + * + * @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 Reader. + * + * @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 Reader 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 Reader 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 d36076288c..851817d0b4 100644 --- a/src/org/apache/commons/io/comparator/DefaultFileComparator.java +++ b/src/org/apache/commons/io/comparator/DefaultFileComparator.java @@ -1,68 +1,68 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 default {@link File#compareTo(File)} method. - *

- * This comparator can be used to sort lists or arrays of files - * by using the default file comparison. - *

- * Example of sorting a list of files using the - * {@link #DEFAULT_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, DefaultFileComparator.DEFAULT_COMPARATOR);
- * 
- *

- * Example of doing a reverse sort of an array of files using the - * {@link #DEFAULT_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, DefaultFileComparator.DEFAULT_REVERSE);
- * 
- *

- * - * @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, Serializable { - - /** Singleton default comparator instance */ - public static final Comparator DEFAULT_COMPARATOR = new DefaultFileComparator(); - - /** Singleton reverse default comparator instance */ - public static final Comparator DEFAULT_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 default {@link File#compareTo(File)} method. + *

+ * This comparator can be used to sort lists or arrays of files + * by using the default file comparison. + *

+ * Example of sorting a list of files using the + * {@link #DEFAULT_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, DefaultFileComparator.DEFAULT_COMPARATOR);
+ * 
+ *

+ * Example of doing a reverse sort of an array of files using the + * {@link #DEFAULT_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, DefaultFileComparator.DEFAULT_REVERSE);
+ * 
+ *

+ * + * @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, Serializable { + + /** Singleton default comparator instance */ + public static final Comparator DEFAULT_COMPARATOR = new DefaultFileComparator(); + + /** Singleton reverse default comparator instance */ + public static final Comparator DEFAULT_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 158480fb39..492b2f537a 100644 --- a/src/org/apache/commons/io/comparator/ExtensionFileComparator.java +++ b/src/org/apache/commons/io/comparator/ExtensionFileComparator.java @@ -1,112 +1,112 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 extensions for order - * (see {@link FilenameUtils#getExtension(String)}). - *

- * 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. - *

- * Example of a case-sensitive file extension sort using the - * {@link #EXTENSION_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, ExtensionFileComparator.EXTENSION_COMPARATOR);
- * 
- *

- * Example of a reverse case-insensitive file extension sort using the - * {@link #EXTENSION_INSENSITIVE_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, ExtensionFileComparator.EXTENSION_INSENSITIVE_REVERSE);
- * 
- *

- * - * @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, Serializable { - - /** Case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator EXTENSION_COMPARATOR = new ExtensionFileComparator(); - - /** Reverse case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator EXTENSION_REVERSE = new ReverseComparator(EXTENSION_COMPARATOR); - - /** Case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator EXTENSION_INSENSITIVE_COMPARATOR = new ExtensionFileComparator(IOCase.INSENSITIVE); - - /** Reverse case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator EXTENSION_INSENSITIVE_REVERSE - = new ReverseComparator(EXTENSION_INSENSITIVE_COMPARATOR); - - /** System sensitive extension comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator EXTENSION_SYSTEM_COMPARATOR = new ExtensionFileComparator(IOCase.SYSTEM); - - /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator EXTENSION_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 extensions for order + * (see {@link FilenameUtils#getExtension(String)}). + *

+ * 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. + *

+ * Example of a case-sensitive file extension sort using the + * {@link #EXTENSION_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, ExtensionFileComparator.EXTENSION_COMPARATOR);
+ * 
+ *

+ * Example of a reverse case-insensitive file extension sort using the + * {@link #EXTENSION_INSENSITIVE_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, ExtensionFileComparator.EXTENSION_INSENSITIVE_REVERSE);
+ * 
+ *

+ * + * @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, Serializable { + + /** Case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator EXTENSION_COMPARATOR = new ExtensionFileComparator(); + + /** Reverse case-sensitive extension comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator EXTENSION_REVERSE = new ReverseComparator(EXTENSION_COMPARATOR); + + /** Case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator EXTENSION_INSENSITIVE_COMPARATOR = new ExtensionFileComparator(IOCase.INSENSITIVE); + + /** Reverse case-insensitive extension comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator EXTENSION_INSENSITIVE_REVERSE + = new ReverseComparator(EXTENSION_INSENSITIVE_COMPARATOR); + + /** System sensitive extension comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator EXTENSION_SYSTEM_COMPARATOR = new ExtensionFileComparator(IOCase.SYSTEM); + + /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator EXTENSION_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 8265023d06..cd966a4e74 100644 --- a/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java +++ b/src/org/apache/commons/io/comparator/LastModifiedFileComparator.java @@ -1,79 +1,79 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 last modified date/time of two files for order - * (see {@link File#lastModified()}). - *

- * This comparator can be used to sort lists or arrays of files - * by their last modified date/time. - *

- * Example of sorting a list of files using the - * {@link #LASTMODIFIED_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
- * 
- *

- * Example of doing a reverse sort of an array of files using the - * {@link #LASTMODIFIED_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
- * 
- *

- * - * @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, Serializable { - - /** Last modified comparator instance */ - public static final Comparator LASTMODIFIED_COMPARATOR = new LastModifiedFileComparator(); - - /** Reverse last modified comparator instance */ - public static final Comparator LASTMODIFIED_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 last modified date/time of two files for order + * (see {@link File#lastModified()}). + *

+ * This comparator can be used to sort lists or arrays of files + * by their last modified date/time. + *

+ * Example of sorting a list of files using the + * {@link #LASTMODIFIED_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
+ * 
+ *

+ * Example of doing a reverse sort of an array of files using the + * {@link #LASTMODIFIED_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
+ * 
+ *

+ * + * @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, Serializable { + + /** Last modified comparator instance */ + public static final Comparator LASTMODIFIED_COMPARATOR = new LastModifiedFileComparator(); + + /** Reverse last modified comparator instance */ + public static final Comparator LASTMODIFIED_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 76af21eeb6..cbaf4c4754 100644 --- a/src/org/apache/commons/io/comparator/NameFileComparator.java +++ b/src/org/apache/commons/io/comparator/NameFileComparator.java @@ -1,106 +1,106 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 names of two files for order (see {@link File#getName()}). - *

- * 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. - *

- * Example of a case-sensitive file name sort using the - * {@link #NAME_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, NameFileComparator.NAME_COMPARATOR);
- * 
- *

- * Example of a reverse case-insensitive file name sort using the - * {@link #NAME_INSENSITIVE_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, NameFileComparator.NAME_INSENSITIVE_REVERSE);
- * 
- *

- * - * @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, Serializable { - - /** Case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator NAME_COMPARATOR = new NameFileComparator(); - - /** Reverse case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator NAME_REVERSE = new ReverseComparator(NAME_COMPARATOR); - - /** Case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator NAME_INSENSITIVE_COMPARATOR = new NameFileComparator(IOCase.INSENSITIVE); - - /** Reverse case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator NAME_INSENSITIVE_REVERSE = new ReverseComparator(NAME_INSENSITIVE_COMPARATOR); - - /** System sensitive name comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator NAME_SYSTEM_COMPARATOR = new NameFileComparator(IOCase.SYSTEM); - - /** Reverse system sensitive name comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator NAME_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 names of two files for order (see {@link File#getName()}). + *

+ * 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. + *

+ * Example of a case-sensitive file name sort using the + * {@link #NAME_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, NameFileComparator.NAME_COMPARATOR);
+ * 
+ *

+ * Example of a reverse case-insensitive file name sort using the + * {@link #NAME_INSENSITIVE_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, NameFileComparator.NAME_INSENSITIVE_REVERSE);
+ * 
+ *

+ * + * @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, Serializable { + + /** Case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator NAME_COMPARATOR = new NameFileComparator(); + + /** Reverse case-sensitive name comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator NAME_REVERSE = new ReverseComparator(NAME_COMPARATOR); + + /** Case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator NAME_INSENSITIVE_COMPARATOR = new NameFileComparator(IOCase.INSENSITIVE); + + /** Reverse case-insensitive name comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator NAME_INSENSITIVE_REVERSE = new ReverseComparator(NAME_INSENSITIVE_COMPARATOR); + + /** System sensitive name comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator NAME_SYSTEM_COMPARATOR = new NameFileComparator(IOCase.SYSTEM); + + /** Reverse system sensitive name comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator NAME_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 0b28b69602..cf6c36185f 100644 --- a/src/org/apache/commons/io/comparator/PathFileComparator.java +++ b/src/org/apache/commons/io/comparator/PathFileComparator.java @@ -1,107 +1,107 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 path of two files for order (see {@link File#getPath()}). - *

- * 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. - *

- * Example of a case-sensitive file path sort using the - * {@link #PATH_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, PathFileComparator.PATH_COMPARATOR);
- * 
- *

- * Example of a reverse case-insensitive file path sort using the - * {@link #PATH_INSENSITIVE_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, PathFileComparator.PATH_INSENSITIVE_REVERSE);
- * 
- *

- * - * @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, Serializable { - - /** Case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator PATH_COMPARATOR = new PathFileComparator(); - - /** Reverse case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */ - public static final Comparator PATH_REVERSE = new ReverseComparator(PATH_COMPARATOR); - - /** Case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator PATH_INSENSITIVE_COMPARATOR = new PathFileComparator(IOCase.INSENSITIVE); - - /** Reverse case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */ - public static final Comparator PATH_INSENSITIVE_REVERSE = new ReverseComparator(PATH_INSENSITIVE_COMPARATOR); - - /** System sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator PATH_SYSTEM_COMPARATOR = new PathFileComparator(IOCase.SYSTEM); - - /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ - public static final Comparator PATH_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 path of two files for order (see {@link File#getPath()}). + *

+ * 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. + *

+ * Example of a case-sensitive file path sort using the + * {@link #PATH_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, PathFileComparator.PATH_COMPARATOR);
+ * 
+ *

+ * Example of a reverse case-insensitive file path sort using the + * {@link #PATH_INSENSITIVE_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, PathFileComparator.PATH_INSENSITIVE_REVERSE);
+ * 
+ *

+ * + * @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, Serializable { + + /** Case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator PATH_COMPARATOR = new PathFileComparator(); + + /** Reverse case-sensitive path comparator instance (see {@link IOCase#SENSITIVE}) */ + public static final Comparator PATH_REVERSE = new ReverseComparator(PATH_COMPARATOR); + + /** Case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator PATH_INSENSITIVE_COMPARATOR = new PathFileComparator(IOCase.INSENSITIVE); + + /** Reverse case-insensitive path comparator instance (see {@link IOCase#INSENSITIVE}) */ + public static final Comparator PATH_INSENSITIVE_REVERSE = new ReverseComparator(PATH_INSENSITIVE_COMPARATOR); + + /** System sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator PATH_SYSTEM_COMPARATOR = new PathFileComparator(IOCase.SYSTEM); + + /** Reverse system sensitive path comparator instance (see {@link IOCase#SYSTEM}) */ + public static final Comparator PATH_SYSTEM_REVERSE = new ReverseComparator(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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 af9749ee3e..30846a3cae 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 implements Comparator, Serializable { - - private final Comparator delegate; - - /** - * Construct an instance with the sepecified delegate {@link Comparator}. - * - * @param delegate The comparator to delegate to - */ - public ReverseComparator(Comparator 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(Object obj1, Object 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 implements Comparator, Serializable { + + private final Comparator delegate; + + /** + * Construct an instance with the sepecified delegate {@link Comparator}. + * + * @param delegate The comparator to delegate to + */ + public ReverseComparator(Comparator 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(Object obj1, Object 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 a1621671c2..4a3f67249b 100644 --- a/src/org/apache/commons/io/comparator/SizeFileComparator.java +++ b/src/org/apache/commons/io/comparator/SizeFileComparator.java @@ -1,132 +1,132 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 length/size of two files for order (see - * {@link File#length()} and {@link FileUtils#sizeOfDirectory(File)}). - *

- * This comparator can be used to sort lists or arrays of files - * by their length/size. - *

- * Example of sorting a list of files using the - * {@link #SIZE_COMPARATOR} singleton instance: - *

- *       List<File> list = ...
- *       Collections.sort(list, LengthFileComparator.LENGTH_COMPARATOR);
- * 
- *

- * Example of doing a reverse sort of an array of files using the - * {@link #SIZE_REVERSE} singleton instance: - *

- *       File[] array = ...
- *       Arrays.sort(array, LengthFileComparator.LENGTH_REVERSE);
- * 
- *

- * N.B. Directories are treated as zero size unless - * sumDirectoryContents is true. - * - * @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, Serializable { - - /** Size comparator instance - directories are treated as zero size */ - public static final Comparator SIZE_COMPARATOR = new SizeFileComparator(); - - /** Reverse size comparator instance - directories are treated as zero size */ - public static final Comparator SIZE_REVERSE = new ReverseComparator(SIZE_COMPARATOR); - - /** - * Size comparator instance which sums the size of a directory's contents - * using {@link FileUtils#sizeOfDirectory(File)} - */ - public static final Comparator 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 SIZE_SUMDIR_REVERSE = new ReverseComparator(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. - *

- * If the sumDirectoryContents is true The size of - * directories is calculated using {@link FileUtils#sizeOfDirectory(File)}. - * - * @param sumDirectoryContents true if the sum of the directoryies contents - * should be calculated, otherwise false 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(Object obj1, Object obj2) { - File file1 = (File)obj1; - File file2 = (File)obj2; - 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 length/size of two files for order (see + * {@link File#length()} and {@link FileUtils#sizeOfDirectory(File)}). + *

+ * This comparator can be used to sort lists or arrays of files + * by their length/size. + *

+ * Example of sorting a list of files using the + * {@link #SIZE_COMPARATOR} singleton instance: + *

+ *       List<File> list = ...
+ *       Collections.sort(list, LengthFileComparator.LENGTH_COMPARATOR);
+ * 
+ *

+ * Example of doing a reverse sort of an array of files using the + * {@link #SIZE_REVERSE} singleton instance: + *

+ *       File[] array = ...
+ *       Arrays.sort(array, LengthFileComparator.LENGTH_REVERSE);
+ * 
+ *

+ * N.B. Directories are treated as zero size unless + * sumDirectoryContents is true. + * + * @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, Serializable { + + /** Size comparator instance - directories are treated as zero size */ + public static final Comparator SIZE_COMPARATOR = new SizeFileComparator(); + + /** Reverse size comparator instance - directories are treated as zero size */ + public static final Comparator SIZE_REVERSE = new ReverseComparator(SIZE_COMPARATOR); + + /** + * Size comparator instance which sums the size of a directory's contents + * using {@link FileUtils#sizeOfDirectory(File)} + */ + public static final Comparator 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 SIZE_SUMDIR_REVERSE = new ReverseComparator(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. + *

+ * If the sumDirectoryContents is true The size of + * directories is calculated using {@link FileUtils#sizeOfDirectory(File)}. + * + * @param sumDirectoryContents true if the sum of the directoryies contents + * should be calculated, otherwise false 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(Object obj1, Object obj2) { + File file1 = (File)obj1; + File file2 = (File)obj2; + 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 a2f756f183..fe81e2fa9f 100644 --- a/src/org/apache/commons/io/comparator/package.html +++ b/src/org/apache/commons/io/comparator/package.html @@ -1,25 +1,25 @@ - - - - -

This package provides various {@link java.util.Comparator} implementations -for {@link java.io.File}s. -

- - - + + + + +

This package provides various {@link java.util.Comparator} implementations +for {@link java.io.File}s. +

+ + + diff --git a/src/org/apache/commons/io/filefilter/AbstractFileFilter.java b/src/org/apache/commons/io/filefilter/AbstractFileFilter.java index 9e188f82e1..b0dfcfa989 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. - *

- * Note that a subclass must 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. + *

+ * Note that a subclass must 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 ab73cb840e..0139e68142 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. - *

- * For example, to print all files and directories in the - * current directory older than one day: - * - *

- * 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]);
- * }
- * 
- * - * @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. - *

- * If last modification time equals cutoff and newer files are required, - * file IS NOT selected. - * If last modification time equals cutoff and older files are required, - * file IS 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. + *

+ * For example, to print all files and directories in the + * current directory older than one day: + * + *

+ * 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]);
+ * }
+ * 
+ * + * @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. + *

+ * If last modification time equals cutoff and newer files are required, + * file IS NOT selected. + * If last modification time equals cutoff and older files are required, + * file IS 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 deda11f93b..a8375271b8 100644 --- a/src/org/apache/commons/io/filefilter/AndFileFilter.java +++ b/src/org/apache/commons/io/filefilter/AndFileFilter.java @@ -1,167 +1,167 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 true if all filters in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * false. - * - * @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 fileFilters; - - /** - * Constructs a new instance of AndFileFilter. - * - * @since Commons IO 1.1 - */ - public AndFileFilter() { - this.fileFilters = new ArrayList(); - } - - /** - * Constructs a new instance of AndFileFilter - * 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 fileFilters) { - if (fileFilters == null) { - this.fileFilters = new ArrayList(); - } else { - this.fileFilters = new ArrayList(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(); - addFileFilter(filter1); - addFileFilter(filter2); - } - - /** - * {@inheritDoc} - */ - public void addFileFilter(final IOFileFilter ioFileFilter) { - this.fileFilters.add(ioFileFilter); - } - - /** - * {@inheritDoc} - */ - public List getFileFilters() { - return Collections.unmodifiableList(this.fileFilters); - } - - /** - * {@inheritDoc} - */ - public boolean removeFileFilter(final IOFileFilter ioFileFilter) { - return this.fileFilters.remove(ioFileFilter); - } - - /** - * {@inheritDoc} - */ - public void setFileFilters(final List fileFilters) { - this.fileFilters = new ArrayList(fileFilters); - } - - /** - * {@inheritDoc} - */ - public boolean accept(final File file) { - if (this.fileFilters.size() == 0) { - return false; - } - for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { - IOFileFilter fileFilter = (IOFileFilter) iter.next(); - if (!fileFilter.accept(file)) { - return false; - } - } - return true; - } - - /** - * {@inheritDoc} - */ - public boolean accept(final File file, final String name) { - if (this.fileFilters.size() == 0) { - return false; - } - for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { - IOFileFilter fileFilter = (IOFileFilter) iter.next(); - if (!fileFilter.accept(file, name)) { - return false; - } - } - return true; - } - - /** - * Provide a String representaion of this file filter. - * - * @return a String representaion - */ - 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 true if all filters in the + * list return true. Otherwise, it returns false. + * Checking of the file filter list stops when the first filter returns + * false. + * + * @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 fileFilters; + + /** + * Constructs a new instance of AndFileFilter. + * + * @since Commons IO 1.1 + */ + public AndFileFilter() { + this.fileFilters = new ArrayList(); + } + + /** + * Constructs a new instance of AndFileFilter + * 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 fileFilters) { + if (fileFilters == null) { + this.fileFilters = new ArrayList(); + } else { + this.fileFilters = new ArrayList(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(); + addFileFilter(filter1); + addFileFilter(filter2); + } + + /** + * {@inheritDoc} + */ + public void addFileFilter(final IOFileFilter ioFileFilter) { + this.fileFilters.add(ioFileFilter); + } + + /** + * {@inheritDoc} + */ + public List getFileFilters() { + return Collections.unmodifiableList(this.fileFilters); + } + + /** + * {@inheritDoc} + */ + public boolean removeFileFilter(final IOFileFilter ioFileFilter) { + return this.fileFilters.remove(ioFileFilter); + } + + /** + * {@inheritDoc} + */ + public void setFileFilters(final List fileFilters) { + this.fileFilters = new ArrayList(fileFilters); + } + + /** + * {@inheritDoc} + */ + public boolean accept(final File file) { + if (this.fileFilters.size() == 0) { + return false; + } + for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { + IOFileFilter fileFilter = (IOFileFilter) iter.next(); + if (!fileFilter.accept(file)) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public boolean accept(final File file, final String name) { + if (this.fileFilters.size() == 0) { + return false; + } + for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { + IOFileFilter fileFilter = (IOFileFilter) iter.next(); + if (!fileFilter.accept(file, name)) { + return false; + } + } + return true; + } + + /** + * Provide a String representaion of this file filter. + * + * @return a String representaion + */ + 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 a9c132570c..1744af0e3d 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 Files that can be read. - *

- * Example, showing how to print out a list of the - * current directory's readable files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( CanReadFileFilter.CAN_READ );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * Example, showing how to print out a list of the - * current directory's un-readable files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( CanReadFileFilter.CANNOT_READ );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * Example, showing how to print out a list of the - * current directory's read-only files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( CanReadFileFilter.READ_ONLY );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @since Commons IO 1.3 - * @version $Revision: 587916 $ - */ -public class CanReadFileFilter extends AbstractFileFilter implements Serializable { - - /** Singleton instance of readable filter */ - public static final IOFileFilter CAN_READ = new CanReadFileFilter(); - - /** Singleton instance of not readable filter */ - public static final IOFileFilter CANNOT_READ = new NotFileFilter(CAN_READ); - - /** Singleton instance of read-only 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 true if the file can be - * read, otherwise false. - */ - 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 Files that can be read. + *

+ * Example, showing how to print out a list of the + * current directory's readable files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.CAN_READ );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * Example, showing how to print out a list of the + * current directory's un-readable files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.CANNOT_READ );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * Example, showing how to print out a list of the + * current directory's read-only files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( CanReadFileFilter.READ_ONLY );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @since Commons IO 1.3 + * @version $Revision: 587916 $ + */ +public class CanReadFileFilter extends AbstractFileFilter implements Serializable { + + /** Singleton instance of readable filter */ + public static final IOFileFilter CAN_READ = new CanReadFileFilter(); + + /** Singleton instance of not readable filter */ + public static final IOFileFilter CANNOT_READ = new NotFileFilter(CAN_READ); + + /** Singleton instance of read-only 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 true if the file can be + * read, otherwise false. + */ + 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 da664f25ca..b91eccae33 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 Files that can be written to. - *

- * Example, showing how to print out a list of the - * current directory's writable files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( CanWriteFileFilter.CAN_WRITE );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * Example, showing how to print out a list of the - * current directory's un-writable files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( CanWriteFileFilter.CANNOT_WRITE );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * N.B. For read-only files, use - * CanReadFileFilter.READ_ONLY. - * - * @since Commons IO 1.3 - * @version $Revision: 587916 $ - */ -public class CanWriteFileFilter extends AbstractFileFilter implements Serializable { - - /** Singleton instance of writable filter */ - public static final IOFileFilter CAN_WRITE = new CanWriteFileFilter(); - - /** Singleton instance of not writable 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 true if the file can be - * written to, otherwise false. - */ - 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 Files that can be written to. + *

+ * Example, showing how to print out a list of the + * current directory's writable files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( CanWriteFileFilter.CAN_WRITE );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * Example, showing how to print out a list of the + * current directory's un-writable files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( CanWriteFileFilter.CANNOT_WRITE );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * N.B. For read-only files, use + * CanReadFileFilter.READ_ONLY. + * + * @since Commons IO 1.3 + * @version $Revision: 587916 $ + */ +public class CanWriteFileFilter extends AbstractFileFilter implements Serializable { + + /** Singleton instance of writable filter */ + public static final IOFileFilter CAN_WRITE = new CanWriteFileFilter(); + + /** Singleton instance of not writable 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 true if the file can be + * written to, otherwise false. + */ + 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 ce1419ee82..5faf0075df 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 getFileFilters(); - - /** - * Removes the specified file filter. - * - * @param ioFileFilter filter to be removed - * @return true if the filter was found in the list, - * false 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 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 getFileFilters(); + + /** + * Removes the specified file filter. + * + * @param ioFileFilter filter to be removed + * @return true if the filter was found in the list, + * false 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 fileFilters); + +} diff --git a/src/org/apache/commons/io/filefilter/DelegateFileFilter.java b/src/org/apache/commons/io/filefilter/DelegateFileFilter.java index c2d67c4691..1c86d7e501 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 3412e7beff..a1a44f522e 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 Files that are directories. - *

- * For example, here is how to print out a list of the - * current directory's subdirectories: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( DirectoryFileFilter.INSTANCE );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @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 Files that are directories. + *

+ * For example, here is how to print out a list of the + * current directory's subdirectories: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( DirectoryFileFilter.INSTANCE );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @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 e88a862d48..f5219f420b 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. - *

- * If the File is a directory it checks that - * it contains no files. - *

- * Example, showing how to print out a list of the - * current directory's empty files/directories: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( EmptyFileFilter.EMPTY );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * Example, showing how to print out a list of the - * current directory's non-empty files/directories: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( EmptyFileFilter.NOT_EMPTY );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @since Commons IO 1.3 - * @version $Revision: 587916 $ - */ -public class EmptyFileFilter extends AbstractFileFilter implements Serializable { - - /** Singleton instance of empty filter */ - public static final IOFileFilter EMPTY = new EmptyFileFilter(); - - /** Singleton instance of not-empty 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 true if the file or directory - * is empty, otherwise false. - */ - 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. + *

+ * If the File is a directory it checks that + * it contains no files. + *

+ * Example, showing how to print out a list of the + * current directory's empty files/directories: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( EmptyFileFilter.EMPTY );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * Example, showing how to print out a list of the + * current directory's non-empty files/directories: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( EmptyFileFilter.NOT_EMPTY );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @since Commons IO 1.3 + * @version $Revision: 587916 $ + */ +public class EmptyFileFilter extends AbstractFileFilter implements Serializable { + + /** Singleton instance of empty filter */ + public static final IOFileFilter EMPTY = new EmptyFileFilter(); + + /** Singleton instance of not-empty 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 true if the file or directory + * is empty, otherwise false. + */ + 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 8a87d40921..5a24c6a0f2 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 0d49eddd44..7547f4e9c5 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 Files that are files (not directories). - *

- * For example, here is how to print out a list of the real files - * within the current directory: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( FileFileFilter.FILE );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @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 Files that are files (not directories). + *

+ * For example, here is how to print out a list of the real files + * within the current directory: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( FileFileFilter.FILE );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @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 71c37b1d28..e09e28bb99 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 IOFileFilter that wraps the - * FileFilter 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 IOFileFilter that wraps the - * FilenameFilter 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 null 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 null 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 IOFileFilter that wraps the + * FileFilter 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 IOFileFilter that wraps the + * FilenameFilter 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 null 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 null 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 244153d5e3..9d90aa7f4a 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 Files that are hidden. - *

- * Example, showing how to print out a list of the - * current directory's hidden files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( HiddenFileFilter.HIDDEN );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - *

- * Example, showing how to print out a list of the - * current directory's visible (i.e. not hidden) files: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( HiddenFileFilter.VISIBLE );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @since Commons IO 1.3 - * @version $Revision: 587916 $ - */ -public class HiddenFileFilter extends AbstractFileFilter implements Serializable { - - /** Singleton instance of hidden filter */ - public static final IOFileFilter HIDDEN = new HiddenFileFilter(); - - /** Singleton instance of visible 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 true if the file is - * hidden, otherwise false. - */ - 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 Files that are hidden. + *

+ * Example, showing how to print out a list of the + * current directory's hidden files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( HiddenFileFilter.HIDDEN );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + *

+ * Example, showing how to print out a list of the + * current directory's visible (i.e. not hidden) files: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( HiddenFileFilter.VISIBLE );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @since Commons IO 1.3 + * @version $Revision: 587916 $ + */ +public class HiddenFileFilter extends AbstractFileFilter implements Serializable { + + /** Singleton instance of hidden filter */ + public static final IOFileFilter HIDDEN = new HiddenFileFilter(); + + /** Singleton instance of visible 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 true if the file is + * hidden, otherwise false. + */ + 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 5ebd827510..cd650c17dc 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. - *

- * 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. - *

- * 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. + *

+ * 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. + *

+ * 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 fa1c80f730..92c4353ad0 100644 --- a/src/org/apache/commons/io/filefilter/NameFileFilter.java +++ b/src/org/apache/commons/io/filefilter/NameFileFilter.java @@ -1,191 +1,191 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * For example, to print all files and directories in the - * current directory whose name is Test: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( new NameFileFilter("Test") );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @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. - *

- * 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. - *

- * 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 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 names, IOCase caseSensitivity) { - if (names == null) { - throw new IllegalArgumentException("The list of names must not be null"); - } - this.names = (String[]) 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 - */ - 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 - */ - 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 - */ - 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. + *

+ * For example, to print all files and directories in the + * current directory whose name is Test: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( new NameFileFilter("Test") );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @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. + *

+ * 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. + *

+ * 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 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 names, IOCase caseSensitivity) { + if (names == null) { + throw new IllegalArgumentException("The list of names must not be null"); + } + this.names = (String[]) 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 + */ + 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 + */ + 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 + */ + 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 710c8ecf31..3991f5d45e 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 59cc0f2b04..bcbb786902 100644 --- a/src/org/apache/commons/io/filefilter/OrFileFilter.java +++ b/src/org/apache/commons/io/filefilter/OrFileFilter.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.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 true if any filters in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * true. - * - * @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 fileFilters; - - /** - * Constructs a new instance of OrFileFilter. - * - * @since Commons IO 1.1 - */ - public OrFileFilter() { - this.fileFilters = new ArrayList(); - } - - /** - * Constructs a new instance of OrFileFilter - * with the specified filters. - * - * @param fileFilters the file filters for this filter, copied, null ignored - * @since Commons IO 1.1 - */ - public OrFileFilter(final List fileFilters) { - if (fileFilters == null) { - this.fileFilters = new ArrayList(); - } else { - this.fileFilters = new ArrayList(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(); - addFileFilter(filter1); - addFileFilter(filter2); - } - - /** - * {@inheritDoc} - */ - public void addFileFilter(final IOFileFilter ioFileFilter) { - this.fileFilters.add(ioFileFilter); - } - - /** - * {@inheritDoc} - */ - public List getFileFilters() { - return Collections.unmodifiableList(this.fileFilters); - } - - /** - * {@inheritDoc} - */ - public boolean removeFileFilter(IOFileFilter ioFileFilter) { - return this.fileFilters.remove(ioFileFilter); - } - - /** - * {@inheritDoc} - */ - public void setFileFilters(final List fileFilters) { - this.fileFilters = fileFilters; - } - - /** - * {@inheritDoc} - */ - public boolean accept(final File file) { - for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { - IOFileFilter fileFilter = (IOFileFilter) iter.next(); - if (fileFilter.accept(file)) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - public boolean accept(final File file, final String name) { - for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { - IOFileFilter fileFilter = (IOFileFilter) iter.next(); - if (fileFilter.accept(file, name)) { - return true; - } - } - return false; - } - - /** - * Provide a String representaion of this file filter. - * - * @return a String representaion - */ - 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 true if any filters in the + * list return true. Otherwise, it returns false. + * Checking of the file filter list stops when the first filter returns + * true. + * + * @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 fileFilters; + + /** + * Constructs a new instance of OrFileFilter. + * + * @since Commons IO 1.1 + */ + public OrFileFilter() { + this.fileFilters = new ArrayList(); + } + + /** + * Constructs a new instance of OrFileFilter + * with the specified filters. + * + * @param fileFilters the file filters for this filter, copied, null ignored + * @since Commons IO 1.1 + */ + public OrFileFilter(final List fileFilters) { + if (fileFilters == null) { + this.fileFilters = new ArrayList(); + } else { + this.fileFilters = new ArrayList(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(); + addFileFilter(filter1); + addFileFilter(filter2); + } + + /** + * {@inheritDoc} + */ + public void addFileFilter(final IOFileFilter ioFileFilter) { + this.fileFilters.add(ioFileFilter); + } + + /** + * {@inheritDoc} + */ + public List getFileFilters() { + return Collections.unmodifiableList(this.fileFilters); + } + + /** + * {@inheritDoc} + */ + public boolean removeFileFilter(IOFileFilter ioFileFilter) { + return this.fileFilters.remove(ioFileFilter); + } + + /** + * {@inheritDoc} + */ + public void setFileFilters(final List fileFilters) { + this.fileFilters = fileFilters; + } + + /** + * {@inheritDoc} + */ + public boolean accept(final File file) { + for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { + IOFileFilter fileFilter = (IOFileFilter) iter.next(); + if (fileFilter.accept(file)) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean accept(final File file, final String name) { + for (Iterator iter = this.fileFilters.iterator(); iter.hasNext();) { + IOFileFilter fileFilter = (IOFileFilter) iter.next(); + if (fileFilter.accept(file, name)) { + return true; + } + } + return false; + } + + /** + * Provide a String representaion of this file filter. + * + * @return a String representaion + */ + 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 0b6fcb9613..15826bcea0 100644 --- a/src/org/apache/commons/io/filefilter/PrefixFileFilter.java +++ b/src/org/apache/commons/io/filefilter/PrefixFileFilter.java @@ -1,197 +1,197 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * For example, to print all files and directories in the - * current directory whose name starts with Test: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( new PrefixFileFilter("Test") );
- * for ( int i = 0; i < files.length; i++ ) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @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. - *

- * 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. - *

- * 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 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 prefixes, IOCase caseSensitivity) { - if (prefixes == null) { - throw new IllegalArgumentException("The list of prefixes must not be null"); - } - this.prefixes = (String[]) 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 - */ - 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 - */ - 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 - */ - 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. + *

+ * For example, to print all files and directories in the + * current directory whose name starts with Test: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( new PrefixFileFilter("Test") );
+ * for ( int i = 0; i < files.length; i++ ) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @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. + *

+ * 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. + *

+ * 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 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 prefixes, IOCase caseSensitivity) { + if (prefixes == null) { + throw new IllegalArgumentException("The list of prefixes must not be null"); + } + this.prefixes = (String[]) 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 + */ + 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 + */ + 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 + */ + 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 b49a1bd3b5..d47d834a0f 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). - *

- * See java.util.regex.Pattern for regex matching rules - *

- * - *

- * e.g. - *

- * 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]);
- * }
- * 
- * - * @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). + *

+ * See java.util.regex.Pattern for regex matching rules + *

+ * + *

+ * e.g. + *

+ * 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]);
+ * }
+ * 
+ * + * @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 614e4243f1..1e97a42155 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. - *

- * For example, to print all files and directories in the - * current directory whose size is greater than 1 MB: - * - *

- * 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]);
- * }
- * 
- * - * @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. - *

- * If size equals threshold and smaller files are required, - * file IS NOT selected. - * If size equals threshold and larger files are required, - * file IS 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. + *

+ * For example, to print all files and directories in the + * current directory whose size is greater than 1 MB: + * + *

+ * 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]);
+ * }
+ * 
+ * + * @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. + *

+ * If size equals threshold and smaller files are required, + * file IS NOT selected. + * If size equals threshold and larger files are required, + * file IS 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 bee34402c4..192e9b5951 100644 --- a/src/org/apache/commons/io/filefilter/SuffixFileFilter.java +++ b/src/org/apache/commons/io/filefilter/SuffixFileFilter.java @@ -1,198 +1,198 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * For example, to retrieve and print all *.java files - * in the current directory: - * - *

- * File dir = new File(".");
- * String[] files = dir.list( new SuffixFileFilter(".java") );
- * for (int i = 0; i < files.length; i++) {
- *     System.out.println(files[i]);
- * }
- * 
- * - * @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. - *

- * 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. - *

- * 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 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 suffixes, IOCase caseSensitivity) { - if (suffixes == null) { - throw new IllegalArgumentException("The list of suffixes must not be null"); - } - this.suffixes = (String[]) 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 - */ - 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 - */ - 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 - */ - 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. + *

+ * For example, to retrieve and print all *.java files + * in the current directory: + * + *

+ * File dir = new File(".");
+ * String[] files = dir.list( new SuffixFileFilter(".java") );
+ * for (int i = 0; i < files.length; i++) {
+ *     System.out.println(files[i]);
+ * }
+ * 
+ * + * @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. + *

+ * 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. + *

+ * 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 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 suffixes, IOCase caseSensitivity) { + if (suffixes == null) { + throw new IllegalArgumentException("The list of suffixes must not be null"); + } + this.suffixes = (String[]) 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 + */ + 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 + */ + 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 + */ + 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 be1b13a7e6..9eb1e756c8 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 791fe985bf..2946980271 100644 --- a/src/org/apache/commons/io/filefilter/WildcardFileFilter.java +++ b/src/org/apache/commons/io/filefilter/WildcardFileFilter.java @@ -1,196 +1,196 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * This filter selects files and directories based on one or more wildcards. - * Testing is case-sensitive by default, but this can be configured. - *

- * 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. - *

- * For example: - *

- * 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]);
- * }
- * 
- * - * @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. - *

- * 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. - *

- * 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 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 wildcards, IOCase caseSensitivity) { - if (wildcards == null) { - throw new IllegalArgumentException("The wildcard list must not be null"); - } - this.wildcards = (String[]) 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 - */ - 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 - */ - 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 - */ - 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. + *

+ * This filter selects files and directories based on one or more wildcards. + * Testing is case-sensitive by default, but this can be configured. + *

+ * 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. + *

+ * For example: + *

+ * 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]);
+ * }
+ * 
+ * + * @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. + *

+ * 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. + *

+ * 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 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 wildcards, IOCase caseSensitivity) { + if (wildcards == null) { + throw new IllegalArgumentException("The wildcard list must not be null"); + } + this.wildcards = (String[]) 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 + */ + 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 + */ + 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 + */ + 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/WildcardFilter.java b/src/org/apache/commons/io/filefilter/WildcardFilter.java index 2a6e0dde03..e9ec025861 100644 --- a/src/org/apache/commons/io/filefilter/WildcardFilter.java +++ b/src/org/apache/commons/io/filefilter/WildcardFilter.java @@ -1,140 +1,140 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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; - -/** - * Filters files using the supplied wildcards. - *

- * This filter selects files, but not directories, based on one or more wildcards - * and using case-sensitive comparison. - *

- * 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. - * See {@link FilenameUtils#wildcardMatch} for more information. - *

- * For example: - *

- * File dir = new File(".");
- * FileFilter fileFilter = new WildcardFilter("*test*.java~*~");
- * File[] files = dir.listFiles(fileFilter);
- * for (int i = 0; i < files.length; i++) {
- *   System.out.println(files[i]);
- * }
- * 
- * - * @author Jason Anderson - * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $ - * @since Commons IO 1.1 - * @deprecated Use WilcardFileFilter. Deprecated as this class performs directory - * filtering which it shouldn't do, but that can't be removed due to compatability. - */ -public class WildcardFilter extends AbstractFileFilter implements Serializable { - - /** The wildcards that will be used to match filenames. */ - private final String[] wildcards; - - /** - * 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 WildcardFilter(String wildcard) { - if (wildcard == null) { - throw new IllegalArgumentException("The wildcard must not be null"); - } - this.wildcards = new String[] { wildcard }; - } - - /** - * Construct a new case-sensitive wildcard filter for an array of wildcards. - * - * @param wildcards the array of wildcards to match - * @throws IllegalArgumentException if the pattern array is null - */ - public WildcardFilter(String[] wildcards) { - if (wildcards == null) { - throw new IllegalArgumentException("The wildcard array must not be null"); - } - this.wildcards = wildcards; - } - - /** - * Construct a new case-sensitive wildcard filter for a list of wildcards. - * - * @param wildcards the list of wildcards to match - * @throws IllegalArgumentException if the pattern list is null - * @throws ClassCastException if the list does not contain Strings - */ - public WildcardFilter(List wildcards) { - if (wildcards == null) { - throw new IllegalArgumentException("The wildcard list must not be null"); - } - this.wildcards = (String[]) wildcards.toArray(new String[wildcards.size()]); - } - - //----------------------------------------------------------------------- - /** - * 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 - */ - public boolean accept(File dir, String name) { - if (dir != null && new File(dir, name).isDirectory()) { - return false; - } - - for (int i = 0; i < wildcards.length; i++) { - if (FilenameUtils.wildcardMatch(name, wildcards[i])) { - 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 - */ - public boolean accept(File file) { - if (file.isDirectory()) { - return false; - } - - for (int i = 0; i < wildcards.length; i++) { - if (FilenameUtils.wildcardMatch(file.getName(), wildcards[i])) { - return true; - } - } - - 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; +import java.util.List; + +import org.apache.commons.io.FilenameUtils; + +/** + * Filters files using the supplied wildcards. + *

+ * This filter selects files, but not directories, based on one or more wildcards + * and using case-sensitive comparison. + *

+ * 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. + * See {@link FilenameUtils#wildcardMatch} for more information. + *

+ * For example: + *

+ * File dir = new File(".");
+ * FileFilter fileFilter = new WildcardFilter("*test*.java~*~");
+ * File[] files = dir.listFiles(fileFilter);
+ * for (int i = 0; i < files.length; i++) {
+ *   System.out.println(files[i]);
+ * }
+ * 
+ * + * @author Jason Anderson + * @version $Revision: 606381 $ $Date: 2007-12-22 02:03:16 +0000 (Sat, 22 Dec 2007) $ + * @since Commons IO 1.1 + * @deprecated Use WilcardFileFilter. Deprecated as this class performs directory + * filtering which it shouldn't do, but that can't be removed due to compatability. + */ +public class WildcardFilter extends AbstractFileFilter implements Serializable { + + /** The wildcards that will be used to match filenames. */ + private final String[] wildcards; + + /** + * 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 WildcardFilter(String wildcard) { + if (wildcard == null) { + throw new IllegalArgumentException("The wildcard must not be null"); + } + this.wildcards = new String[] { wildcard }; + } + + /** + * Construct a new case-sensitive wildcard filter for an array of wildcards. + * + * @param wildcards the array of wildcards to match + * @throws IllegalArgumentException if the pattern array is null + */ + public WildcardFilter(String[] wildcards) { + if (wildcards == null) { + throw new IllegalArgumentException("The wildcard array must not be null"); + } + this.wildcards = wildcards; + } + + /** + * Construct a new case-sensitive wildcard filter for a list of wildcards. + * + * @param wildcards the list of wildcards to match + * @throws IllegalArgumentException if the pattern list is null + * @throws ClassCastException if the list does not contain Strings + */ + public WildcardFilter(List wildcards) { + if (wildcards == null) { + throw new IllegalArgumentException("The wildcard list must not be null"); + } + this.wildcards = (String[]) wildcards.toArray(new String[wildcards.size()]); + } + + //----------------------------------------------------------------------- + /** + * 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 + */ + public boolean accept(File dir, String name) { + if (dir != null && new File(dir, name).isDirectory()) { + return false; + } + + for (int i = 0; i < wildcards.length; i++) { + if (FilenameUtils.wildcardMatch(name, wildcards[i])) { + 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 + */ + public boolean accept(File file) { + if (file.isDirectory()) { + return false; + } + + for (int i = 0; i < wildcards.length; i++) { + if (FilenameUtils.wildcardMatch(file.getName(), wildcards[i])) { + return true; + } + } + + return false; + } + +} diff --git a/src/org/apache/commons/io/filefilter/package.html b/src/org/apache/commons/io/filefilter/package.html index 7a45f251de..e495e95213 100644 --- a/src/org/apache/commons/io/filefilter/package.html +++ b/src/org/apache/commons/io/filefilter/package.html @@ -1,143 +1,143 @@ - - - - -

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.

-

These filter can be used to list files or in {@link java.awt.FileDialog}, -for example.

- -

There are a number of 'primitive' filters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DirectoryFilterOnly accept directories
PrefixFileFilterFilter based on a prefix
SuffixFileFilterFilter based on a suffix
NameFileFilterFilter based on a filename
WildcardFileFilterFilter based on wildcards
AgeFileFilterFilter based on last modified time of file
SizeFileFilterFilter based on file size
- -

And there are five 'boolean' filters:

- - - - - - - - - - - - - - - - - - - - - - - - - -
TrueFileFilterAccept all files
FalseFileFilterAccept no files
NotFileFilterApplies a logical NOT to an existing filter
AndFileFilterCombines two filters using a logical AND
OrFileFilterCombines two filter using a logical OR
- -

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":

- -
-  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]);
-  }
-
- -

This package also contains a utility class: -FileFilterUtils. 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:

-
-  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]);
-  }
-
-

There are a few other goodies in that class so please have a look at the -documentation in detail.

- - + + + + +

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.

+

These filter can be used to list files or in {@link java.awt.FileDialog}, +for example.

+ +

There are a number of 'primitive' filters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectoryFilterOnly accept directories
PrefixFileFilterFilter based on a prefix
SuffixFileFilterFilter based on a suffix
NameFileFilterFilter based on a filename
WildcardFileFilterFilter based on wildcards
AgeFileFilterFilter based on last modified time of file
SizeFileFilterFilter based on file size
+ +

And there are five 'boolean' filters:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TrueFileFilterAccept all files
FalseFileFilterAccept no files
NotFileFilterApplies a logical NOT to an existing filter
AndFileFilterCombines two filters using a logical AND
OrFileFilterCombines two filter using a logical OR
+ +

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":

+ +
+  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]);
+  }
+
+ +

This package also contains a utility class: +FileFilterUtils. 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:

+
+  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]);
+  }
+
+

There are a few other goodies in that class so please have a look at the +documentation in detail.

+ + diff --git a/src/org/apache/commons/io/input/AutoCloseInputStream.java b/src/org/apache/commons/io/input/AutoCloseInputStream.java index bb62358f7f..401dfd667e 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. - *

- * 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. - *

- * This method is automatically called by the read methods when the end - * of input has been reached. - *

- * 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. + *

+ * 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. + *

+ * This method is automatically called by the read methods when the end + * of input has been reached. + *

+ * 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 6ee11d87d1..1be5282102 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. - *

- * Note: 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 null - */ - 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 true - */ - 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. + *

+ * Note: 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 null + */ + 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 true + */ + 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 13d0489465..334ff895f9 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 - * ClassLoader rather than the system default. - *

- * 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 + * ClassLoader rather than the system default. + *

+ * 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 2058beeb2a..59641046b1 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. - *

- * 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. + *

+ * 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 86c83c9030..b0a7ccdf64 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. - *

- * 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 - * null 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. + *

+ * 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 + * null 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 2782276c88..2e5da27875 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. - *

- * 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. - *

- * NOTE: From v1.3 this method throws an ArithmeticException if the - * count is greater than can be expressed by an int. - * See {@link #getByteCount()} for a method using a long. - * - * @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. - *

- * NOTE: From v1.3 this method throws an ArithmeticException if the - * count is greater than can be expressed by an int. - * See {@link #resetByteCount()} for a method using a long. - * - * @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. - *

- * NOTE: This method is an alternative for getCount() - * 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. - *

- * NOTE: This method is an alternative for resetCount() - * 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. + *

+ * 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. + *

+ * NOTE: From v1.3 this method throws an ArithmeticException if the + * count is greater than can be expressed by an int. + * See {@link #getByteCount()} for a method using a long. + * + * @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. + *

+ * NOTE: From v1.3 this method throws an ArithmeticException if the + * count is greater than can be expressed by an int. + * See {@link #resetByteCount()} for a method using a long. + * + * @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. + *

+ * NOTE: This method is an alternative for getCount() + * 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. + *

+ * NOTE: This method is an alternative for resetCount() + * 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 1ae8889169..7ef592cf93 100644 --- a/src/org/apache/commons/io/input/DemuxInputStream.java +++ b/src/org/apache/commons/io/input/DemuxInputStream.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.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 Peter Donald - * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $ - */ -public class DemuxInputStream - extends InputStream -{ - private InheritableThreadLocal m_streams = new InheritableThreadLocal(); - - /** - * 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 - */ - 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 - */ - 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 (InputStream)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 Peter Donald + * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $ + */ +public class DemuxInputStream + extends InputStream +{ + private InheritableThreadLocal m_streams = new InheritableThreadLocal(); + + /** + * 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 + */ + 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 + */ + 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 (InputStream)m_streams.get(); + } +} diff --git a/src/org/apache/commons/io/input/NullInputStream.java b/src/org/apache/commons/io/input/NullInputStream.java index 7cee2c6d09..96cc6c42da 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. - *

- * This implementation provides a light weight - * object for testing with an {@link InputStream} - * where the contents don't matter. - *

- * 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. - *

- * 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 processByte() and - * processBytes() methods can be implemented to generate - * data, for example: - * - *

- *  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
- *          }
- *      }
- *  }
- * 
- * - * @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 mark() 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 mark is supported. - * - * @return Whether mark is supported or not. - */ - public boolean markSupported() { - return markSupported; - } - - /** - * Read a byte. - * - * @return Either The byte value returned by processByte() - * or -1 if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 read() method. - *

- * 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 read(byte[], offset, length) - * method. - *

- * 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 -1 if throwEofException is - * set to false - * @throws EOFException if throwEofException is set - * to true. - */ - 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. + *

+ * This implementation provides a light weight + * object for testing with an {@link InputStream} + * where the contents don't matter. + *

+ * 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. + *

+ * 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 processByte() and + * processBytes() methods can be implemented to generate + * data, for example: + * + *

+ *  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
+ *          }
+ *      }
+ *  }
+ * 
+ * + * @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 mark() 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 mark is supported. + * + * @return Whether mark is supported or not. + */ + public boolean markSupported() { + return markSupported; + } + + /** + * Read a byte. + * + * @return Either The byte value returned by processByte() + * or -1 if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 read() method. + *

+ * 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 read(byte[], offset, length) + * method. + *

+ * 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 -1 if throwEofException is + * set to false + * @throws EOFException if throwEofException is set + * to true. + */ + 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 159e390216..95f7400f2c 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. - *

- * This implementation provides a light weight - * object for testing with an {@link Reader} - * where the contents don't matter. - *

- * 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. - *

- * 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 processChar() and - * processChars() methods can be implemented to generate - * data, for example: - * - *

- *  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
- *          }
- *      }
- *  }
- * 
- * - * @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 mark() 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 mark is supported. - * - * @return Whether mark is supported or not. - */ - public boolean markSupported() { - return markSupported; - } - - /** - * Read a character. - * - * @return Either The character value returned by processChar() - * or -1 if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 -1 - * if the end of file has been reached and - * throwEofException is set to false. - * @throws EOFException if the end of file is reached and - * throwEofException is set to true. - * @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 read() method. - *

- * 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 read(char[], offset, length) - * method. - *

- * 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 -1 if throwEofException is - * set to false - * @throws EOFException if throwEofException is set - * to true. - */ - 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. + *

+ * This implementation provides a light weight + * object for testing with an {@link Reader} + * where the contents don't matter. + *

+ * 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. + *

+ * 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 processChar() and + * processChars() methods can be implemented to generate + * data, for example: + * + *

+ *  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
+ *          }
+ *      }
+ *  }
+ * 
+ * + * @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 mark() 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 mark is supported. + * + * @return Whether mark is supported or not. + */ + public boolean markSupported() { + return markSupported; + } + + /** + * Read a character. + * + * @return Either The character value returned by processChar() + * or -1 if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 -1 + * if the end of file has been reached and + * throwEofException is set to false. + * @throws EOFException if the end of file is reached and + * throwEofException is set to true. + * @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 read() method. + *

+ * 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 read(char[], offset, length) + * method. + *

+ * 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 -1 if throwEofException is + * set to false + * @throws EOFException if throwEofException is set + * to true. + */ + 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 a08ad92d04..5b2edd5820 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. - *

- * 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 read() 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 read(byte[]) 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 read(byte[], int, int) 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 skip(long) 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 available() 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 close() method. - * @throws IOException if an I/O error occurs - */ - public void close() throws IOException { - in.close(); - } - - /** - * Invokes the delegate's mark(int) method. - * @param idx read ahead limit - */ - public synchronized void mark(int idx) { - in.mark(idx); - } - - /** - * Invokes the delegate's reset() method. - * @throws IOException if an I/O error occurs - */ - public synchronized void reset() throws IOException { - in.reset(); - } - - /** - * Invokes the delegate's markSupported() 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. + *

+ * 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 read() 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 read(byte[]) 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 read(byte[], int, int) 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 skip(long) 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 available() 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 close() method. + * @throws IOException if an I/O error occurs + */ + public void close() throws IOException { + in.close(); + } + + /** + * Invokes the delegate's mark(int) method. + * @param idx read ahead limit + */ + public synchronized void mark(int idx) { + in.mark(idx); + } + + /** + * Invokes the delegate's reset() method. + * @throws IOException if an I/O error occurs + */ + public synchronized void reset() throws IOException { + in.reset(); + } + + /** + * Invokes the delegate's markSupported() 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 d55290f5a4..81e113cccb 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. - *

- * 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 read() 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 read(char[]) 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 read(char[], int, int) 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 skip(long) 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 ready() 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 close() method. - * @throws IOException if an I/O error occurs - */ - public void close() throws IOException { - in.close(); - } - - /** - * Invokes the delegate's mark(int) 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 reset() method. - * @throws IOException if an I/O error occurs - */ - public synchronized void reset() throws IOException { - in.reset(); - } - - /** - * Invokes the delegate's markSupported() 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. + *

+ * 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 read() 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 read(char[]) 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 read(char[], int, int) 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 skip(long) 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 ready() 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 close() method. + * @throws IOException if an I/O error occurs + */ + public void close() throws IOException { + in.close(); + } + + /** + * Invokes the delegate's mark(int) 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 reset() method. + * @throws IOException if an I/O error occurs + */ + public synchronized void reset() throws IOException { + in.reset(); + } + + /** + * Invokes the delegate's markSupported() 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 5b65b1eee3..fe2f97eb70 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. - *

- * Origin of code: Avalon Excalibur (IO) - * - * @author Peter Donald - * @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 {@link #readByte()} == 0 - * @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 read() 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 read(byte[] data, int, int) 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 read(byte[] data, int, int) 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 read() 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 skip(int) 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. + *

+ * Origin of code: Avalon Excalibur (IO) + * + * @author Peter Donald + * @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 {@link #readByte()} == 0 + * @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 read() 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 read(byte[] data, int, int) 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 read(byte[] data, int, int) 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 read() 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 skip(int) 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 fed000ed68..acc4649ae0 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. - *

- * 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 true. - * - * @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. + *

+ * 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 true. + * + * @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 9aa8b15bad..440fa0f7ee 100644 --- a/src/org/apache/commons/io/input/package.html +++ b/src/org/apache/commons/io/input/package.html @@ -1,25 +1,25 @@ - - - - -

-This package provides implementations of input classes, such as -InputStream and Reader. -

- - + + + + +

+This package provides implementations of input classes, such as +InputStream and Reader. +

+ + diff --git a/src/org/apache/commons/io/output/ByteArrayOutputStream.java b/src/org/apache/commons/io/output/ByteArrayOutputStream.java index 66d5e8dc35..b42dd3aa2c 100644 --- a/src/org/apache/commons/io/output/ByteArrayOutputStream.java +++ b/src/org/apache/commons/io/output/ByteArrayOutputStream.java @@ -1,308 +1,308 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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. - *

- * The data can be retrieved using toByteArray() and - * toString(). - *

- * Closing a ByteArrayOutputStream has no effect. The methods in - * this class can be called after the stream has been closed without - * generating an IOException. - *

- * 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 Jeremias Maerki - * @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 buffers = new ArrayList(); - /** 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 byte[] buffer - * specified by index. - * - * @param index the index of the buffer required - * @return the buffer - */ - private byte[] getBuffer(int index) { - return (byte[]) 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 - */ - 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 - */ - 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 ByteArrayOutputStream has no effect. The methods in - * this class can be called after the stream has been closed without - * generating an IOException. - * - * @throws IOException never (this method should not declare this exception - * but it has to now due to backwards compatability) - */ - 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() - */ - 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. + *

+ * The data can be retrieved using toByteArray() and + * toString(). + *

+ * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + *

+ * 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 Jeremias Maerki + * @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 buffers = new ArrayList(); + /** 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 byte[] buffer + * specified by index. + * + * @param index the index of the buffer required + * @return the buffer + */ + private byte[] getBuffer(int index) { + return (byte[]) 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 + */ + 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 + */ + 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 ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + * + * @throws IOException never (this method should not declare this exception + * but it has to now due to backwards compatability) + */ + 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() + */ + 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 63f44be405..58a9946024 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. - *

- * 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. + *

+ * 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 b585c0cf4f..8c881c6857 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. - *

- * 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 null 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. + *

+ * 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 null 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 6728828607..392276a917 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. - *

- * 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. - *

- * NOTE: From v1.3 this method throws an ArithmeticException if the - * count is greater than can be expressed by an int. - * See {@link #getByteCount()} for a method using a long. - * - * @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. - *

- * NOTE: From v1.3 this method throws an ArithmeticException if the - * count is greater than can be expressed by an int. - * See {@link #resetByteCount()} for a method using a long. - * - * @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. - *

- * NOTE: This method is an alternative for getCount(). - * 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. - *

- * NOTE: This method is an alternative for resetCount(). - * 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. + *

+ * 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. + *

+ * NOTE: From v1.3 this method throws an ArithmeticException if the + * count is greater than can be expressed by an int. + * See {@link #getByteCount()} for a method using a long. + * + * @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. + *

+ * NOTE: From v1.3 this method throws an ArithmeticException if the + * count is greater than can be expressed by an int. + * See {@link #resetByteCount()} for a method using a long. + * + * @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. + *

+ * NOTE: This method is an alternative for getCount(). + * 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. + *

+ * NOTE: This method is an alternative for resetCount(). + * 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 b8a9e96070..c0f223341d 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. - *

- * 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 Martin Cooper - * @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 memoryOutputStream or - * diskOutputStream. - */ - 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 true if the data is available in memory; - * false 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 null. - * - * @return The data for this output stream, or null 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. - *

- * If the constructor specifying the file is used then it returns that - * same output file, even when threashold has not been reached. - *

- * 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 null is returned. - * - * @return The file for this output stream, or null 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. + *

+ * 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 Martin Cooper + * @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 memoryOutputStream or + * diskOutputStream. + */ + 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 true if the data is available in memory; + * false 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 null. + * + * @return The data for this output stream, or null 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. + *

+ * If the constructor specifying the file is used then it returns that + * same output file, even when threashold has not been reached. + *

+ * 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 null is returned. + * + * @return The file for this output stream, or null 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 0869111183..36edec61da 100644 --- a/src/org/apache/commons/io/output/DemuxOutputStream.java +++ b/src/org/apache/commons/io/output/DemuxOutputStream.java @@ -1,102 +1,102 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 Peter Donald - * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $ - */ -public class DemuxOutputStream - extends OutputStream -{ - private InheritableThreadLocal m_streams = new InheritableThreadLocal(); - - /** - * 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 - */ - 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 - */ - 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 - */ - 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 (OutputStream)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 Peter Donald + * @version $Revision: 437567 $ $Date: 2006-08-28 07:39:07 +0100 (Mon, 28 Aug 2006) $ + */ +public class DemuxOutputStream + extends OutputStream +{ + private InheritableThreadLocal m_streams = new InheritableThreadLocal(); + + /** + * 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 + */ + 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 + */ + 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 + */ + 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 (OutputStream)m_streams.get(); + } +} diff --git a/src/org/apache/commons/io/output/FileWriterWithEncoding.java b/src/org/apache/commons/io/output/FileWriterWithEncoding.java index a8f89334b2..d5708ad17d 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. - *

- * This class provides a simple alternative to FileWriter - * that allows an encoding to be set. Unfortunately, it cannot subclass - * FileWriter. - *

- * By default, the file will be overwritten, but this may be changed to append. - *

- * 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. - *

- * - * - * @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. + *

+ * This class provides a simple alternative to FileWriter + * that allows an encoding to be set. Unfortunately, it cannot subclass + * FileWriter. + *

+ * By default, the file will be overwritten, but this may be changed to append. + *

+ * 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. + *

+ * + * + * @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 6b10bd2823..1092926cd4 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. - *

- * This class provides a simple alternative to FileWriter - * that will use a lock file to prevent duplicate writes. - *

- * 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 - * java.io.tmpdir. - * The encoding may also be specified, and defaults to the platform default. - * - * @author Scott Sanders - * @author Michael Salmon - * @author Jon S. Stevens - * @author Daniel Rall - * @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. + *

+ * This class provides a simple alternative to FileWriter + * that will use a lock file to prevent duplicate writes. + *

+ * 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 + * java.io.tmpdir. + * The encoding may also be specified, and defaults to the platform default. + * + * @author Scott Sanders + * @author Michael Salmon + * @author Jon S. Stevens + * @author Daniel Rall + * @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 7e3cdaf2c2..5558d30d72 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 /dev/null. - *

- * 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 /dev/null. - * @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 /dev/null. - * @param b The byte to write - */ - public void write(int b) { - //to /dev/null - } - - /** - * Does nothing - output to /dev/null. - * @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 /dev/null. + *

+ * 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 /dev/null. + * @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 /dev/null. + * @param b The byte to write + */ + public void write(int b) { + //to /dev/null + } + + /** + * Does nothing - output to /dev/null. + * @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 aed52aba89..8fd2bf489c 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 /dev/null. - *

- * This Writer 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 /dev/null. - * @param idx The character to write - */ - public void write(int idx) { - //to /dev/null - } - - /** - * Does nothing - output to /dev/null. - * @param chr The characters to write - */ - public void write(char[] chr) { - //to /dev/null - } - - /** - * Does nothing - output to /dev/null. - * @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 /dev/null. - * @param str The string to write - */ - public void write(String str) { - //to /dev/null - } - - /** - * Does nothing - output to /dev/null. - * @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 /dev/null. + *

+ * This Writer 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 /dev/null. + * @param idx The character to write + */ + public void write(int idx) { + //to /dev/null + } + + /** + * Does nothing - output to /dev/null. + * @param chr The characters to write + */ + public void write(char[] chr) { + //to /dev/null + } + + /** + * Does nothing - output to /dev/null. + * @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 /dev/null. + * @param str The string to write + */ + public void write(String str) { + //to /dev/null + } + + /** + * Does nothing - output to /dev/null. + * @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 b63d723171..9376d6ce4d 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 write(int) 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 write(byte[]) 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 write(byte[]) 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 flush() method. - * @throws IOException if an I/O error occurs - */ - public void flush() throws IOException { - out.flush(); - } - - /** - * Invokes the delegate's close() 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 write(int) 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 write(byte[]) 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 write(byte[]) 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 flush() method. + * @throws IOException if an I/O error occurs + */ + public void flush() throws IOException { + out.flush(); + } + + /** + * Invokes the delegate's close() 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 fbec628851..8b617d8524 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 write(int) 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 write(char[]) 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 write(char[], int, int) 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 write(String) 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 write(String) 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 flush() method. - * @throws IOException if an I/O error occurs - */ - public void flush() throws IOException { - out.flush(); - } - - /** - * Invokes the delegate's close() 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 write(int) 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 write(char[]) 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 write(char[], int, int) 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 write(String) 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 write(String) 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 flush() method. + * @throws IOException if an I/O error occurs + */ + public void flush() throws IOException { + out.flush(); + } + + /** + * Invokes the delegate's close() 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 ee957fb3b1..6f799d19c3 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 fa69a804cd..ca6ede2344 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. - *

- * This class overrides all OutputStream methods. However, these - * overrides ultimately call the corresponding methods in the underlying output - * stream implementation. - *

- * NOTE: This implementation may trigger the event before the threshold - * is actually reached, since it triggers when a pending write operation would - * cause the threshold to be exceeded. - * - * @author Martin Cooper - * - * @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 b.length 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 len bytes from the specified byte array starting at - * offset off 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 true if the threshold has been reached; - * false 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 - * OutputStream 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. + *

+ * This class overrides all OutputStream methods. However, these + * overrides ultimately call the corresponding methods in the underlying output + * stream implementation. + *

+ * NOTE: This implementation may trigger the event before the threshold + * is actually reached, since it triggers when a pending write operation would + * cause the threshold to be exceeded. + * + * @author Martin Cooper + * + * @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 b.length 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 len bytes from the specified byte array starting at + * offset off 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 true if the threshold has been reached; + * false 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 + * OutputStream 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 db2cbce59e..4415613309 100644 --- a/src/org/apache/commons/io/output/package.html +++ b/src/org/apache/commons/io/output/package.html @@ -1,25 +1,25 @@ - - - - -

-This package provides implementations of output classes, such as -OutputStream and Writer. -

- - + + + + +

+This package provides implementations of output classes, such as +OutputStream and Writer. +

+ + diff --git a/src/org/apache/commons/io/overview.html b/src/org/apache/commons/io/overview.html index 31311b5e96..5f2ff1e77a 100644 --- a/src/org/apache/commons/io/overview.html +++ b/src/org/apache/commons/io/overview.html @@ -1,32 +1,32 @@ - - - - -

-The commons-io component contains utility classes, -filters, streams, readers and writers. -

-

-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. -

- - + + + + +

+The commons-io component contains utility classes, +filters, streams, readers and writers. +

+

+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. +

+ + diff --git a/src/org/apache/commons/io/package.html b/src/org/apache/commons/io/package.html index e5ba9b0aa9..914a0a60c1 100644 --- a/src/org/apache/commons/io/package.html +++ b/src/org/apache/commons/io/package.html @@ -1,47 +1,47 @@ - - - - -

-This package defines utility classes for working with streams, readers, -writers and files. The most commonly used classes are described here: -

-

-IOUtils is the most frequently used class. -It provides operations to read, write, copy and close streams. -

-

-FileUtils provides operations based around the JDK File class. -These include reading, writing, copying, comparing and deleting. -

-

-FilenameUtils 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. -

-

-FileSystemUtils 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. -

-

-EndianUtils swaps data between Big-Endian and Little-Endian formats. -

- - + + + + +

+This package defines utility classes for working with streams, readers, +writers and files. The most commonly used classes are described here: +

+

+IOUtils is the most frequently used class. +It provides operations to read, write, copy and close streams. +

+

+FileUtils provides operations based around the JDK File class. +These include reading, writing, copying, comparing and deleting. +

+

+FilenameUtils 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. +

+

+FileSystemUtils 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. +

+

+EndianUtils swaps data between Big-Endian and Little-Endian formats. +

+ + diff --git a/src/org/apache/james/mime4j/AbstractContentHandler.java b/src/org/apache/james/mime4j/AbstractContentHandler.java index 06c9b90a05..3df6fb76b1 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 ContentHandler with default implementations of all - * the methods of the ContentHandler 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 ContentHandler with default implementations of all + * the methods of the ContentHandler 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 515658a8df..ca447b0325 100644 --- a/src/org/apache/james/mime4j/BodyDescriptor.java +++ b/src/org/apache/james/mime4j/BodyDescriptor.java @@ -1,410 +1,410 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Encapsulates the values of the MIME-specific header fields - * (which starts with Content-). - * - * - * @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 parameters = new HashMap(); - private boolean contentTypeSet = false; - private boolean contentTransferEncSet = false; - - /** - * Creates a new root BodyDescriptor instance. - */ - public BodyDescriptor() { - this(null); - } - - /** - * Creates a new BodyDescriptor instance. - * - * @param parent the descriptor of the parent or null 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 Content- 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 params = getHeaderParams(sb.toString()); - - String main = (String) 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 = (String) params.get("boundary"); - - if (main != null - && ((main.startsWith("multipart/") && b != null) - || !main.startsWith("multipart/"))) { - - mimeType = main; - } - - if (isMultipart()) { - boundary = b; - } - - String c = (String) 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 getHeaderParams(String headerValue) { - Map result = new HashMap(); - - // 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; - - 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 - * - * @return - */ - public boolean isMessage() { - return mimeType.equals("message/rfc822"); - } - - /** - * Retrun true if the BodyDescripotro belogns to a multipart - * - * @return - */ - public boolean isMultipart() { - return mimeType.startsWith("multipart/"); - } - - /** - * Return the MimeType - * - * @return mimeType - */ - public String getMimeType() { - return mimeType; - } - - /** - * Return the boundary - * - * @return boundary - */ - public String getBoundary() { - return boundary; - } - - /** - * Return the charset - * - * @return charset - */ - public String getCharset() { - return charset; - } - - /** - * Return all parameters for the BodyDescriptor - * - * @return parameters - */ - public Map getParameters() { - return parameters; - } - - /** - * Return the TransferEncoding - * - * @return transferEncoding - */ - public String getTransferEncoding() { - return transferEncoding; - } - - /** - * Return true if it's base64 encoded - * - * @return - * - */ - public boolean isBase64Encoded() { - return "base64".equals(transferEncoding); - } - - /** - * Return true if it's quoted-printable - * @return - */ - public boolean isQuotedPrintableEncoded() { - return "quoted-printable".equals(transferEncoding); - } - - 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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Encapsulates the values of the MIME-specific header fields + * (which starts with Content-). + * + * + * @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 parameters = new HashMap(); + private boolean contentTypeSet = false; + private boolean contentTransferEncSet = false; + + /** + * Creates a new root BodyDescriptor instance. + */ + public BodyDescriptor() { + this(null); + } + + /** + * Creates a new BodyDescriptor instance. + * + * @param parent the descriptor of the parent or null 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 Content- 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 params = getHeaderParams(sb.toString()); + + String main = (String) 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 = (String) params.get("boundary"); + + if (main != null + && ((main.startsWith("multipart/") && b != null) + || !main.startsWith("multipart/"))) { + + mimeType = main; + } + + if (isMultipart()) { + boundary = b; + } + + String c = (String) 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 getHeaderParams(String headerValue) { + Map result = new HashMap(); + + // 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; + + 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 + * + * @return + */ + public boolean isMessage() { + return mimeType.equals("message/rfc822"); + } + + /** + * Retrun true if the BodyDescripotro belogns to a multipart + * + * @return + */ + public boolean isMultipart() { + return mimeType.startsWith("multipart/"); + } + + /** + * Return the MimeType + * + * @return mimeType + */ + public String getMimeType() { + return mimeType; + } + + /** + * Return the boundary + * + * @return boundary + */ + public String getBoundary() { + return boundary; + } + + /** + * Return the charset + * + * @return charset + */ + public String getCharset() { + return charset; + } + + /** + * Return all parameters for the BodyDescriptor + * + * @return parameters + */ + public Map getParameters() { + return parameters; + } + + /** + * Return the TransferEncoding + * + * @return transferEncoding + */ + public String getTransferEncoding() { + return transferEncoding; + } + + /** + * Return true if it's base64 encoded + * + * @return + * + */ + public boolean isBase64Encoded() { + return "base64".equals(transferEncoding); + } + + /** + * Return true if it's quoted-printable + * @return + */ + public boolean isQuotedPrintableEncoded() { + return "quoted-printable".equals(transferEncoding); + } + + public String toString() { + return mimeType; + } +} diff --git a/src/org/apache/james/mime4j/CloseShieldInputStream.java b/src/org/apache/james/mime4j/CloseShieldInputStream.java index 94995d1102..3acc98f7c4 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 946c894017..f8213a0d3c 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; - -/** - *

- * Receives notifications of the content of a plain RFC822 or MIME message. - * Implement this interface and register an instance of that implementation - * with a MimeStreamParser instance using its - * {@link org.apache.james.mime4j.MimeStreamParser#setContentHandler(ContentHandler)} - * method. The parser uses the ContentHandler instance to report - * basic message-related events like the start and end of the body of a - * part in a multipart MIME entity. - *

- *

- * Events will be generated in the order the corresponding elements occur in - * the message stream parsed by the parser. E.g.: - *

- *      startMessage()
- *          startHeader()
- *              field(...)
- *              field(...)
- *              ...
- *          endHeader()
- *          startMultipart()
- *              preamble(...)
- *              startBodyPart()
- *                  startHeader()
- *                      field(...)
- *                      field(...)
- *                      ...
- *                  endHeader()
- *                  body()
- *              endBodyPart()
- *              startBodyPart()
- *                  startHeader()
- *                      field(...)
- *                      field(...)
- *                      ...
- *                  endHeader()
- *                  body()
- *              endBodyPart()
- *              epilogue(...)
- *          endMultipart()
- *      endMessage()
- * 
- * The above shows an example of a MIME message consisting of a multipart - * body containing two body parts. - *

- *

- * 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. - *

- * - * - * @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 - * multipart/* 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 - * (Field-Name: field value). 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 multipart/* 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 multipart/* 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 Content-Type and - * Content-Transfer-Encoding 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 bd - * 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 raw 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; + +/** + *

+ * Receives notifications of the content of a plain RFC822 or MIME message. + * Implement this interface and register an instance of that implementation + * with a MimeStreamParser instance using its + * {@link org.apache.james.mime4j.MimeStreamParser#setContentHandler(ContentHandler)} + * method. The parser uses the ContentHandler instance to report + * basic message-related events like the start and end of the body of a + * part in a multipart MIME entity. + *

+ *

+ * Events will be generated in the order the corresponding elements occur in + * the message stream parsed by the parser. E.g.: + *

+ *      startMessage()
+ *          startHeader()
+ *              field(...)
+ *              field(...)
+ *              ...
+ *          endHeader()
+ *          startMultipart()
+ *              preamble(...)
+ *              startBodyPart()
+ *                  startHeader()
+ *                      field(...)
+ *                      field(...)
+ *                      ...
+ *                  endHeader()
+ *                  body()
+ *              endBodyPart()
+ *              startBodyPart()
+ *                  startHeader()
+ *                      field(...)
+ *                      field(...)
+ *                      ...
+ *                  endHeader()
+ *                  body()
+ *              endBodyPart()
+ *              epilogue(...)
+ *          endMultipart()
+ *      endMessage()
+ * 
+ * The above shows an example of a MIME message consisting of a multipart + * body containing two body parts. + *

+ *

+ * 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. + *

+ * + * + * @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 + * multipart/* 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 + * (Field-Name: field value). 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 multipart/* 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 multipart/* 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 Content-Type and + * Content-Transfer-Encoding 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 bd + * 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 raw 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 7d5009ca57..01a7f5db3f 100644 --- a/src/org/apache/james/mime4j/EOLConvertingInputStream.java +++ b/src/org/apache/james/mime4j/EOLConvertingInputStream.java @@ -1,108 +1,108 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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 \r - * bytes not followed by \n and \n not - * preceded by \r to \r\n. - * - * - * @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; - - /** - * Creates a new EOLConvertingInputStream - * instance converting bytes in the given InputStream. - * The flag CONVERT_BOTH is the default. - * - * @param in the InputStream to read from. - */ - public EOLConvertingInputStream(InputStream in) { - this(in, CONVERT_BOTH); - } - /** - * Creates a new EOLConvertingInputStream - * instance converting bytes in the given InputStream. - * - * @param in the InputStream to read from. - * @param flags one of CONVERT_CR, CONVERT_LF or - * CONVERT_BOTH. - */ - public EOLConvertingInputStream(InputStream in, int flags) { - super(); - - this.in = new PushbackInputStream(in, 2); - this.flags = flags; - } - - /** - * Closes the underlying stream. - * - * @throws IOException on I/O errors. - */ - public void close() throws IOException { - in.close(); - } - - /** - * @see java.io.InputStream#read() - */ - public int read() throws IOException { - int b = in.read(); - - if (b == -1) { - return -1; - } - - if ((flags & CONVERT_CR) != 0 && b == '\r') { - int c = in.read(); - if (c != -1) { - in.unread(c); - } - if (c != '\n') { - in.unread('\n'); - } - } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') { - b = '\r'; - in.unread('\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 \r + * bytes not followed by \n and \n not + * preceded by \r to \r\n. + * + * + * @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; + + /** + * Creates a new EOLConvertingInputStream + * instance converting bytes in the given InputStream. + * The flag CONVERT_BOTH is the default. + * + * @param in the InputStream to read from. + */ + public EOLConvertingInputStream(InputStream in) { + this(in, CONVERT_BOTH); + } + /** + * Creates a new EOLConvertingInputStream + * instance converting bytes in the given InputStream. + * + * @param in the InputStream to read from. + * @param flags one of CONVERT_CR, CONVERT_LF or + * CONVERT_BOTH. + */ + public EOLConvertingInputStream(InputStream in, int flags) { + super(); + + this.in = new PushbackInputStream(in, 2); + this.flags = flags; + } + + /** + * Closes the underlying stream. + * + * @throws IOException on I/O errors. + */ + public void close() throws IOException { + in.close(); + } + + /** + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + int b = in.read(); + + if (b == -1) { + return -1; + } + + if ((flags & CONVERT_CR) != 0 && b == '\r') { + int c = in.read(); + if (c != -1) { + in.unread(c); + } + if (c != '\n') { + in.unread('\n'); + } + } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') { + b = '\r'; + in.unread('\n'); + } + + previous = b; + + return b; + } + +} diff --git a/src/org/apache/james/mime4j/MimeBoundaryInputStream.java b/src/org/apache/james/mime4j/MimeBoundaryInputStream.java index 0fffb7820b..75d1bf7fcd 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 true 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 true if there are more parts in the underlying - * stream, false otherwise. - */ - public boolean hasMoreParts() { - return moreParts; - } - - /** - * Determines if the parent stream has reached EOF - * - * @return true if EOF has been reached for the parent stream, - * false 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 true 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 true if there are more parts in the underlying + * stream, false otherwise. + */ + public boolean hasMoreParts() { + return moreParts; + } + + /** + * Determines if the parent stream has reached EOF + * + * @return true if EOF has been reached for the parent stream, + * false 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 2e486adc33..5f2d099143 100644 --- a/src/org/apache/james/mime4j/MimeStreamParser.java +++ b/src/org/apache/james/mime4j/MimeStreamParser.java @@ -1,320 +1,320 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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.util.BitSet; -import java.util.LinkedList; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.james.mime4j.decoder.Base64InputStream; -import org.apache.james.mime4j.decoder.QuotedPrintableInputStream; - -/** - *

- * Parses MIME (or RFC822) message streams of bytes or characters and reports - * parsing events to a ContentHandler instance. - *

- *

- * Typical usage:
- *

- *      ContentHandler handler = new MyHandler();
- *      MimeStreamParser parser = new MimeStreamParser();
- *      parser.setContentHandler(handler);
- *      parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
- * 
- * NOTE: All lines must end with CRLF - * (\r\n). 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 bodyDescriptors = new LinkedList(); - private ContentHandler handler = null; - private boolean raw = 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 MimeStreamParser 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 true if in raw mode, false - * 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 true enables raw mode, false - * 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()) { - 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(); - } - } - - 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 BodyDescriptor 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 ContentHandler to use when reporting - * parsing events. - * - * @param h the ContentHandler. - */ - 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 java.io.IOException; +import java.io.InputStream; +import java.util.BitSet; +import java.util.LinkedList; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.james.mime4j.decoder.Base64InputStream; +import org.apache.james.mime4j.decoder.QuotedPrintableInputStream; + +/** + *

+ * Parses MIME (or RFC822) message streams of bytes or characters and reports + * parsing events to a ContentHandler instance. + *

+ *

+ * Typical usage:
+ *

+ *      ContentHandler handler = new MyHandler();
+ *      MimeStreamParser parser = new MimeStreamParser();
+ *      parser.setContentHandler(handler);
+ *      parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
+ * 
+ * NOTE: All lines must end with CRLF + * (\r\n). 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 bodyDescriptors = new LinkedList(); + private ContentHandler handler = null; + private boolean raw = 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 MimeStreamParser 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 true if in raw mode, false + * 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 true enables raw mode, false + * 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()) { + 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(); + } + } + + 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 BodyDescriptor 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 ContentHandler to use when reporting + * parsing events. + * + * @param h the ContentHandler. + */ + 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 fa848df18f..ebf36dce21 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; - -/** - * InputStream 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 RootInputStream. - * - * @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 \r\n read so far plus 1). - * - * @return the current line number. - */ - public int getLineNumber() { - return lineNumber; - } - - /** - * Truncates this InputStream. 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; + +/** + * InputStream 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 RootInputStream. + * + * @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 \r\n read so far plus 1). + * + * @return the current line number. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Truncates this InputStream. 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 13f1fd2c6b..7d25d08045 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 Content-Type and - * Content-Transfer-Encoding 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 Content-Type and + * Content-Transfer-Encoding 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 930b982c40..9af54951b6 100644 --- a/src/org/apache/james/mime4j/decoder/Base64InputStream.java +++ b/src/org/apache/james/mime4j/decoder/Base64InputStream.java @@ -1,146 +1,146 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * 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 static Log log = LogFactory.getLog(Base64InputStream.class); - - private final InputStream s; - private final ByteQueue byteq = new ByteQueue(3); - private boolean done = false; - - public Base64InputStream(InputStream s) { - this.s = s; - } - - /** - * Closes the underlying stream. - * - * @throws IOException on I/O errors. - */ - public void close() throws IOException { - s.close(); - } - - public int read() throws IOException { - if (byteq.count() == 0) { - fillBuffer(); - if (byteq.count() == 0) { - return -1; - } - } - - byte val = byteq.dequeue(); - if (val >= 0) - return val; - else - return val & 0xFF; - } - - /** - * Retrieve data from the underlying stream, decode it, - * and put the results in the byteq. - * @throws IOException - */ - private void fillBuffer() throws IOException { - byte[] data = new byte[4]; - int pos = 0; - - int i; - while (!done) { - switch (i = s.read()) { - case -1: - if (pos > 0) { - log.warn("Unexpected EOF in MIME parser, dropping " - + pos + " sextets"); - } - return; - case '=': - decodeAndEnqueue(data, pos); - done = true; - break; - default: - byte sX = TRANSLATION[i]; - if (sX < 0) - continue; - data[pos++] = sX; - if (pos == data.length) { - decodeAndEnqueue(data, pos); - return; - } - break; - } - } - } - - private void decodeAndEnqueue(byte[] data, int len) { - int accum = 0; - accum |= data[0] << 18; - accum |= data[1] << 12; - accum |= data[2] << 6; - accum |= data[3]; - - byte b1 = (byte)(accum >>> 16); - byteq.enqueue(b1); - - if (len > 2) { - byte b2 = (byte)((accum >>> 8) & 0xFF); - byteq.enqueue(b2); - - if (len > 3) { - byte b3 = (byte)(accum & 0xFF); - byteq.enqueue(b3); - } - } - } - - 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. * + ****************************************************************/ + +package org.apache.james.mime4j.decoder; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * 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 static Log log = LogFactory.getLog(Base64InputStream.class); + + private final InputStream s; + private final ByteQueue byteq = new ByteQueue(3); + private boolean done = false; + + public Base64InputStream(InputStream s) { + this.s = s; + } + + /** + * Closes the underlying stream. + * + * @throws IOException on I/O errors. + */ + public void close() throws IOException { + s.close(); + } + + public int read() throws IOException { + if (byteq.count() == 0) { + fillBuffer(); + if (byteq.count() == 0) { + return -1; + } + } + + byte val = byteq.dequeue(); + if (val >= 0) + return val; + else + return val & 0xFF; + } + + /** + * Retrieve data from the underlying stream, decode it, + * and put the results in the byteq. + * @throws IOException + */ + private void fillBuffer() throws IOException { + byte[] data = new byte[4]; + int pos = 0; + + int i; + while (!done) { + switch (i = s.read()) { + case -1: + if (pos > 0) { + log.warn("Unexpected EOF in MIME parser, dropping " + + pos + " sextets"); + } + return; + case '=': + decodeAndEnqueue(data, pos); + done = true; + break; + default: + byte sX = TRANSLATION[i]; + if (sX < 0) + continue; + data[pos++] = sX; + if (pos == data.length) { + decodeAndEnqueue(data, pos); + return; + } + break; + } + } + } + + private void decodeAndEnqueue(byte[] data, int len) { + int accum = 0; + accum |= data[0] << 18; + accum |= data[1] << 12; + accum |= data[2] << 6; + accum |= data[3]; + + byte b1 = (byte)(accum >>> 16); + byteq.enqueue(b1); + + if (len > 2) { + byte b2 = (byte)((accum >>> 8) & 0xFF); + byteq.enqueue(b2); + + if (len > 3) { + byte b3 = (byte)(accum & 0xFF); + byteq.enqueue(b3); + } + } + } + + 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 68e7d3380c..6d7ccef524 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 7876ff7002..c391293673 100644 --- a/src/org/apache/james/mime4j/decoder/DecoderUtil.java +++ b/src/org/apache/james/mime4j/decoder/DecoderUtil.java @@ -1,277 +1,277 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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 org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 eb3f09c9a9..3a3a1a4510 100644 --- a/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java +++ b/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java @@ -1,227 +1,227 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * 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 = so far; expecting another 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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * 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 = so far; expecting another 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 dc32caf368..f01194fd10 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. - *

- * The removal order of an UnboundedFifoByteBuffer 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. - *

- * 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. - *

- * Note that this implementation is not synchronized. The following can be - * used to provide synchronized access to your UnboundedFifoByteBuffer: - *

- *   Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
- * 
- *

- * 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: - * - *

-     *   new UnboundedFifoByteBuffer(32);
-     * 
- */ - 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. + *

+ * The removal order of an UnboundedFifoByteBuffer 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. + *

+ * 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. + *

+ * Note that this implementation is not synchronized. The following can be + * used to provide synchronized access to your UnboundedFifoByteBuffer: + *

+ *   Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
+ * 
+ *

+ * 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: + * + *

+     *   new UnboundedFifoByteBuffer(32);
+     * 
+ */ + 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 0e4297c76a..a2e6f992f6 100644 --- a/src/org/apache/james/mime4j/field/AddressListField.java +++ b/src/org/apache/james/mime4j/field/AddressListField.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.field; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 eb6151377a..73d8d23392 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 Content-Transfer-Encoding field. - * - * - * @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $ - */ -public class ContentTransferEncodingField extends Field { - /** - * The 7bit encoding. - */ - public static final String ENC_7BIT = "7bit"; - /** - * The 8bit encoding. - */ - public static final String ENC_8BIT = "8bit"; - /** - * The binary encoding. - */ - public static final String ENC_BINARY = "binary"; - /** - * The quoted-printable encoding. - */ - public static final String ENC_QUOTED_PRINTABLE = "quoted-printable"; - /** - * The base64 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 - * 7bit if not set or if - * f is null. - * - * @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 Content-Transfer-Encoding field. + * + * + * @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $ + */ +public class ContentTransferEncodingField extends Field { + /** + * The 7bit encoding. + */ + public static final String ENC_7BIT = "7bit"; + /** + * The 8bit encoding. + */ + public static final String ENC_8BIT = "8bit"; + /** + * The binary encoding. + */ + public static final String ENC_BINARY = "binary"; + /** + * The quoted-printable encoding. + */ + public static final String ENC_QUOTED_PRINTABLE = "quoted-printable"; + /** + * The base64 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 + * 7bit if not set or if + * f is null. + * + * @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 51616419d7..01f640ccec 100644 --- a/src/org/apache/james/mime4j/field/ContentTypeField.java +++ b/src/org/apache/james/mime4j/field/ContentTypeField.java @@ -1,256 +1,256 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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 Content-Type field. - * - *

TODO: Remove dependency on Java 1.4 regexps

- * - * - * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $ - */ -public class ContentTypeField extends Field { - - /** - * The prefix of all multipart MIME types. - */ - public static final String TYPE_MULTIPART_PREFIX = "multipart/"; - /** - * The multipart/digest MIME type. - */ - public static final String TYPE_MULTIPART_DIGEST = "multipart/digest"; - /** - * The text/plain MIME type. - */ - public static final String TYPE_TEXT_PLAIN = "text/plain"; - /** - * The message/rfc822 MIME type. - */ - public static final String TYPE_MESSAGE_RFC822 = "message/rfc822"; - /** - * The name of the boundary parameter. - */ - public static final String PARAM_BOUNDARY = "boundary"; - /** - * The name of the charset parameter. - */ - public static final String PARAM_CHARSET = "charset"; - - private String mimeType = ""; - private Map parameters = null; - private ParseException parseException; - - protected ContentTypeField(String name, String body, String raw, String mimeType, Map 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 null 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 null if not set. - */ - public String getParameter(String name) { - return parameters != null - ? (String) parameters.get(name.toLowerCase()) - : null; - } - - /** - * Gets all parameters. - * - * @return the parameters. - */ - public Map getParameters() { - return parameters != null - ? Collections.unmodifiableMap(parameters) - : Collections.EMPTY_MAP; - } - - /** - * Gets the value of the boundary parameter if set. - * - * @return the boundary parameter value or null - * if not set. - */ - public String getBoundary() { - return getParameter(PARAM_BOUNDARY); - } - - /** - * Gets the value of the charset parameter if set. - * - * @return the charset parameter value or null - * if not set. - */ - public String getCharset() { - return getParameter(PARAM_CHARSET); - } - - /** - * Gets the value of the charset parameter if set for the - * given field. Returns the default us-ascii if not set or if - * f is null. - * - * @return the charset 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 true if the MIME type of this field matches, - * false otherwise. - */ - public boolean isMimeType(String mimeType) { - return this.mimeType.equalsIgnoreCase(mimeType); - } - - /** - * Determines if the MIME type of this field is multipart/*. - * - * @return true if this field is has a multipart/* - * MIME type, false 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 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 paramNames = parser.getParamNames(); - ArrayList 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((int)(paramNames.size() * 1.3 + 1)); - String paramName = ((String)paramNames.get(i)).toLowerCase(); - String paramValue = ((String)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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 Content-Type field. + * + *

TODO: Remove dependency on Java 1.4 regexps

+ * + * + * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $ + */ +public class ContentTypeField extends Field { + + /** + * The prefix of all multipart MIME types. + */ + public static final String TYPE_MULTIPART_PREFIX = "multipart/"; + /** + * The multipart/digest MIME type. + */ + public static final String TYPE_MULTIPART_DIGEST = "multipart/digest"; + /** + * The text/plain MIME type. + */ + public static final String TYPE_TEXT_PLAIN = "text/plain"; + /** + * The message/rfc822 MIME type. + */ + public static final String TYPE_MESSAGE_RFC822 = "message/rfc822"; + /** + * The name of the boundary parameter. + */ + public static final String PARAM_BOUNDARY = "boundary"; + /** + * The name of the charset parameter. + */ + public static final String PARAM_CHARSET = "charset"; + + private String mimeType = ""; + private Map parameters = null; + private ParseException parseException; + + protected ContentTypeField(String name, String body, String raw, String mimeType, Map 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 null 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 null if not set. + */ + public String getParameter(String name) { + return parameters != null + ? (String) parameters.get(name.toLowerCase()) + : null; + } + + /** + * Gets all parameters. + * + * @return the parameters. + */ + public Map getParameters() { + return parameters != null + ? Collections.unmodifiableMap(parameters) + : Collections.EMPTY_MAP; + } + + /** + * Gets the value of the boundary parameter if set. + * + * @return the boundary parameter value or null + * if not set. + */ + public String getBoundary() { + return getParameter(PARAM_BOUNDARY); + } + + /** + * Gets the value of the charset parameter if set. + * + * @return the charset parameter value or null + * if not set. + */ + public String getCharset() { + return getParameter(PARAM_CHARSET); + } + + /** + * Gets the value of the charset parameter if set for the + * given field. Returns the default us-ascii if not set or if + * f is null. + * + * @return the charset 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 true if the MIME type of this field matches, + * false otherwise. + */ + public boolean isMimeType(String mimeType) { + return this.mimeType.equalsIgnoreCase(mimeType); + } + + /** + * Determines if the MIME type of this field is multipart/*. + * + * @return true if this field is has a multipart/* + * MIME type, false 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 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 paramNames = parser.getParamNames(); + ArrayList 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((int)(paramNames.size() * 1.3 + 1)); + String paramName = ((String)paramNames.get(i)).toLowerCase(); + String paramValue = ((String)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 a7b24b5bd7..fa34ceda4b 100644 --- a/src/org/apache/james/mime4j/field/DateTimeField.java +++ b/src/org/apache/james/mime4j/field/DateTimeField.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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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, final String body, final String raw) { - Date date = null; - ParseException parseException = null; - 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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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, final String body, final String raw) { + Date date = null; + ParseException parseException = null; + 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 84fcdcb2d5..3695afe3e1 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 e7787a3363..c06160292b 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 parsers = new HashMap(); - private FieldParser defaultParser = new UnstructuredField.Parser(); - - /** - * Sets the parser used for the field named name. - * @param name the name of the field - * @param parser the parser for fields named name - */ - public void setFieldParser(final String name, final FieldParser parser) { - parsers.put(name.toLowerCase(), parser); - } - - public FieldParser getParser(final String name) { - final FieldParser field = (FieldParser) 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 parsers = new HashMap(); + private FieldParser defaultParser = new UnstructuredField.Parser(); + + /** + * Sets the parser used for the field named name. + * @param name the name of the field + * @param parser the parser for fields named name + */ + public void setFieldParser(final String name, final FieldParser parser) { + parsers.put(name.toLowerCase(), parser); + } + + public FieldParser getParser(final String name) { + final FieldParser field = (FieldParser) 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 7c2a20dc8e..4dea5c5cfb 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 - * Field class. The type of the class returned depends on - * the field name: - * - * - * - * - * - * - *
Field nameClass returnedContent-Typeorg.apache.james.mime4j.field.ContentTypeFieldotherorg.apache.james.mime4j.field.UnstructuredField
- * - * @param s the string to parse. - * @return a Field 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 (Subject, - * From, 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 Content-Type field. - * - * @return true if this is a Content-Type field, - * false otherwise. - */ - public boolean isContentType() { - return CONTENT_TYPE.equalsIgnoreCase(name); - } - - /** - * Determines if this is a Subject field. - * - * @return true if this is a Subject field, - * false otherwise. - */ - public boolean isSubject() { - return SUBJECT.equalsIgnoreCase(name); - } - - /** - * Determines if this is a From field. - * - * @return true if this is a From field, - * false otherwise. - */ - public boolean isFrom() { - return FROM.equalsIgnoreCase(name); - } - - /** - * Determines if this is a To field. - * - * @return true if this is a To field, - * false 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 + * Field class. The type of the class returned depends on + * the field name: + * + * + * + * + * + * + *
Field nameClass returnedContent-Typeorg.apache.james.mime4j.field.ContentTypeFieldotherorg.apache.james.mime4j.field.UnstructuredField
+ * + * @param s the string to parse. + * @return a Field 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 (Subject, + * From, 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 Content-Type field. + * + * @return true if this is a Content-Type field, + * false otherwise. + */ + public boolean isContentType() { + return CONTENT_TYPE.equalsIgnoreCase(name); + } + + /** + * Determines if this is a Subject field. + * + * @return true if this is a Subject field, + * false otherwise. + */ + public boolean isSubject() { + return SUBJECT.equalsIgnoreCase(name); + } + + /** + * Determines if this is a From field. + * + * @return true if this is a From field, + * false otherwise. + */ + public boolean isFrom() { + return FROM.equalsIgnoreCase(name); + } + + /** + * Determines if this is a To field. + * + * @return true if this is a To field, + * false 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 4f33c9e266..78aaf13349 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 96cca551fd..867a38a139 100644 --- a/src/org/apache/james/mime4j/field/MailboxField.java +++ b/src/org/apache/james/mime4j/field/MailboxField.java @@ -1,68 +1,68 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 efb18cd351..e5b986deac 100644 --- a/src/org/apache/james/mime4j/field/MailboxListField.java +++ b/src/org/apache/james/mime4j/field/MailboxListField.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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 5e2adf9f2d..6084e44355 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 Subject. - * - * - * @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 Subject. + * + * + * @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 47cee0ba01..5438eb5b26 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 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 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 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 results); + +} diff --git a/src/org/apache/james/mime4j/field/address/AddressList.java b/src/org/apache/james/mime4j/field/address/AddressList.java index 75072bde4e..f623239d08 100644 --- a/src/org/apache/james/mime4j/field/address/AddressList.java +++ b/src/org/apache/james/mime4j/field/address/AddressList.java @@ -1,140 +1,140 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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 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 addresses, boolean dontCopy) { - if (addresses != null) - this.addresses = (dontCopy ? addresses : (ArrayList) addresses.clone()); - else - this.addresses = new ArrayList(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 (Address) 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 results = new ArrayList(); - 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 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 addresses, boolean dontCopy) { + if (addresses != null) + this.addresses = (dontCopy ? addresses : (ArrayList) addresses.clone()); + else + this.addresses = new ArrayList(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 (Address) 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 results = new ArrayList(); + 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 3699afed71..b17f4e3f7a 100644 --- a/src/org/apache/james/mime4j/field/address/Builder.java +++ b/src/org/apache/james/mime4j/field/address/Builder.java @@ -1,244 +1,244 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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.*; -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 list = new ArrayList(); - 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 results = new ArrayList(); - 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 results = new ArrayList(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 { - - 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 Object 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.*; +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 list = new ArrayList(); + 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 results = new ArrayList(); + 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 results = new ArrayList(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 { + + 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 Object 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 23c377e9e8..d90b222857 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 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 domains, boolean dontCopy) { - if (domains != null) - this.domains = (dontCopy ? domains : (ArrayList) domains.clone()); - else - this.domains = new ArrayList(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 (String) 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 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 domains, boolean dontCopy) { + if (domains != null) + this.domains = (dontCopy ? domains : (ArrayList) domains.clone()); + else + this.domains = new ArrayList(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 (String) 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 eb4002708e..63e294549e 100644 --- a/src/org/apache/james/mime4j/field/address/Group.java +++ b/src/org/apache/james/mime4j/field/address/Group.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.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; - } - - 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(); - } - - protected void doAddMailboxesTo(ArrayList 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; + } + + 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(); + } + + protected void doAddMailboxesTo(ArrayList 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 57668abfe9..1bc32b8187 100644 --- a/src/org/apache/james/mime4j/field/address/Mailbox.java +++ b/src/org/apache/james/mime4j/field/address/Mailbox.java @@ -1,119 +1,119 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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 - * - * - * - * 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 + ">"; - } - - protected final void doAddMailboxesTo(ArrayList results) { - results.add(this); - } - - 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 + * + * + * + * 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 + ">"; + } + + protected final void doAddMailboxesTo(ArrayList results) { + results.add(this); + } + + 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 b25264da3e..db814a7fec 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 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 mailboxes, boolean dontCopy) { - if (mailboxes != null) - this.mailboxes = (dontCopy ? mailboxes : (ArrayList) mailboxes.clone()); - else - this.mailboxes = new ArrayList(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 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 mailboxes, boolean dontCopy) { + if (mailboxes != null) + this.mailboxes = (dontCopy ? mailboxes : (ArrayList) mailboxes.clone()); + else + this.mailboxes = new ArrayList(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 dea0d821cb..36b800c819 100644 --- a/src/org/apache/james/mime4j/field/address/NamedMailbox.java +++ b/src/org/apache/james/mime4j/field/address/NamedMailbox.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.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) - */ - 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) + */ + public String getAddressString(boolean includeRoute) { + return (name == null ? "" : name + " ") + super.getAddressString(includeRoute); + } +} 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 42fe3db0c5..7809746165 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/datetime/DateTime.java b/src/org/apache/james/mime4j/field/datetime/DateTime.java index bf00ca7536..506ff54e5d 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/message/AbstractBody.java b/src/org/apache/james/mime4j/message/AbstractBody.java index d2647ec779..190e882e01 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 Body 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 Body 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 bfc992a8fe..fc05a7d8a9 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 InputStream 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 InputStream 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 54b8948dbb..5d9b381906 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 474030d7f5..7640183366 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 96f2a4875f..06039f3d78 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 null if this is the root entity. - * - * @return the parent or null. - */ - public Entity getParent() { - return parent; - } - - /** - * Sets the parent entity of this entity. - * - * @param parent the parent entity or null 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 Entity. The MIME type - * is derived by looking at the parent's Content-Type field if no - * Content-Type field is set for this Entity. - * - * @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 Entity. - * - * @return the MIME character set encoding. - */ - public String getCharset() { - return ContentTypeField.getCharset( - (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE)); - } - - /** - * Determines the transfer encoding of this Entity. - * - * @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 Entity matches the - * given one. MIME types are case-insensitive. - * - * @param type the MIME type to match against. - * @return true on match, false otherwise. - */ - public boolean isMimeType(String type) { - return getMimeType().equalsIgnoreCase(type); - } - - /** - * Determines if the MIME type of this Entity is - * multipart/*. Since multipart-entities must have - * a boundary parameter in the Content-Type field this - * method returns false if no boundary exists. - * - * @return true on match, false 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 null if this is the root entity. + * + * @return the parent or null. + */ + public Entity getParent() { + return parent; + } + + /** + * Sets the parent entity of this entity. + * + * @param parent the parent entity or null 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 Entity. The MIME type + * is derived by looking at the parent's Content-Type field if no + * Content-Type field is set for this Entity. + * + * @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 Entity. + * + * @return the MIME character set encoding. + */ + public String getCharset() { + return ContentTypeField.getCharset( + (ContentTypeField) getHeader().getField(Field.CONTENT_TYPE)); + } + + /** + * Determines the transfer encoding of this Entity. + * + * @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 Entity matches the + * given one. MIME types are case-insensitive. + * + * @param type the MIME type to match against. + * @return true on match, false otherwise. + */ + public boolean isMimeType(String type) { + return getMimeType().equalsIgnoreCase(type); + } + + /** + * Determines if the MIME type of this Entity is + * multipart/*. Since multipart-entities must have + * a boundary parameter in the Content-Type field this + * method returns false if no boundary exists. + * + * @return true on match, false 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 90a0ef4802..3e97464791 100644 --- a/src/org/apache/james/mime4j/message/Header.java +++ b/src/org/apache/james/mime4j/message/Header.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.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 fields = new LinkedList(); - private HashMap fieldMap = new HashMap(); - - /** - * Creates a new empty Header. - */ - public Header() { - } - - /** - * Creates a new Header 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() { - public void endHeader() { - parser.stop(); - } - 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 values = (List) fieldMap.get(field.getName().toLowerCase()); - if (values == null) { - values = new LinkedList(); - 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 Field objects. - */ - public List getFields() { - return Collections.unmodifiableList(fields); - } - - /** - * Gets a Field 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 null if none found. - */ - public Field getField(String name) { - List l = (List) fieldMap.get(name.toLowerCase()); - if (l != null && !l.isEmpty()) { - return (Field) l.get(0); - } - return null; - } - - /** - * Gets all Fields having the specified field name. - * - * @param name the field name (e.g. From, Subject). - * @return the list of fields. - */ - public List getFields(String name) { - List l = (List) fieldMap.get(name.toLowerCase()); - return Collections.unmodifiableList(l); - } - - /** - * Return Header Object as String representation. Each headerline is - * seperated by "\r\n" - * - * @return headers - */ - public String toString() { - StringBuffer str = new StringBuffer(); - for (Iterator 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 fields = new LinkedList(); + private HashMap fieldMap = new HashMap(); + + /** + * Creates a new empty Header. + */ + public Header() { + } + + /** + * Creates a new Header 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() { + public void endHeader() { + parser.stop(); + } + 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 values = (List) fieldMap.get(field.getName().toLowerCase()); + if (values == null) { + values = new LinkedList(); + 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 Field objects. + */ + public List getFields() { + return Collections.unmodifiableList(fields); + } + + /** + * Gets a Field 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 null if none found. + */ + public Field getField(String name) { + List l = (List) fieldMap.get(name.toLowerCase()); + if (l != null && !l.isEmpty()) { + return (Field) l.get(0); + } + return null; + } + + /** + * Gets all Fields having the specified field name. + * + * @param name the field name (e.g. From, Subject). + * @return the list of fields. + */ + public List getFields(String name) { + List l = (List) fieldMap.get(name.toLowerCase()); + return Collections.unmodifiableList(l); + } + + /** + * Return Header Object as String representation. Each headerline is + * seperated by "\r\n" + * + * @return headers + */ + public String toString() { + StringBuffer str = new StringBuffer(); + for (Iterator 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 0db44e199b..fb78ff0802 100644 --- a/src/org/apache/james/mime4j/message/MemoryBinaryBody.java +++ b/src/org/apache/james/mime4j/message/MemoryBinaryBody.java @@ -1,90 +1,90 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 e5e9d9d728..ce4318a4da 100644 --- a/src/org/apache/james/mime4j/message/MemoryTextBody.java +++ b/src/org/apache/james/mime4j/message/MemoryTextBody.java @@ -1,116 +1,116 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 fc28c586ac..14aacfc2b8 100644 --- a/src/org/apache/james/mime4j/message/Message.java +++ b/src/org/apache/james/mime4j/message/Message.java @@ -1,256 +1,256 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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 - * Message object. - * - *
- *      Message msg = new Message(new BufferedInputStream(
- *                                      new FileInputStream("mime.msg")));
- * 
- * - * - * - * @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 Message. - */ - public Message() { - } - - /** - * Parses the specified MIME message stream into a Message - * 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 Subject field. - * - * @return the Subject field or null 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) - */ - 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 stack = new Stack(); - - 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 + * Message object. + * + *
+ *      Message msg = new Message(new BufferedInputStream(
+ *                                      new FileInputStream("mime.msg")));
+ * 
+ * + * + * + * @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 Message. + */ + public Message() { + } + + /** + * Parses the specified MIME message stream into a Message + * 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 Subject field. + * + * @return the Subject field or null 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) + */ + 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 stack = new Stack(); + + 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 3c4b519c48..a3a255b71e 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 bodyParts = new LinkedList(); - private Entity parent = null; - private String subType = "alternative"; - - /** - * Creates a new empty Multipart instance. - */ - public Multipart() { - } - - /** - * Gets the multipart sub-type. E.g. alternative (the default) - * or parallel. 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. alternative - * or parallel. 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 it = bodyParts.iterator(); it.hasNext();) { - ((BodyPart) 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 BodyPart objects. - */ - public List getBodyParts() { - return Collections.unmodifiableList(bodyParts); - } - - /** - * Sets the list of body parts. - * - * @param bodyParts the new list of BodyPart objects. - */ - public void setBodyParts(List bodyParts) { - this.bodyParts = bodyParts; - for (Iterator it = bodyParts.iterator(); it.hasNext();) { - ((BodyPart) 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 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"); - ((BodyPart) 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 bodyParts = new LinkedList(); + private Entity parent = null; + private String subType = "alternative"; + + /** + * Creates a new empty Multipart instance. + */ + public Multipart() { + } + + /** + * Gets the multipart sub-type. E.g. alternative (the default) + * or parallel. 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. alternative + * or parallel. 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 it = bodyParts.iterator(); it.hasNext();) { + ((BodyPart) 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 BodyPart objects. + */ + public List getBodyParts() { + return Collections.unmodifiableList(bodyParts); + } + + /** + * Sets the list of body parts. + * + * @param bodyParts the new list of BodyPart objects. + */ + public void setBodyParts(List bodyParts) { + this.bodyParts = bodyParts; + for (Iterator it = bodyParts.iterator(); it.hasNext();) { + ((BodyPart) 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 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"); + ((BodyPart) 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 3ded83db8a..2151a53cde 100644 --- a/src/org/apache/james/mime4j/message/TempFileBinaryBody.java +++ b/src/org/apache/james/mime4j/message/TempFileBinaryBody.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.james.mime4j.message; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 0b3b70c25a..25a3b29587 100644 --- a/src/org/apache/james/mime4j/message/TempFileTextBody.java +++ b/src/org/apache/james/mime4j/message/TempFileTextBody.java @@ -1,115 +1,115 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 4fe7144665..cbf24c2e76 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 text/* entity body. - * - * - * @version $Id: TextBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $ - */ -public interface TextBody extends Body { - - /** - * Gets a Reader which may be used to read out the contents - * of this body. - * - * @return the Reader. - * @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 text/* entity body. + * + * + * @version $Id: TextBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $ + */ +public interface TextBody extends Body { + + /** + * Gets a Reader which may be used to read out the contents + * of this body. + * + * @return the Reader. + * @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 8bb0440393..e18ceb3fb0 100644 --- a/src/org/apache/james/mime4j/util/CharsetUtil.java +++ b/src/org/apache/james/mime4j/util/CharsetUtil.java @@ -1,1245 +1,1245 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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.TreeSet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Utility class for working with character sets. It is somewhat similar to - * the Java 1.4 java.nio.charset.Charset 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 - * - * Java 1.5 (J2SE5.0) Supported Encodings document. - *

- * The - * IANA Character Sets document has been used to determine the preferred - * MIME character set names and to get a list of known aliases. - *

- * This is a complete list of the character sets known to this class: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Canonical (Java) nameMIME preferredAliases
ASCIIUS-ASCIIANSI_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
Big5Big5csBig5 CN-Big5 BIG-FIVE BIGFIVE
Big5_HKSCSBig5-HKSCSbig5hkscs
Big5_Solaris?
Cp037IBM037ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037
Cp1006?
Cp1025?
Cp1026IBM1026csIBM1026
Cp1046?
Cp1047IBM1047IBM-1047
Cp1097?
Cp1098?
Cp1112?
Cp1122?
Cp1123?
Cp1124?
Cp1140IBM01140CCSID01140 CP01140 ebcdic-us-37+euro
Cp1141IBM01141CCSID01141 CP01141 ebcdic-de-273+euro
Cp1142IBM01142CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro
Cp1143IBM01143CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro
Cp1144IBM01144CCSID01144 CP01144 ebcdic-it-280+euro
Cp1145IBM01145CCSID01145 CP01145 ebcdic-es-284+euro
Cp1146IBM01146CCSID01146 CP01146 ebcdic-gb-285+euro
Cp1147IBM01147CCSID01147 CP01147 ebcdic-fr-297+euro
Cp1148IBM01148CCSID01148 CP01148 ebcdic-international-500+euro
Cp1149IBM01149CCSID01149 CP01149 ebcdic-is-871+euro
Cp1250windows-1250
Cp1251windows-1251
Cp1252windows-1252
Cp1253windows-1253
Cp1254windows-1254
Cp1255windows-1255
Cp1256windows-1256
Cp1257windows-1257
Cp1258windows-1258
Cp1381?
Cp1383?
Cp273IBM273csIBM273
Cp277IBM277EBCDIC-CP-DK EBCDIC-CP-NO csIBM277
Cp278IBM278CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278
Cp280IBM280ebcdic-cp-it csIBM280
Cp284IBM284ebcdic-cp-es csIBM284
Cp285IBM285ebcdic-cp-gb csIBM285
Cp297IBM297ebcdic-cp-fr csIBM297
Cp33722?
Cp420IBM420ebcdic-cp-ar1 csIBM420
Cp424IBM424ebcdic-cp-he csIBM424
Cp437IBM437437 csPC8CodePage437
Cp500IBM500ebcdic-cp-be ebcdic-cp-ch csIBM500
Cp737?
Cp775IBM775csPC775Baltic
Cp838IBM-Thai
Cp850IBM850850 csPC850Multilingual
Cp852IBM852852 csPCp852
Cp855IBM855855 csIBM855
Cp856?
Cp857IBM857857 csIBM857
Cp858IBM00858CCSID00858 CP00858 PC-Multilingual-850+euro
Cp860IBM860860 csIBM860
Cp861IBM861861 cp-is csIBM861
Cp862IBM862862 csPC862LatinHebrew
Cp863IBM863863 csIBM863
Cp864IBM864cp864 csIBM864
Cp865IBM865865 csIBM865
Cp866IBM866866 csIBM866
Cp868IBM868cp-ar csIBM868
Cp869IBM869cp-gr csIBM869
Cp870IBM870ebcdic-cp-roece ebcdic-cp-yu csIBM870
Cp871IBM871ebcdic-cp-is csIBM871
Cp875?
Cp918IBM918ebcdic-cp-ar2 csIBM918
Cp921?
Cp922?
Cp930?
Cp933?
Cp935?
Cp937?
Cp939?
Cp942?
Cp942C?
Cp943?
Cp943C?
Cp948?
Cp949?
Cp949C?
Cp950?
Cp964?
Cp970?
EUC_CNGB2312x-EUC-CN csGB2312 euccn euc-cn gb2312-80 gb2312-1980 CN-GB CN-GB-ISOIR165
EUC_JPEUC-JPcsEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese eucjis x-eucjp eucjp x-euc-jp
EUC_JP_LINUX?
EUC_JP_Solaris?
EUC_KREUC-KRcsEUCKR ksc5601 5601 ksc5601_1987 ksc_5601 ksc5601-1987 ks_c_5601-1987 euckr
EUC_TWEUC-TWx-EUC-TW cns11643 euctw
GB18030GB18030gb18030-2000
GBKwindows-936CP936 MS936 ms_936 x-mswin-936
ISCII91?x-ISCII91 iscii
ISO2022CNISO-2022-CN
ISO2022JPISO-2022-JPcsISO2022JP JIS jis_encoding csjisencoding
ISO2022KRISO-2022-KRcsISO2022KR
ISO2022_CN_CNS?
ISO2022_CN_GB?
ISO8859_1ISO-8859-1ISO_8859-1:1987 iso-ir-100 ISO_8859-1 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 819 IBM-819 ISO8859-1 ISO_8859_1
ISO8859_13ISO-8859-13
ISO8859_15ISO-8859-15ISO_8859-15 Latin-9 8859_15 csISOlatin9 IBM923 cp923 923 L9 IBM-923 ISO8859-15 LATIN9 LATIN0 csISOlatin0 ISO8859_15_FDIS
ISO8859_2ISO-8859-2ISO_8859-2:1987 iso-ir-101 ISO_8859-2 latin2 l2 csISOLatin2 8859_2 iso8859_2
ISO8859_3ISO-8859-3ISO_8859-3:1988 iso-ir-109 ISO_8859-3 latin3 l3 csISOLatin3 8859_3
ISO8859_4ISO-8859-4ISO_8859-4:1988 iso-ir-110 ISO_8859-4 latin4 l4 csISOLatin4 8859_4
ISO8859_5ISO-8859-5ISO_8859-5:1988 iso-ir-144 ISO_8859-5 cyrillic csISOLatinCyrillic 8859_5
ISO8859_6ISO-8859-6ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ECMA-114 ASMO-708 arabic csISOLatinArabic 8859_6
ISO8859_7ISO-8859-7ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 sun_eu_greek
ISO8859_8ISO-8859-8ISO_8859-8:1988 iso-ir-138 ISO_8859-8 hebrew csISOLatinHebrew 8859_8
ISO8859_9ISO-8859-9ISO_8859-9:1989 iso-ir-148 ISO_8859-9 latin5 l5 csISOLatin5 8859_9
JISAutoDetect?
JIS_C6626-1983JIS_C6626-1983x-JIS0208 JIS0208 csISO87JISX0208 x0208 JIS_X0208-1983 iso-ir-87
JIS_X0201JIS_X0201X0201 JIS0201 csHalfWidthKatakana
JIS_X0212-1990JIS_X0212-1990iso-ir-159 x0212 JIS0212 csISO159JISX02121990
KOI8_RKOI8-RcsKOI8R koi8
MS874windows-874cp874
MS932Windows-31Jwindows-932 csWindows31J x-ms-cp932
MS949windows-949windows949 ms_949 x-windows-949
MS950windows-950x-windows-950
MS950_HKSCS
MacArabic?
MacCentralEurope?
MacCroatian?
MacCyrillic?
MacDingbat?
MacGreekMacGreek
MacHebrew?
MacIceland?
MacRomanMacRomanMacintosh MAC csMacintosh
MacRomania?
MacSymbol?
MacThai?
MacTurkish?
MacUkraine?
SJISShift_JISMS_Kanji csShiftJIS shift-jis x-sjis pck
TIS620TIS-620
UTF-16UTF-16UTF_16
UTF8UTF-8
UnicodeBig?
UnicodeBigUnmarkedUTF-16BEX-UTF-16BE UTF_16BE ISO-10646-UCS-2
UnicodeLittle?
UnicodeLittleUnmarkedUTF-16LEUTF_16LE X-UTF-16LE
x-Johabjohabjohab cp1361 ms1361 ksc5601-1992 ksc5601_1992
x-iso-8859-11?
- * - * - * @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 { - 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(Object o) { - Charset c = (Charset) o; - 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 decodingSupported = null; - - /** - * Contains the canonical names of character sets which can be used to - * encode Java chars into bytes. - */ - private static TreeSet encodingSupported = null; - - /** - * Maps character set names to Charset objects. All possible names of - * a charset will be mapped to the Charset. - */ - private static HashMap charsetMap = null; - - static { - decodingSupported = new TreeSet(); - encodingSupported = new TreeSet(); - 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()); - } catch (UnsupportedOperationException e) { - } catch (UnsupportedEncodingException e) { - } - try { - "dummy".getBytes(JAVA_CHARSETS[i].canonical); - encodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase()); - } catch (UnsupportedOperationException e) { - } catch (UnsupportedEncodingException e) { - } - } - - charsetMap = new HashMap(); - for (int i = 0; i < JAVA_CHARSETS.length; i++) { - Charset c = JAVA_CHARSETS[i]; - charsetMap.put(c.canonical.toLowerCase(), c); - if (c.mime != null) { - charsetMap.put(c.mime.toLowerCase(), c); - } - if (c.aliases != null) { - for (int j = 0; j < c.aliases.length; j++) { - charsetMap.put(c.aliases[j].toLowerCase(), 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 true 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 true if the specified character is a whitespace - * character, false otherwise. - */ - public static boolean isWhitespace(char ch) { - return ch == SP || ch == HT || ch == CR || ch == LF; - } - - /** - * Returns true if the specified string consists entirely of - * whitespace characters. - * - * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J - * - * @param s - * string to test. - * @return true if the specified string consists entirely of - * whitespace characters, false 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 true. - * Use {@link #toJavaCharset(String)} to get the canonical Java character - * set name. - * - * @param charsetName the characters set name. - * @return true if encoding is supported, false - * otherwise. - */ - public static boolean isEncodingSupported(String charsetName) { - return encodingSupported.contains(charsetName.toLowerCase()); - } - - /** - * 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 true. - * Use {@link #toJavaCharset(String)} to get the canonical Java character - * set name. - * - * @param charsetName the characters set name. - * @return true if decoding is supported, false - * otherwise. - */ - public static boolean isDecodingSupported(String charsetName) { - return decodingSupported.contains(charsetName.toLowerCase()); - } - - /** - * Gets the preferred MIME character set name for the specified - * character set or null if not known. - * - * @param charsetName the character set name to look for. - * @return the MIME preferred name or null if not known. - */ - public static String toMimeCharset(String charsetName) { - Charset c = (Charset) charsetMap.get(charsetName.toLowerCase()); - if (c != null) { - return c.mime; - } - return null; - } - - /** - * Gets the canonical Java character set name for the specified - * character set or null 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 null if not known. - */ - public static String toJavaCharset(String charsetName) { - Charset c = (Charset) charsetMap.get(charsetName.toLowerCase()); - 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(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - - for (Iterator it = l.iterator(); it.hasNext();) { - Charset c = (Charset) it.next(); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - sb.append(" * \n"); - } - sb.append(" *
Canonical (Java) nameMIME preferredAliases
" + c.canonical + "" + (c.mime == null ? "?" : c.mime)+ ""); - for (int i = 0; c.aliases != null && i < c.aliases.length; i++) { - sb.append(c.aliases[i] + " "); - } - sb.append("
\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.TreeSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility class for working with character sets. It is somewhat similar to + * the Java 1.4 java.nio.charset.Charset 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 + * + * Java 1.5 (J2SE5.0) Supported Encodings document. + *

+ * The + * IANA Character Sets document has been used to determine the preferred + * MIME character set names and to get a list of known aliases. + *

+ * This is a complete list of the character sets known to this class: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Canonical (Java) nameMIME preferredAliases
ASCIIUS-ASCIIANSI_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
Big5Big5csBig5 CN-Big5 BIG-FIVE BIGFIVE
Big5_HKSCSBig5-HKSCSbig5hkscs
Big5_Solaris?
Cp037IBM037ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037
Cp1006?
Cp1025?
Cp1026IBM1026csIBM1026
Cp1046?
Cp1047IBM1047IBM-1047
Cp1097?
Cp1098?
Cp1112?
Cp1122?
Cp1123?
Cp1124?
Cp1140IBM01140CCSID01140 CP01140 ebcdic-us-37+euro
Cp1141IBM01141CCSID01141 CP01141 ebcdic-de-273+euro
Cp1142IBM01142CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro
Cp1143IBM01143CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro
Cp1144IBM01144CCSID01144 CP01144 ebcdic-it-280+euro
Cp1145IBM01145CCSID01145 CP01145 ebcdic-es-284+euro
Cp1146IBM01146CCSID01146 CP01146 ebcdic-gb-285+euro
Cp1147IBM01147CCSID01147 CP01147 ebcdic-fr-297+euro
Cp1148IBM01148CCSID01148 CP01148 ebcdic-international-500+euro
Cp1149IBM01149CCSID01149 CP01149 ebcdic-is-871+euro
Cp1250windows-1250
Cp1251windows-1251
Cp1252windows-1252
Cp1253windows-1253
Cp1254windows-1254
Cp1255windows-1255
Cp1256windows-1256
Cp1257windows-1257
Cp1258windows-1258
Cp1381?
Cp1383?
Cp273IBM273csIBM273
Cp277IBM277EBCDIC-CP-DK EBCDIC-CP-NO csIBM277
Cp278IBM278CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278
Cp280IBM280ebcdic-cp-it csIBM280
Cp284IBM284ebcdic-cp-es csIBM284
Cp285IBM285ebcdic-cp-gb csIBM285
Cp297IBM297ebcdic-cp-fr csIBM297
Cp33722?
Cp420IBM420ebcdic-cp-ar1 csIBM420
Cp424IBM424ebcdic-cp-he csIBM424
Cp437IBM437437 csPC8CodePage437
Cp500IBM500ebcdic-cp-be ebcdic-cp-ch csIBM500
Cp737?
Cp775IBM775csPC775Baltic
Cp838IBM-Thai
Cp850IBM850850 csPC850Multilingual
Cp852IBM852852 csPCp852
Cp855IBM855855 csIBM855
Cp856?
Cp857IBM857857 csIBM857
Cp858IBM00858CCSID00858 CP00858 PC-Multilingual-850+euro
Cp860IBM860860 csIBM860
Cp861IBM861861 cp-is csIBM861
Cp862IBM862862 csPC862LatinHebrew
Cp863IBM863863 csIBM863
Cp864IBM864cp864 csIBM864
Cp865IBM865865 csIBM865
Cp866IBM866866 csIBM866
Cp868IBM868cp-ar csIBM868
Cp869IBM869cp-gr csIBM869
Cp870IBM870ebcdic-cp-roece ebcdic-cp-yu csIBM870
Cp871IBM871ebcdic-cp-is csIBM871
Cp875?
Cp918IBM918ebcdic-cp-ar2 csIBM918
Cp921?
Cp922?
Cp930?
Cp933?
Cp935?
Cp937?
Cp939?
Cp942?
Cp942C?
Cp943?
Cp943C?
Cp948?
Cp949?
Cp949C?
Cp950?
Cp964?
Cp970?
EUC_CNGB2312x-EUC-CN csGB2312 euccn euc-cn gb2312-80 gb2312-1980 CN-GB CN-GB-ISOIR165
EUC_JPEUC-JPcsEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese eucjis x-eucjp eucjp x-euc-jp
EUC_JP_LINUX?
EUC_JP_Solaris?
EUC_KREUC-KRcsEUCKR ksc5601 5601 ksc5601_1987 ksc_5601 ksc5601-1987 ks_c_5601-1987 euckr
EUC_TWEUC-TWx-EUC-TW cns11643 euctw
GB18030GB18030gb18030-2000
GBKwindows-936CP936 MS936 ms_936 x-mswin-936
ISCII91?x-ISCII91 iscii
ISO2022CNISO-2022-CN
ISO2022JPISO-2022-JPcsISO2022JP JIS jis_encoding csjisencoding
ISO2022KRISO-2022-KRcsISO2022KR
ISO2022_CN_CNS?
ISO2022_CN_GB?
ISO8859_1ISO-8859-1ISO_8859-1:1987 iso-ir-100 ISO_8859-1 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 819 IBM-819 ISO8859-1 ISO_8859_1
ISO8859_13ISO-8859-13
ISO8859_15ISO-8859-15ISO_8859-15 Latin-9 8859_15 csISOlatin9 IBM923 cp923 923 L9 IBM-923 ISO8859-15 LATIN9 LATIN0 csISOlatin0 ISO8859_15_FDIS
ISO8859_2ISO-8859-2ISO_8859-2:1987 iso-ir-101 ISO_8859-2 latin2 l2 csISOLatin2 8859_2 iso8859_2
ISO8859_3ISO-8859-3ISO_8859-3:1988 iso-ir-109 ISO_8859-3 latin3 l3 csISOLatin3 8859_3
ISO8859_4ISO-8859-4ISO_8859-4:1988 iso-ir-110 ISO_8859-4 latin4 l4 csISOLatin4 8859_4
ISO8859_5ISO-8859-5ISO_8859-5:1988 iso-ir-144 ISO_8859-5 cyrillic csISOLatinCyrillic 8859_5
ISO8859_6ISO-8859-6ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ECMA-114 ASMO-708 arabic csISOLatinArabic 8859_6
ISO8859_7ISO-8859-7ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 sun_eu_greek
ISO8859_8ISO-8859-8ISO_8859-8:1988 iso-ir-138 ISO_8859-8 hebrew csISOLatinHebrew 8859_8
ISO8859_9ISO-8859-9ISO_8859-9:1989 iso-ir-148 ISO_8859-9 latin5 l5 csISOLatin5 8859_9
JISAutoDetect?
JIS_C6626-1983JIS_C6626-1983x-JIS0208 JIS0208 csISO87JISX0208 x0208 JIS_X0208-1983 iso-ir-87
JIS_X0201JIS_X0201X0201 JIS0201 csHalfWidthKatakana
JIS_X0212-1990JIS_X0212-1990iso-ir-159 x0212 JIS0212 csISO159JISX02121990
KOI8_RKOI8-RcsKOI8R koi8
MS874windows-874cp874
MS932Windows-31Jwindows-932 csWindows31J x-ms-cp932
MS949windows-949windows949 ms_949 x-windows-949
MS950windows-950x-windows-950
MS950_HKSCS
MacArabic?
MacCentralEurope?
MacCroatian?
MacCyrillic?
MacDingbat?
MacGreekMacGreek
MacHebrew?
MacIceland?
MacRomanMacRomanMacintosh MAC csMacintosh
MacRomania?
MacSymbol?
MacThai?
MacTurkish?
MacUkraine?
SJISShift_JISMS_Kanji csShiftJIS shift-jis x-sjis pck
TIS620TIS-620
UTF-16UTF-16UTF_16
UTF8UTF-8
UnicodeBig?
UnicodeBigUnmarkedUTF-16BEX-UTF-16BE UTF_16BE ISO-10646-UCS-2
UnicodeLittle?
UnicodeLittleUnmarkedUTF-16LEUTF_16LE X-UTF-16LE
x-Johabjohabjohab cp1361 ms1361 ksc5601-1992 ksc5601_1992
x-iso-8859-11?
+ * + * + * @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 { + 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(Object o) { + Charset c = (Charset) o; + 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 decodingSupported = null; + + /** + * Contains the canonical names of character sets which can be used to + * encode Java chars into bytes. + */ + private static TreeSet encodingSupported = null; + + /** + * Maps character set names to Charset objects. All possible names of + * a charset will be mapped to the Charset. + */ + private static HashMap charsetMap = null; + + static { + decodingSupported = new TreeSet(); + encodingSupported = new TreeSet(); + 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()); + } catch (UnsupportedOperationException e) { + } catch (UnsupportedEncodingException e) { + } + try { + "dummy".getBytes(JAVA_CHARSETS[i].canonical); + encodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase()); + } catch (UnsupportedOperationException e) { + } catch (UnsupportedEncodingException e) { + } + } + + charsetMap = new HashMap(); + for (int i = 0; i < JAVA_CHARSETS.length; i++) { + Charset c = JAVA_CHARSETS[i]; + charsetMap.put(c.canonical.toLowerCase(), c); + if (c.mime != null) { + charsetMap.put(c.mime.toLowerCase(), c); + } + if (c.aliases != null) { + for (int j = 0; j < c.aliases.length; j++) { + charsetMap.put(c.aliases[j].toLowerCase(), 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 true 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 true if the specified character is a whitespace + * character, false otherwise. + */ + public static boolean isWhitespace(char ch) { + return ch == SP || ch == HT || ch == CR || ch == LF; + } + + /** + * Returns true if the specified string consists entirely of + * whitespace characters. + * + * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J + * + * @param s + * string to test. + * @return true if the specified string consists entirely of + * whitespace characters, false 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 true. + * Use {@link #toJavaCharset(String)} to get the canonical Java character + * set name. + * + * @param charsetName the characters set name. + * @return true if encoding is supported, false + * otherwise. + */ + public static boolean isEncodingSupported(String charsetName) { + return encodingSupported.contains(charsetName.toLowerCase()); + } + + /** + * 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 true. + * Use {@link #toJavaCharset(String)} to get the canonical Java character + * set name. + * + * @param charsetName the characters set name. + * @return true if decoding is supported, false + * otherwise. + */ + public static boolean isDecodingSupported(String charsetName) { + return decodingSupported.contains(charsetName.toLowerCase()); + } + + /** + * Gets the preferred MIME character set name for the specified + * character set or null if not known. + * + * @param charsetName the character set name to look for. + * @return the MIME preferred name or null if not known. + */ + public static String toMimeCharset(String charsetName) { + Charset c = (Charset) charsetMap.get(charsetName.toLowerCase()); + if (c != null) { + return c.mime; + } + return null; + } + + /** + * Gets the canonical Java character set name for the specified + * character set or null 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 null if not known. + */ + public static String toJavaCharset(String charsetName) { + Charset c = (Charset) charsetMap.get(charsetName.toLowerCase()); + 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(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + + for (Iterator it = l.iterator(); it.hasNext();) { + Charset c = (Charset) it.next(); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + sb.append(" * \n"); + } + sb.append(" *
Canonical (Java) nameMIME preferredAliases
" + c.canonical + "" + (c.mime == null ? "?" : c.mime)+ ""); + for (int i = 0; c.aliases != null && i < c.aliases.length; i++) { + sb.append(c.aliases[i] + " "); + } + sb.append("
\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 a5ac0596d9..cf3b107617 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 9fcd21d0ca..7865b6e8a4 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 7ca0371a21..51ddd51f74 100644 --- a/src/org/apache/james/mime4j/util/SimpleTempStorage.java +++ b/src/org/apache/james/mime4j/util/SimpleTempStorage.java @@ -1,236 +1,236 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @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 SimpleTempStorageManager 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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @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 SimpleTempStorageManager 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 f67e1e93ee..cc2ea6d98e 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 InputStream to read bytes from this temporary file. - * NOTE: The stream should NOT be wrapped in - * BufferedInputStream by the caller. If the implementing - * TempFile creates a FileInputStream or any - * other stream which would benefit from being buffered it's the - * TempFile's responsibility to wrap it. - * - * @return the stream. - * @throws IOException - */ - InputStream getInputStream() throws IOException; - - /** - * Gets an OutputStream to write bytes to this temporary file. - * NOTE: The stream should NOT be wrapped in - * BufferedOutputStream by the caller. If the implementing - * TempFile creates a FileOutputStream or any - * other stream which would benefit from being buffered it's the - * TempFile's responsibility to wrap it. - * - * @return the stream. - * @throws IOException - */ - OutputStream getOutputStream() throws IOException; - - /** - * Returns the absolute path including file name of this - * TempFile. The path may be null 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 true if this file is currently in memory, - * false 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 InputStream to read bytes from this temporary file. + * NOTE: The stream should NOT be wrapped in + * BufferedInputStream by the caller. If the implementing + * TempFile creates a FileInputStream or any + * other stream which would benefit from being buffered it's the + * TempFile's responsibility to wrap it. + * + * @return the stream. + * @throws IOException + */ + InputStream getInputStream() throws IOException; + + /** + * Gets an OutputStream to write bytes to this temporary file. + * NOTE: The stream should NOT be wrapped in + * BufferedOutputStream by the caller. If the implementing + * TempFile creates a FileOutputStream or any + * other stream which would benefit from being buffered it's the + * TempFile's responsibility to wrap it. + * + * @return the stream. + * @throws IOException + */ + OutputStream getOutputStream() throws IOException; + + /** + * Returns the absolute path including file name of this + * TempFile. The path may be null 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 true if this file is currently in memory, + * false 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 3b55aa6db0..3c4cf8163e 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 - * .tmp 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. null gives no prefix. - * @param suffix the suffix to use. null gives - * .tmp. - * @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 allowInMemory - * 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. null gives no prefix. - * @param suffix the suffix to use. null gives - * .tmp. - * @param allowInMemory if true the file MIGHT be created in - * memory if supported by the implentation. If false 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 + * .tmp 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. null gives no prefix. + * @param suffix the suffix to use. null gives + * .tmp. + * @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 allowInMemory + * 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. null gives no prefix. + * @param suffix the suffix to use. null gives + * .tmp. + * @param allowInMemory if true the file MIGHT be created in + * memory if supported by the implentation. If false 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 ca951531aa..a8546a1e5c 100644 --- a/src/org/apache/james/mime4j/util/TempStorage.java +++ b/src/org/apache/james/mime4j/util/TempStorage.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.util; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @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; + } +} -- GitLab