17e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki/* 27e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Copyright (C) 2010 The Android Open Source Project 37e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 47e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Licensed under the Apache License, Version 2.0 (the "License"); 57e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * you may not use this file except in compliance with the License. 67e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * You may obtain a copy of the License at 77e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 87e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 97e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 107e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Unless required by applicable law or agreed to in writing, software 117e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 127e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * See the License for the specific language governing permissions and 147e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * limitations under the License. 157e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 167e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 177e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukipackage com.android.email.mail.store.imap; 187e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.Logging; 207e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 217e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport android.util.Log; 227e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 237e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.io.ByteArrayInputStream; 247e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.io.InputStream; 257e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.text.ParseException; 267e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.text.SimpleDateFormat; 277e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.util.Date; 287e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.util.Locale; 297e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 307e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki/** 317e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Class represents an IMAP "element" that is not a list. 327e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 337e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too. 347e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]". 357e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * See {@link ImapResponseParser}. 367e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 377e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukipublic abstract class ImapString extends ImapElement { 387e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private static final byte[] EMPTY_BYTES = new byte[0]; 397e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 407e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static final ImapString EMPTY = new ImapString() { 410fd444b0b4c07ac1545e0edc64b54fd738edfc94Makoto Onuki @Override public void destroy() { 420fd444b0b4c07ac1545e0edc64b54fd738edfc94Makoto Onuki // Don't call super.destroy(). 430fd444b0b4c07ac1545e0edc64b54fd738edfc94Makoto Onuki // It's a shared object. We don't want the mDestroyed to be set on this. 440fd444b0b4c07ac1545e0edc64b54fd738edfc94Makoto Onuki } 450fd444b0b4c07ac1545e0edc64b54fd738edfc94Makoto Onuki 467e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public String getString() { 477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return ""; 487e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 507e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public InputStream getAsStream() { 517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return new ByteArrayInputStream(EMPTY_BYTES); 527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public String toString() { 557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return ""; 567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki }; 587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // This is used only for parsing IMAP's FETCH ENVELOPE command, in which 607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be 617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // handled by Locale.US 627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private final static SimpleDateFormat DATE_TIME_FORMAT = 637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); 647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private boolean mIsInteger; 667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private int mParsedInteger; 677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private Date mParsedDate; 687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isList() { 717e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 727e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 747e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 757e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isString() { 767e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 777e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 787e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 797e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 807e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return true if and only if the length of the string is larger than 0. 817e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 827e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser 837e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * #parseBareString}. 847e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is 857e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * treated literally. 867e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 877e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isEmpty() { 887e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().length() == 0; 897e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 907e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 917e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract String getString(); 927e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 937e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract InputStream getAsStream(); 947e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 957e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 967e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether it can be parsed as a number. 977e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 987e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isNumber() { 997e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (mIsInteger) { 1007e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1017e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1027e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki try { 1037e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mParsedInteger = Integer.parseInt(getString()); 1047e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mIsInteger = true; 1057e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1067e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } catch (NumberFormatException e) { 1077e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1087e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1097e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1107e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1117e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1127e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return value parsed as a number. 1137e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1147e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final int getNumberOrZero() { 1157e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!isNumber()) { 1167e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return 0; 1177e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1187e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return mParsedInteger; 1197e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1207e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1217e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1227e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}. 1237e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1247e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isDate() { 1257e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (mParsedDate != null) { 1267e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1277e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1287e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (isEmpty()) { 1297e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1307e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1317e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki try { 1327e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mParsedDate = DATE_TIME_FORMAT.parse(getString()); 1337e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1347e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } catch (ParseException e) { 13531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.w(Logging.LOG_TAG, getString() + " can't be parsed as a date."); 1367e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1377e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1387e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1397e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1407e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1417e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return value it can be parsed as a {@link Date}, or null otherwise. 1427e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1437e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final Date getDateOrNull() { 1447e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!isDate()) { 1457e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return null; 1467e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return mParsedDate; 1487e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1507e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether the value case-insensitively equals to {@code s}. 1527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean is(String s) { 1547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (s == null) { 1557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().equalsIgnoreCase(s); 1587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether the value case-insensitively starts with {@code s}. 1637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean startsWith(String prefix) { 1657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (prefix == null) { 1667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final String me = this.getString(); 1697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (me.length() < prefix.length()) { 1707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1717e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1727e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return me.substring(0, prefix.length()).equalsIgnoreCase(prefix); 1737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1747e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1757e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // To force subclasses to implement it. 1767e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 1777e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract String toString(); 1787e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1797e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 1807e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean equalsForTest(ImapElement that) { 1817e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!super.equalsForTest(that)) { 1827e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1837e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1847e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki ImapString thatString = (ImapString) that; 1857e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().equals(thatString.getString()); 1867e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1877e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki} 188