1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.email.mail.store.imap; 18 19 import com.android.emailcommon.Logging; 20 import com.android.mail.utils.LogUtils; 21 22 import java.io.ByteArrayInputStream; 23 import java.io.InputStream; 24 import java.text.ParseException; 25 import java.text.SimpleDateFormat; 26 import java.util.Date; 27 import java.util.Locale; 28 29 /** 30 * Class represents an IMAP "element" that is not a list. 31 * 32 * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too. 33 * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]". 34 * See {@link ImapResponseParser}. 35 */ 36 public abstract class ImapString extends ImapElement { 37 private static final byte[] EMPTY_BYTES = new byte[0]; 38 39 public static final ImapString EMPTY = new ImapString() { 40 @Override public void destroy() { 41 // Don't call super.destroy(). 42 // It's a shared object. We don't want the mDestroyed to be set on this. 43 } 44 45 @Override public String getString() { 46 return ""; 47 } 48 49 @Override public InputStream getAsStream() { 50 return new ByteArrayInputStream(EMPTY_BYTES); 51 } 52 53 @Override public String toString() { 54 return ""; 55 } 56 }; 57 58 // This is used only for parsing IMAP's FETCH ENVELOPE command, in which 59 // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be 60 // handled by Locale.US 61 private final static SimpleDateFormat DATE_TIME_FORMAT = 62 new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); 63 64 private boolean mIsInteger; 65 private int mParsedInteger; 66 private Date mParsedDate; 67 68 @Override isList()69 public final boolean isList() { 70 return false; 71 } 72 73 @Override isString()74 public final boolean isString() { 75 return true; 76 } 77 78 /** 79 * @return true if and only if the length of the string is larger than 0. 80 * 81 * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser 82 * #parseBareString}. 83 * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is 84 * treated literally. 85 */ isEmpty()86 public final boolean isEmpty() { 87 return getString().length() == 0; 88 } 89 getString()90 public abstract String getString(); 91 getAsStream()92 public abstract InputStream getAsStream(); 93 94 /** 95 * @return whether it can be parsed as a number. 96 */ isNumber()97 public final boolean isNumber() { 98 if (mIsInteger) { 99 return true; 100 } 101 try { 102 mParsedInteger = Integer.parseInt(getString()); 103 mIsInteger = true; 104 return true; 105 } catch (NumberFormatException e) { 106 return false; 107 } 108 } 109 110 /** 111 * @return value parsed as a number. 112 */ getNumberOrZero()113 public final int getNumberOrZero() { 114 if (!isNumber()) { 115 return 0; 116 } 117 return mParsedInteger; 118 } 119 120 /** 121 * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}. 122 */ isDate()123 public final boolean isDate() { 124 if (mParsedDate != null) { 125 return true; 126 } 127 if (isEmpty()) { 128 return false; 129 } 130 try { 131 mParsedDate = DATE_TIME_FORMAT.parse(getString()); 132 return true; 133 } catch (ParseException e) { 134 LogUtils.w(Logging.LOG_TAG, getString() + " can't be parsed as a date."); 135 return false; 136 } 137 } 138 139 /** 140 * @return value it can be parsed as a {@link Date}, or null otherwise. 141 */ getDateOrNull()142 public final Date getDateOrNull() { 143 if (!isDate()) { 144 return null; 145 } 146 return mParsedDate; 147 } 148 149 /** 150 * @return whether the value case-insensitively equals to {@code s}. 151 */ is(String s)152 public final boolean is(String s) { 153 if (s == null) { 154 return false; 155 } 156 return getString().equalsIgnoreCase(s); 157 } 158 159 160 /** 161 * @return whether the value case-insensitively starts with {@code s}. 162 */ startsWith(String prefix)163 public final boolean startsWith(String prefix) { 164 if (prefix == null) { 165 return false; 166 } 167 final String me = this.getString(); 168 if (me.length() < prefix.length()) { 169 return false; 170 } 171 return me.substring(0, prefix.length()).equalsIgnoreCase(prefix); 172 } 173 174 // To force subclasses to implement it. 175 @Override toString()176 public abstract String toString(); 177 178 @Override equalsForTest(ImapElement that)179 public final boolean equalsForTest(ImapElement that) { 180 if (!super.equalsForTest(that)) { 181 return false; 182 } 183 ImapString thatString = (ImapString) that; 184 return getString().equals(thatString.getString()); 185 } 186 } 187