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; 205c523858385176c33a7456bb84035de78552d22dMarc Blank 215c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.util.Log; 225c523858385176c33a7456bb84035de78552d22dMarc Blank 235c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.ByteArrayInputStream; 245c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.InputStream; 255c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.text.ParseException; 265c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.text.SimpleDateFormat; 275c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.util.Date; 285c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.util.Locale; 295c523858385176c33a7456bb84035de78552d22dMarc Blank 305c523858385176c33a7456bb84035de78552d22dMarc Blank/** 315c523858385176c33a7456bb84035de78552d22dMarc Blank * Class represents an IMAP "element" that is not a list. 325c523858385176c33a7456bb84035de78552d22dMarc Blank * 335c523858385176c33a7456bb84035de78552d22dMarc Blank * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too. 345c523858385176c33a7456bb84035de78552d22dMarc Blank * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]". 355c523858385176c33a7456bb84035de78552d22dMarc Blank * See {@link ImapResponseParser}. 365c523858385176c33a7456bb84035de78552d22dMarc Blank */ 375c523858385176c33a7456bb84035de78552d22dMarc Blankpublic abstract class ImapString extends ImapElement { 385c523858385176c33a7456bb84035de78552d22dMarc Blank private static final byte[] EMPTY_BYTES = new byte[0]; 395c523858385176c33a7456bb84035de78552d22dMarc Blank 405c523858385176c33a7456bb84035de78552d22dMarc Blank public static final ImapString EMPTY = new ImapString() { 415c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public void destroy() { 425c523858385176c33a7456bb84035de78552d22dMarc Blank // Don't call super.destroy(). 435c523858385176c33a7456bb84035de78552d22dMarc Blank // It's a shared object. We don't want the mDestroyed to be set on this. 445c523858385176c33a7456bb84035de78552d22dMarc Blank } 455c523858385176c33a7456bb84035de78552d22dMarc Blank 465c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public String getString() { 475c523858385176c33a7456bb84035de78552d22dMarc Blank return ""; 485c523858385176c33a7456bb84035de78552d22dMarc Blank } 495c523858385176c33a7456bb84035de78552d22dMarc Blank 505c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public InputStream getAsStream() { 515c523858385176c33a7456bb84035de78552d22dMarc Blank return new ByteArrayInputStream(EMPTY_BYTES); 525c523858385176c33a7456bb84035de78552d22dMarc Blank } 535c523858385176c33a7456bb84035de78552d22dMarc Blank 545c523858385176c33a7456bb84035de78552d22dMarc Blank @Override public String toString() { 555c523858385176c33a7456bb84035de78552d22dMarc Blank return ""; 565c523858385176c33a7456bb84035de78552d22dMarc Blank } 575c523858385176c33a7456bb84035de78552d22dMarc Blank }; 585c523858385176c33a7456bb84035de78552d22dMarc Blank 595c523858385176c33a7456bb84035de78552d22dMarc Blank // This is used only for parsing IMAP's FETCH ENVELOPE command, in which 605c523858385176c33a7456bb84035de78552d22dMarc Blank // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be 615c523858385176c33a7456bb84035de78552d22dMarc Blank // handled by Locale.US 625c523858385176c33a7456bb84035de78552d22dMarc Blank private final static SimpleDateFormat DATE_TIME_FORMAT = 635c523858385176c33a7456bb84035de78552d22dMarc Blank new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); 645c523858385176c33a7456bb84035de78552d22dMarc Blank 655c523858385176c33a7456bb84035de78552d22dMarc Blank private boolean mIsInteger; 665c523858385176c33a7456bb84035de78552d22dMarc Blank private int mParsedInteger; 675c523858385176c33a7456bb84035de78552d22dMarc Blank private Date mParsedDate; 685c523858385176c33a7456bb84035de78552d22dMarc Blank 695c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 705c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isList() { 715c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 725c523858385176c33a7456bb84035de78552d22dMarc Blank } 735c523858385176c33a7456bb84035de78552d22dMarc Blank 745c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 755c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isString() { 765c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 775c523858385176c33a7456bb84035de78552d22dMarc Blank } 785c523858385176c33a7456bb84035de78552d22dMarc Blank 795c523858385176c33a7456bb84035de78552d22dMarc Blank /** 805c523858385176c33a7456bb84035de78552d22dMarc Blank * @return true if and only if the length of the string is larger than 0. 815c523858385176c33a7456bb84035de78552d22dMarc Blank * 825c523858385176c33a7456bb84035de78552d22dMarc Blank * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser 835c523858385176c33a7456bb84035de78552d22dMarc Blank * #parseBareString}. 845c523858385176c33a7456bb84035de78552d22dMarc Blank * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is 855c523858385176c33a7456bb84035de78552d22dMarc Blank * treated literally. 865c523858385176c33a7456bb84035de78552d22dMarc Blank */ 875c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isEmpty() { 885c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().length() == 0; 895c523858385176c33a7456bb84035de78552d22dMarc Blank } 905c523858385176c33a7456bb84035de78552d22dMarc Blank 915c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract String getString(); 925c523858385176c33a7456bb84035de78552d22dMarc Blank 935c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract InputStream getAsStream(); 945c523858385176c33a7456bb84035de78552d22dMarc Blank 955c523858385176c33a7456bb84035de78552d22dMarc Blank /** 965c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether it can be parsed as a number. 975c523858385176c33a7456bb84035de78552d22dMarc Blank */ 985c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isNumber() { 995c523858385176c33a7456bb84035de78552d22dMarc Blank if (mIsInteger) { 1005c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1015c523858385176c33a7456bb84035de78552d22dMarc Blank } 1025c523858385176c33a7456bb84035de78552d22dMarc Blank try { 1035c523858385176c33a7456bb84035de78552d22dMarc Blank mParsedInteger = Integer.parseInt(getString()); 1045c523858385176c33a7456bb84035de78552d22dMarc Blank mIsInteger = true; 1055c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1065c523858385176c33a7456bb84035de78552d22dMarc Blank } catch (NumberFormatException e) { 1075c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1085c523858385176c33a7456bb84035de78552d22dMarc Blank } 1095c523858385176c33a7456bb84035de78552d22dMarc Blank } 1105c523858385176c33a7456bb84035de78552d22dMarc Blank 1115c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1125c523858385176c33a7456bb84035de78552d22dMarc Blank * @return value parsed as a number. 1135c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1145c523858385176c33a7456bb84035de78552d22dMarc Blank public final int getNumberOrZero() { 1155c523858385176c33a7456bb84035de78552d22dMarc Blank if (!isNumber()) { 1165c523858385176c33a7456bb84035de78552d22dMarc Blank return 0; 1175c523858385176c33a7456bb84035de78552d22dMarc Blank } 1185c523858385176c33a7456bb84035de78552d22dMarc Blank return mParsedInteger; 1195c523858385176c33a7456bb84035de78552d22dMarc Blank } 1205c523858385176c33a7456bb84035de78552d22dMarc Blank 1215c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1225c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}. 1235c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1245c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean isDate() { 1255c523858385176c33a7456bb84035de78552d22dMarc Blank if (mParsedDate != null) { 1265c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1275c523858385176c33a7456bb84035de78552d22dMarc Blank } 1285c523858385176c33a7456bb84035de78552d22dMarc Blank if (isEmpty()) { 1295c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1305c523858385176c33a7456bb84035de78552d22dMarc Blank } 1315c523858385176c33a7456bb84035de78552d22dMarc Blank try { 1325c523858385176c33a7456bb84035de78552d22dMarc Blank mParsedDate = DATE_TIME_FORMAT.parse(getString()); 1335c523858385176c33a7456bb84035de78552d22dMarc Blank return true; 1345c523858385176c33a7456bb84035de78552d22dMarc Blank } catch (ParseException e) { 1355c523858385176c33a7456bb84035de78552d22dMarc Blank Log.w(Logging.LOG_TAG, getString() + " can't be parsed as a date."); 1365c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1375c523858385176c33a7456bb84035de78552d22dMarc Blank } 1385c523858385176c33a7456bb84035de78552d22dMarc Blank } 1395c523858385176c33a7456bb84035de78552d22dMarc Blank 1405c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1415c523858385176c33a7456bb84035de78552d22dMarc Blank * @return value it can be parsed as a {@link Date}, or null otherwise. 1425c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1435c523858385176c33a7456bb84035de78552d22dMarc Blank public final Date getDateOrNull() { 1445c523858385176c33a7456bb84035de78552d22dMarc Blank if (!isDate()) { 1455c523858385176c33a7456bb84035de78552d22dMarc Blank return null; 1465c523858385176c33a7456bb84035de78552d22dMarc Blank } 1475c523858385176c33a7456bb84035de78552d22dMarc Blank return mParsedDate; 1485c523858385176c33a7456bb84035de78552d22dMarc Blank } 1495c523858385176c33a7456bb84035de78552d22dMarc Blank 1505c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1515c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether the value case-insensitively equals to {@code s}. 1525c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1535c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean is(String s) { 1545c523858385176c33a7456bb84035de78552d22dMarc Blank if (s == null) { 1555c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1565c523858385176c33a7456bb84035de78552d22dMarc Blank } 1575c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().equalsIgnoreCase(s); 1585c523858385176c33a7456bb84035de78552d22dMarc Blank } 1595c523858385176c33a7456bb84035de78552d22dMarc Blank 1605c523858385176c33a7456bb84035de78552d22dMarc Blank 1615c523858385176c33a7456bb84035de78552d22dMarc Blank /** 1625c523858385176c33a7456bb84035de78552d22dMarc Blank * @return whether the value case-insensitively starts with {@code s}. 1635c523858385176c33a7456bb84035de78552d22dMarc Blank */ 1645c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean startsWith(String prefix) { 1655c523858385176c33a7456bb84035de78552d22dMarc Blank if (prefix == null) { 1665c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1675c523858385176c33a7456bb84035de78552d22dMarc Blank } 1685c523858385176c33a7456bb84035de78552d22dMarc Blank final String me = this.getString(); 1695c523858385176c33a7456bb84035de78552d22dMarc Blank if (me.length() < prefix.length()) { 1705c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1715c523858385176c33a7456bb84035de78552d22dMarc Blank } 1725c523858385176c33a7456bb84035de78552d22dMarc Blank return me.substring(0, prefix.length()).equalsIgnoreCase(prefix); 1735c523858385176c33a7456bb84035de78552d22dMarc Blank } 1745c523858385176c33a7456bb84035de78552d22dMarc Blank 1755c523858385176c33a7456bb84035de78552d22dMarc Blank // To force subclasses to implement it. 1765c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 1775c523858385176c33a7456bb84035de78552d22dMarc Blank public abstract String toString(); 1785c523858385176c33a7456bb84035de78552d22dMarc Blank 1795c523858385176c33a7456bb84035de78552d22dMarc Blank @Override 1805c523858385176c33a7456bb84035de78552d22dMarc Blank public final boolean equalsForTest(ImapElement that) { 1815c523858385176c33a7456bb84035de78552d22dMarc Blank if (!super.equalsForTest(that)) { 1825c523858385176c33a7456bb84035de78552d22dMarc Blank return false; 1835c523858385176c33a7456bb84035de78552d22dMarc Blank } 1845c523858385176c33a7456bb84035de78552d22dMarc Blank ImapString thatString = (ImapString) that; 1855c523858385176c33a7456bb84035de78552d22dMarc Blank return getString().equals(thatString.getString()); 1865c523858385176c33a7456bb84035de78552d22dMarc Blank } 1875c523858385176c33a7456bb84035de78552d22dMarc Blank} 188