ImapString.java revision 7e5ba0e1eaee76ab6e6c7ea9362348f660796596
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 197e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport com.android.email.Email; 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() { 417e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public String getString() { 427e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return ""; 437e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 447e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 457e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public InputStream getAsStream() { 467e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return new ByteArrayInputStream(EMPTY_BYTES); 477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 487e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override public String toString() { 507e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return ""; 517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki }; 537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // This is used only for parsing IMAP's FETCH ENVELOPE command, in which 557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be 567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // handled by Locale.US 577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private final static SimpleDateFormat DATE_TIME_FORMAT = 587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); 597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private boolean mIsInteger; 617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private int mParsedInteger; 627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private Date mParsedDate; 637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isList() { 667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isString() { 717e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 727e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 747e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 757e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return true if and only if the length of the string is larger than 0. 767e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * 777e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser 787e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * #parseBareString}. 797e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is 807e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * treated literally. 817e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 827e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isEmpty() { 837e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().length() == 0; 847e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 857e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 867e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract String getString(); 877e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 887e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract InputStream getAsStream(); 897e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 907e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 917e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether it can be parsed as a number. 927e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 937e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isNumber() { 947e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (mIsInteger) { 957e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 967e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 977e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki try { 987e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mParsedInteger = Integer.parseInt(getString()); 997e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mIsInteger = true; 1007e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1017e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } catch (NumberFormatException e) { 1027e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1037e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1047e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1057e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1067e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1077e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return value parsed as a number. 1087e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1097e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final int getNumberOrZero() { 1107e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!isNumber()) { 1117e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return 0; 1127e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1137e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return mParsedInteger; 1147e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1157e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1167e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1177e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}. 1187e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1197e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean isDate() { 1207e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (mParsedDate != null) { 1217e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1227e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1237e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (isEmpty()) { 1247e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1257e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1267e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki try { 1277e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki mParsedDate = DATE_TIME_FORMAT.parse(getString()); 1287e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return true; 1297e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } catch (ParseException e) { 1307e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki Log.w(Email.LOG_TAG, getString() + " can't be parsed as a date."); 1317e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1327e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1337e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1347e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1357e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1367e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return value it can be parsed as a {@link Date}, or null otherwise. 1377e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1387e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final Date getDateOrNull() { 1397e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!isDate()) { 1407e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return null; 1417e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1427e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return mParsedDate; 1437e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1447e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1457e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1467e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether the value case-insensitively equals to {@code s}. 1477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1487e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean is(String s) { 1497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (s == null) { 1507e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().equalsIgnoreCase(s); 1537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** 1577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki * @return whether the value case-insensitively starts with {@code s}. 1587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki */ 1597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean startsWith(String prefix) { 1607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (prefix == null) { 1617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final String me = this.getString(); 1647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (me.length() < prefix.length()) { 1657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return me.substring(0, prefix.length()).equalsIgnoreCase(prefix); 1687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki // To force subclasses to implement it. 1717e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 1727e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public abstract String toString(); 1737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 1747e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki @Override 1757e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public final boolean equalsForTest(ImapElement that) { 1767e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki if (!super.equalsForTest(that)) { 1777e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return false; 1787e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1797e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki ImapString thatString = (ImapString) that; 1807e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return getString().equals(thatString.getString()); 1817e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 1827e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki} 183