15c523858385176c33a7456bb84035de78552d22dMarc Blank/* 25c523858385176c33a7456bb84035de78552d22dMarc Blank * Copyright (C) 2010 The Android Open Source Project 35c523858385176c33a7456bb84035de78552d22dMarc Blank * 45c523858385176c33a7456bb84035de78552d22dMarc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 55c523858385176c33a7456bb84035de78552d22dMarc Blank * you may not use this file except in compliance with the License. 65c523858385176c33a7456bb84035de78552d22dMarc Blank * You may obtain a copy of the License at 75c523858385176c33a7456bb84035de78552d22dMarc Blank * 85c523858385176c33a7456bb84035de78552d22dMarc Blank * http://www.apache.org/licenses/LICENSE-2.0 95c523858385176c33a7456bb84035de78552d22dMarc Blank * 105c523858385176c33a7456bb84035de78552d22dMarc Blank * Unless required by applicable law or agreed to in writing, software 115c523858385176c33a7456bb84035de78552d22dMarc Blank * distributed under the License is distributed on an "AS IS" BASIS, 125c523858385176c33a7456bb84035de78552d22dMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135c523858385176c33a7456bb84035de78552d22dMarc Blank * See the License for the specific language governing permissions and 145c523858385176c33a7456bb84035de78552d22dMarc Blank * limitations under the License. 155c523858385176c33a7456bb84035de78552d22dMarc Blank */ 165c523858385176c33a7456bb84035de78552d22dMarc Blank 175c523858385176c33a7456bb84035de78552d22dMarc Blankpackage com.android.email.mail.store.imap; 185c523858385176c33a7456bb84035de78552d22dMarc Blank 195c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.Logging; 20560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 215c523858385176c33a7456bb84035de78552d22dMarc Blank 225c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.ByteArrayInputStream; 235c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.InputStream; 245c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.text.ParseException; 255c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.text.SimpleDateFormat; 265c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.util.Date; 275c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.util.Locale; 285c523858385176c33a7456bb84035de78552d22dMarc Blank 295c523858385176c33a7456bb84035de78552d22dMarc Blank/** 305c523858385176c33a7456bb84035de78552d22dMarc Blank * Class represents an IMAP "element" that is not a list. 315c523858385176c33a7456bb84035de78552d22dMarc Blank * 325c523858385176c33a7456bb84035de78552d22dMarc Blank * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too. 335c523858385176c33a7456bb84035de78552d22dMarc Blank * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]". 345c523858385176c33a7456bb84035de78552d22dMarc Blank * See {@link ImapResponseParser}. 355c523858385176c33a7456bb84035de78552d22dMarc Blank */ 365c523858385176c33a7456bb84035de78552d22dMarc Blankpublic abstract class ImapString extends ImapElement { 375c523858385176c33a7456bb84035de78552d22dMarc Blank private static final byte[] EMPTY_BYTES = new byte[0]; 385c523858385176c33a7456bb84035de78552d22dMarc Blank 395c523858385176c33a7456bb84035de78552d22dMarc Blank public static final ImapString EMPTY = new ImapString() { 405c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public void destroy() { 415c523858385176c33a7456bb84035de78552d22dMarc Blank // Don't call super.destroy(). 425c523858385176c33a7456bb84035de78552d22dMarc Blank // It's a shared object. We don't want the mDestroyed to be set on this. 435c523858385176c33a7456bb84035de78552d22dMarc Blank } 445c523858385176c33a7456bb84035de78552d22dMarc Blank 455c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public String getString() { 465c523858385176c33a7456bb84035de78552d22dMarc Blank return ""; 475c523858385176c33a7456bb84035de78552d22dMarc Blank } 485c523858385176c33a7456bb84035de78552d22dMarc Blank 495c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public InputStream getAsStream() { 505c523858385176c33a7456bb84035de78552d22dMarc Blank return new ByteArrayInputStream(EMPTY_BYTES); 515c523858385176c33a7456bb84035de78552d22dMarc Blank } 525c523858385176c33a7456bb84035de78552d22dMarc Blank 535c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public String toString() { 545c523858385176c33a7456bb84035de78552d22dMarc Blank return ""; 555c523858385176c33a7456bb84035de78552d22dMarc Blank } 565c523858385176c33a7456bb84035de78552d22dMarc Blank }; 575c523858385176c33a7456bb84035de78552d22dMarc Blank 585c523858385176c33a7456bb84035de78552d22dMarc Blank // This is used only for parsing IMAP's FETCH ENVELOPE command, in which 595c523858385176c33a7456bb84035de78552d22dMarc Blank // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be 605c523858385176c33a7456bb84035de78552d22dMarc Blank // handled by Locale.US 615c523858385176c33a7456bb84035de78552d22dMarc Blank private final static SimpleDateFormat DATE_TIME_FORMAT = 625c523858385176c33a7456bb84035de78552d22dMarc Blank new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); 635c523858385176c33a7456bb84035de78552d22dMarc Blank 645c523858385176c33a7456bb84035de78552d22dMarc Blank private boolean mIsInteger; 655c523858385176c33a7456bb84035de78552d22dMarc Blank private int mParsedInteger; 665c523858385176c33a7456bb84035de78552d22dMarc Blank private Date mParsedDate; 675c523858385176c33a7456bb84035de78552d22dMarc Blank 685c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 695c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isList() { 705c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 715c523858385176c33a7456bb84035de78552d22dMarc Blank } 725c523858385176c33a7456bb84035de78552d22dMarc Blank 735c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 745c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isString() { 755c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 765c523858385176c33a7456bb84035de78552d22dMarc Blank } 775c523858385176c33a7456bb84035de78552d22dMarc Blank 785c523858385176c33a7456bb84035de78552d22dMarc Blank /** 795c523858385176c33a7456bb84035de78552d22dMarc Blank * @return true if and only if the length of the string is larger than 0. 805c523858385176c33a7456bb84035de78552d22dMarc Blank * 815c523858385176c33a7456bb84035de78552d22dMarc Blank * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser 825c523858385176c33a7456bb84035de78552d22dMarc Blank * #parseBareString}. 835c523858385176c33a7456bb84035de78552d22dMarc Blank * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is 845c523858385176c33a7456bb84035de78552d22dMarc Blank * treated literally. 855c523858385176c33a7456bb84035de78552d22dMarc Blank */ 865c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isEmpty() { 875c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().length() == 0; 885c523858385176c33a7456bb84035de78552d22dMarc Blank } 895c523858385176c33a7456bb84035de78552d22dMarc Blank 905c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract String getString(); 915c523858385176c33a7456bb84035de78552d22dMarc Blank 925c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract InputStream getAsStream(); 935c523858385176c33a7456bb84035de78552d22dMarc Blank 945c523858385176c33a7456bb84035de78552d22dMarc Blank /** 955c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether it can be parsed as a number. 965c523858385176c33a7456bb84035de78552d22dMarc Blank */ 975c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isNumber() { 985c523858385176c33a7456bb84035de78552d22dMarc Blank if (mIsInteger) { 995c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1005c523858385176c33a7456bb84035de78552d22dMarc Blank } 1015c523858385176c33a7456bb84035de78552d22dMarc Blank try { 1025c523858385176c33a7456bb84035de78552d22dMarc Blank mParsedInteger = Integer.parseInt(getString()); 1035c523858385176c33a7456bb84035de78552d22dMarc Blank mIsInteger = true; 1045c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1055c523858385176c33a7456bb84035de78552d22dMarc Blank } catch (NumberFormatException e) { 1065c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1075c523858385176c33a7456bb84035de78552d22dMarc Blank } 1085c523858385176c33a7456bb84035de78552d22dMarc Blank } 1095c523858385176c33a7456bb84035de78552d22dMarc Blank 1105c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1115c523858385176c33a7456bb84035de78552d22dMarc Blank * @return value parsed as a number. 1125c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1135c523858385176c33a7456bb84035de78552d22dMarc Blank public final int getNumberOrZero() { 1145c523858385176c33a7456bb84035de78552d22dMarc Blank if (!isNumber()) { 1155c523858385176c33a7456bb84035de78552d22dMarc Blank return 0; 1165c523858385176c33a7456bb84035de78552d22dMarc Blank } 1175c523858385176c33a7456bb84035de78552d22dMarc Blank return mParsedInteger; 1185c523858385176c33a7456bb84035de78552d22dMarc Blank } 1195c523858385176c33a7456bb84035de78552d22dMarc Blank 1205c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1215c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}. 1225c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1235c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isDate() { 1245c523858385176c33a7456bb84035de78552d22dMarc Blank if (mParsedDate != null) { 1255c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1265c523858385176c33a7456bb84035de78552d22dMarc Blank } 1275c523858385176c33a7456bb84035de78552d22dMarc Blank if (isEmpty()) { 1285c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1295c523858385176c33a7456bb84035de78552d22dMarc Blank } 1305c523858385176c33a7456bb84035de78552d22dMarc Blank try { 1315c523858385176c33a7456bb84035de78552d22dMarc Blank mParsedDate = DATE_TIME_FORMAT.parse(getString()); 1325c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1335c523858385176c33a7456bb84035de78552d22dMarc Blank } catch (ParseException e) { 134560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.w(Logging.LOG_TAG, getString() + " can't be parsed as a date."); 1355c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1365c523858385176c33a7456bb84035de78552d22dMarc Blank } 1375c523858385176c33a7456bb84035de78552d22dMarc Blank } 1385c523858385176c33a7456bb84035de78552d22dMarc Blank 1395c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1405c523858385176c33a7456bb84035de78552d22dMarc Blank * @return value it can be parsed as a {@link Date}, or null otherwise. 1415c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1425c523858385176c33a7456bb84035de78552d22dMarc Blank public final Date getDateOrNull() { 1435c523858385176c33a7456bb84035de78552d22dMarc Blank if (!isDate()) { 1445c523858385176c33a7456bb84035de78552d22dMarc Blank return null; 1455c523858385176c33a7456bb84035de78552d22dMarc Blank } 1465c523858385176c33a7456bb84035de78552d22dMarc Blank return mParsedDate; 1475c523858385176c33a7456bb84035de78552d22dMarc Blank } 1485c523858385176c33a7456bb84035de78552d22dMarc Blank 1495c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1505c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether the value case-insensitively equals to {@code s}. 1515c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1525c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean is(String s) { 1535c523858385176c33a7456bb84035de78552d22dMarc Blank if (s == null) { 1545c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1555c523858385176c33a7456bb84035de78552d22dMarc Blank } 1565c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().equalsIgnoreCase(s); 1575c523858385176c33a7456bb84035de78552d22dMarc Blank } 1585c523858385176c33a7456bb84035de78552d22dMarc Blank 1595c523858385176c33a7456bb84035de78552d22dMarc Blank 1605c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1615c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether the value case-insensitively starts with {@code s}. 1625c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1635c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean startsWith(String prefix) { 1645c523858385176c33a7456bb84035de78552d22dMarc Blank if (prefix == null) { 1655c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1665c523858385176c33a7456bb84035de78552d22dMarc Blank } 1675c523858385176c33a7456bb84035de78552d22dMarc Blank final String me = this.getString(); 1685c523858385176c33a7456bb84035de78552d22dMarc Blank if (me.length() < prefix.length()) { 1695c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1705c523858385176c33a7456bb84035de78552d22dMarc Blank } 1715c523858385176c33a7456bb84035de78552d22dMarc Blank return me.substring(0, prefix.length()).equalsIgnoreCase(prefix); 1725c523858385176c33a7456bb84035de78552d22dMarc Blank } 1735c523858385176c33a7456bb84035de78552d22dMarc Blank 1745c523858385176c33a7456bb84035de78552d22dMarc Blank // To force subclasses to implement it. 1755c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 1765c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract String toString(); 1775c523858385176c33a7456bb84035de78552d22dMarc Blank 1785c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 1795c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean equalsForTest(ImapElement that) { 1805c523858385176c33a7456bb84035de78552d22dMarc Blank if (!super.equalsForTest(that)) { 1815c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1825c523858385176c33a7456bb84035de78552d22dMarc Blank } 1835c523858385176c33a7456bb84035de78552d22dMarc Blank ImapString thatString = (ImapString) that; 1845c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().equals(thatString.getString()); 1855c523858385176c33a7456bb84035de78552d22dMarc Blank } 1865c523858385176c33a7456bb84035de78552d22dMarc Blank} 187