Home | History | Annotate | Download | only in imap
      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> mList = new ArrayList<ImapElement>();
     39 
     40   /* package */ void add(ImapElement e) {
     41     if (e == null) {
     42       throw new RuntimeException("Can't add null");
     43     }
     44     mList.add(e);
     45   }
     46 
     47   @Override
     48   public final boolean isString() {
     49     return false;
     50   }
     51 
     52   @Override
     53   public final boolean isList() {
     54     return true;
     55   }
     56 
     57   public final int size() {
     58     return mList.size();
     59   }
     60 
     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    */
     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}. */
     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    */
     86   public final ImapElement getElementOrNone(int index) {
     87     return (index >= mList.size()) ? ImapElement.NONE : mList.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    */
     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    */
    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    */
    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 mList.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    */
    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    */
    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    */
    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    */
    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}. */
    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
    164   public void destroy() {
    165     if (mList != null) {
    166       for (ImapElement e : mList) {
    167         e.destroy();
    168       }
    169       mList = null;
    170     }
    171     super.destroy();
    172   }
    173 
    174   @Override
    175   public String toString() {
    176     return mList.toString();
    177   }
    178 
    179   /** Return the text representations of the contents concatenated with ",". */
    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    */
    193   private final StringBuilder flatten(StringBuilder sb) {
    194     sb.append('[');
    195     for (int i = 0; i < mList.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
    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 (!mList.get(i).equalsForTest(thatList.getElementOrNone(i))) {
    221         return false;
    222       }
    223     }
    224     return true;
    225   }
    226 }
    227