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