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 java.util.ArrayList; 20 21 /** 22 * Class represents an IMAP list. 23 */ 24 public class ImapList extends ImapElement { 25 /** 26 * {@link ImapList} representing an empty list. 27 */ 28 public static final ImapList EMPTY = new ImapList() { 29 @Override public void destroy() { 30 // Don't call super.destroy(). 31 // It's a shared object. We don't want the mDestroyed to be set on this. 32 } 33 34 @Override void add(ImapElement e) { 35 throw new RuntimeException(); 36 } 37 }; 38 39 private ArrayList<ImapElement> mList = new ArrayList<ImapElement>(); 40 41 /* package */ void add(ImapElement e) { 42 if (e == null) { 43 throw new RuntimeException("Can't add null"); 44 } 45 mList.add(e); 46 } 47 48 @Override 49 public final boolean isString() { 50 return false; 51 } 52 53 @Override 54 public final boolean isList() { 55 return true; 56 } 57 58 public final int size() { 59 return mList.size(); 60 } 61 62 public final boolean isEmpty() { 63 return size() == 0; 64 } 65 66 /** 67 * Return true if the element at {@code index} exists, is string, and equals to {@code s}. 68 * (case insensitive) 69 */ 70 public final boolean is(int index, String s) { 71 return is(index, s, false); 72 } 73 74 /** 75 * Same as {@link #is(int, String)}, but does the prefix match if {@code prefixMatch}. 76 */ 77 public final boolean is(int index, String s, boolean prefixMatch) { 78 if (!prefixMatch) { 79 return getStringOrEmpty(index).is(s); 80 } else { 81 return getStringOrEmpty(index).startsWith(s); 82 } 83 } 84 85 /** 86 * Return the element at {@code index}. 87 * If {@code index} is out of range, returns {@link ImapElement#NONE}. 88 */ 89 public final ImapElement getElementOrNone(int index) { 90 return (index >= mList.size()) ? ImapElement.NONE : mList.get(index); 91 } 92 93 /** 94 * Return the element at {@code index} if it's a list. 95 * If {@code index} is out of range or not a list, returns {@link ImapList#EMPTY}. 96 */ 97 public final ImapList getListOrEmpty(int index) { 98 ImapElement el = getElementOrNone(index); 99 return el.isList() ? (ImapList) el : EMPTY; 100 } 101 102 /** 103 * Return the element at {@code index} if it's a string. 104 * If {@code index} is out of range or not a string, returns {@link ImapString#EMPTY}. 105 */ 106 public final ImapString getStringOrEmpty(int index) { 107 ImapElement el = getElementOrNone(index); 108 return el.isString() ? (ImapString) el : ImapString.EMPTY; 109 } 110 111 /** 112 * Return an element keyed by {@code key}. Return null if not found. {@code key} has to be 113 * at an even index. 114 */ 115 /* package */ final ImapElement getKeyedElementOrNull(String key, boolean prefixMatch) { 116 for (int i = 1; i < size(); i += 2) { 117 if (is(i-1, key, prefixMatch)) { 118 return mList.get(i); 119 } 120 } 121 return null; 122 } 123 124 /** 125 * Return an {@link ImapList} keyed by {@code key}. 126 * Return {@link ImapList#EMPTY} if not found. 127 */ 128 public final ImapList getKeyedListOrEmpty(String key) { 129 return getKeyedListOrEmpty(key, false); 130 } 131 132 /** 133 * Return an {@link ImapList} keyed by {@code key}. 134 * Return {@link ImapList#EMPTY} if not found. 135 */ 136 public final ImapList getKeyedListOrEmpty(String key, boolean prefixMatch) { 137 ImapElement e = getKeyedElementOrNull(key, prefixMatch); 138 return (e != null) ? ((ImapList) e) : ImapList.EMPTY; 139 } 140 141 /** 142 * Return an {@link ImapString} keyed by {@code key}. 143 * Return {@link ImapString#EMPTY} if not found. 144 */ 145 public final ImapString getKeyedStringOrEmpty(String key) { 146 return getKeyedStringOrEmpty(key, false); 147 } 148 149 /** 150 * Return an {@link ImapString} keyed by {@code key}. 151 * Return {@link ImapString#EMPTY} if not found. 152 */ 153 public final ImapString getKeyedStringOrEmpty(String key, boolean prefixMatch) { 154 ImapElement e = getKeyedElementOrNull(key, prefixMatch); 155 return (e != null) ? ((ImapString) e) : ImapString.EMPTY; 156 } 157 158 /** 159 * Return true if it contains {@code s}. 160 */ 161 public final boolean contains(String s) { 162 for (int i = 0; i < size(); i++) { 163 if (getStringOrEmpty(i).is(s)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 170 @Override 171 public void destroy() { 172 if (mList != null) { 173 for (ImapElement e : mList) { 174 e.destroy(); 175 } 176 mList = null; 177 } 178 super.destroy(); 179 } 180 181 @Override 182 public String toString() { 183 return mList.toString(); 184 } 185 186 /** 187 * Return the text representations of the contents concatenated with ",". 188 */ 189 public final String flatten() { 190 return flatten(new StringBuilder()).toString(); 191 } 192 193 /** 194 * Returns text representations (i.e. getString()) of contents joined together with 195 * "," as the separator. 196 * 197 * Only used for building the capability string passed to vendor policies. 198 * 199 * We can't use toString(), because it's for debugging (meaning the format may change any time), 200 * and it won't expand literals. 201 */ 202 private final StringBuilder flatten(StringBuilder sb) { 203 sb.append('['); 204 for (int i = 0; i < mList.size(); i++) { 205 if (i > 0) { 206 sb.append(','); 207 } 208 final ImapElement e = getElementOrNone(i); 209 if (e.isList()) { 210 getListOrEmpty(i).flatten(sb); 211 } else if (e.isString()) { 212 sb.append(getStringOrEmpty(i).getString()); 213 } 214 } 215 sb.append(']'); 216 return sb; 217 } 218 219 @Override 220 public boolean equalsForTest(ImapElement that) { 221 if (!super.equalsForTest(that)) { 222 return false; 223 } 224 ImapList thatList = (ImapList) that; 225 if (size() != thatList.size()) { 226 return false; 227 } 228 for (int i = 0; i < size(); i++) { 229 if (!mList.get(i).equalsForTest(thatList.getElementOrNone(i))) { 230 return false; 231 } 232 } 233 return true; 234 } 235 } 236