diff --git a/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java b/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java index f5ac5e0d17..1551cd3e0f 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java @@ -79,7 +79,7 @@ public class ContactAccessor { } public Cursor getAllSystemContacts(Context context) { - return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME}, null, null, null); + return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY}, null, null, null); } public boolean isSystemContact(Context context, String number) { diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 01512adac6..dbb8826232 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -16,6 +16,7 @@ */ package org.thoughtcrime.securesms.database; +import android.Manifest; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -23,6 +24,8 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.provider.ContactsContract; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -40,6 +43,7 @@ import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.Hex; @@ -110,7 +114,8 @@ public class DatabaseFactory { private static final int READ_RECEIPTS = 44; private static final int GROUP_RECEIPT_TRACKING = 45; private static final int UNREAD_COUNT_VERSION = 46; - private static final int DATABASE_VERSION = 46; + private static final int MORE_RECIPIENT_FIELDS = 47; + private static final int DATABASE_VERSION = 47; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -1374,6 +1379,42 @@ public class DatabaseFactory { } } + if (oldVersion < MORE_RECIPIENT_FIELDS) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_photo TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_phone_label TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_uri TEXT DEFAULT NULL"); + + if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { + try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"))); + + if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) { + Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); + + try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME, + ContactsContract.PhoneLookup.LOOKUP_KEY, + ContactsContract.PhoneLookup._ID, + ContactsContract.PhoneLookup.NUMBER, + ContactsContract.PhoneLookup.LABEL, + ContactsContract.PhoneLookup.PHOTO_URI}, + null, null, null)) + { + if (contactCursor != null && contactCursor.moveToFirst()) { + ContentValues contentValues = new ContentValues(3); + contentValues.put("system_contact_photo", contactCursor.getString(5)); + contentValues.put("system_phone_label", contactCursor.getString(4)); + contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString()); + + db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()}); + } + } + } + } + } + } + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java index 99576a1fc3..3ef8ca55c6 100644 --- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -14,7 +14,6 @@ import android.text.TextUtils; import com.annimon.stream.Stream; -import org.thoughtcrime.securesms.contacts.avatars.GroupRecordContactPhoto; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.GroupUtil; @@ -175,12 +174,11 @@ public class GroupDatabase extends Database { databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues); - Address address = Address.fromSerialized(groupId); - Recipient recipient = Recipient.from(context, Address.fromSerialized(groupId), false); - - recipient.setName(title); - if (avatar != null) recipient.setContactPhoto(new GroupRecordContactPhoto(address, avatar.getId())); - recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList()); + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + recipient.setName(title); + recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); + recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList()); + }); notifyConversationListListeners(); } @@ -200,10 +198,10 @@ public class GroupDatabase extends Database { GROUP_ID + " = ?", new String[] {groupId}); - Address address = Address.fromSerialized(groupId); - Recipient recipient = Recipient.from(context, address, false); - recipient.setName(title); - if (avatar != null) recipient.setContactPhoto(new GroupRecordContactPhoto(address, avatar.getId())); + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + recipient.setName(title); + recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); + }); notifyConversationListListeners(); } @@ -231,9 +229,7 @@ public class GroupDatabase extends Database { databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", new String[] {groupId}); - Address address = Address.fromSerialized(groupId); - Recipient recipient = Recipient.from(context, address, false); - recipient.setContactPhoto(new GroupRecordContactPhoto(address, avatarId)); + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> recipient.setGroupAvatarId(avatarId)); } public void updateMembers(String groupId, List
members) { diff --git a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java index c349c35e48..a8f8cb5efe 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -15,19 +15,20 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.util.Pair; +import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; import java.io.IOException; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; public class RecipientDatabase extends Database { private static final String TAG = RecipientDatabase.class.getSimpleName(); - private static final String RECIPIENT_PREFERENCES_URI = "content://textsecure/recipients/"; static final String TABLE_NAME = "recipient_preferences"; private static final String ID = "_id"; @@ -43,13 +44,17 @@ public class RecipientDatabase extends Database { private static final String REGISTERED = "registered"; private static final String PROFILE_KEY = "profile_key"; private static final String SYSTEM_DISPLAY_NAME = "system_display_name"; + private static final String SYSTEM_PHOTO_URI = "system_contact_photo"; + private static final String SYSTEM_PHONE_LABEL = "system_phone_label"; + private static final String SYSTEM_CONTACT_URI = "system_contact_uri"; private static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"; private static final String PROFILE_SHARING = "profile_sharing_approval"; private static final String[] RECIPIENT_PROJECTION = new String[] { BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, - PROFILE_KEY, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING + PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, + SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING }; static final ListSoftHashMap
is a memory-constrained map that stores its values in
+ * {@link SoftReference SoftReference}s. (Contrast this with the JDK's
+ * {@link WeakHashMap WeakHashMap}, which uses weak references for its keys, which is of little value if you
+ * want the cache to auto-resize itself based on memory constraints).
+ *
+ * Having the values wrapped by soft references allows the cache to automatically reduce its size based on memory
+ * limitations and garbage collection. This ensures that the cache will not cause memory leaks by holding strong
+ * references to all of its values.
+ *
+ * This class is a generics-enabled Map based on initial ideas from Heinz Kabutz's and Sydney Redelinghuys's
+ * publicly posted version (with their approval), with
+ * continued modifications.
+ *
+ * This implementation is thread-safe and usable in concurrent environments.
+ *
+ * @since 1.0
+ */
+public class SoftHashMapretentionSize
value. This number is intended to be a best-effort retention low
+ * water mark.
+ *
+ * @param retentionSize the total number of most recent entries in the map that will be strongly referenced
+ * (retained), preventing them from being eagerly garbage collected by the JVM.
+ */
+ @SuppressWarnings({"unchecked"})
+ public SoftHashMap(int retentionSize) {
+ super();
+ RETENTION_SIZE = Math.max(0, retentionSize);
+ queue = new ReferenceQueueretentionSize
value. This number is intended to be a best-effort retention low
+ * water mark.
+ *
+ * @param source the backing map to populate this {@code SoftHashMap}
+ * @param retentionSize the total number of most recent entries in the map that will be strongly referenced
+ * (retained), preventing them from being eagerly garbage collected by the JVM.
+ */
+ public SoftHashMap(MapBlockingDeque methods come in four forms, with different ways - * of handling operations that cannot be satisfied immediately, but may be - * satisfied at some point in the future: - * one throws an exception, the second returns a special value (either - * null or false, depending on the operation), the third - * blocks the current thread indefinitely until the operation can succeed, - * and the fourth blocks for only a given maximum time limit before giving - * up. These methods are summarized in the following table: - * - *
- *
First Element (Head) | - *||||
- * | Throws exception | - *Special value | - *Blocks | - *Times out | - *
Insert | - *{@link #addFirst addFirst(e)} | - *{@link #offerFirst offerFirst(e)} | - *{@link #putFirst putFirst(e)} | - *{@link #offerFirst offerFirst(e, time, unit)} | - *
Remove | - *{@link #removeFirst removeFirst()} | - *{@link #pollFirst pollFirst()} | - *{@link #takeFirst takeFirst()} | - *{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)} | - *
Examine | - *{@link #getFirst getFirst()} | - *{@link #peekFirst peekFirst()} | - *not applicable | - *not applicable | - *
Last Element (Tail) | - *||||
- * | Throws exception | - *Special value | - *Blocks | - *Times out | - *
Insert | - *{@link #addLast addLast(e)} | - *{@link #offerLast offerLast(e)} | - *{@link #putLast putLast(e)} | - *{@link #offerLast offerLast(e, time, unit)} | - *
Remove | - *{@link #removeLast() removeLast()} | - *{@link #pollLast() pollLast()} | - *{@link #takeLast takeLast()} | - *{@link #pollLast(long, TimeUnit) pollLast(time, unit)} | - *
Examine | - *{@link #getLast getLast()} | - *{@link #peekLast peekLast()} | - *not applicable | - *not applicable | - *
Like any {@link BlockingQueue}, a BlockingDeque is thread safe, - * does not permit null elements, and may (or may not) be - * capacity-constrained. - * - *
A BlockingDeque implementation may be used directly as a FIFO - * BlockingQueue. The methods inherited from the - * BlockingQueue interface are precisely equivalent to - * BlockingDeque methods as indicated in the following table: - * - *
- *
BlockingQueue Method | - *Equivalent BlockingDeque Method | - *
Insert | - *|
{@link #add add(e)} | - *{@link #addLast addLast(e)} | - *
{@link #offer offer(e)} | - *{@link #offerLast offerLast(e)} | - *
{@link #put put(e)} | - *{@link #putLast putLast(e)} | - *
{@link #offer offer(e, time, unit)} | - *{@link #offerLast offerLast(e, time, unit)} | - *
Remove | - *|
{@link #remove() remove()} | - *{@link #removeFirst() removeFirst()} | - *
{@link #poll() poll()} | - *{@link #pollFirst() pollFirst()} | - *
{@link #take() take()} | - *{@link #takeFirst() takeFirst()} | - *
{@link #poll(long, TimeUnit) poll(time, unit)} | - *{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)} | - *
Examine | - *|
{@link #element() element()} | - *{@link #getFirst() getFirst()} | - *
{@link #peek() peek()} | - *{@link #peekFirst() peekFirst()} | - *
Memory consistency effects: As with other concurrent - * collections, actions in a thread prior to placing an object into a - * {@code BlockingDeque} - * happen-before - * actions subsequent to the access or removal of that element from - * the {@code BlockingDeque} in another thread. - * - *
This interface is a member of the
- *
- * Java Collections Framework.
- *
- * @since 1.6
- * @author Doug Lea
- * @param This method is equivalent to {@link #addLast addLast}.
- *
- * @param e the element to add
- * @throws IllegalStateException {@inheritDoc}
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean add(E e);
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * true upon success and false if no space is currently
- * available. When using a capacity-restricted deque, this method is
- * generally preferable to the {@link #add} method, which can fail to
- * insert an element only by throwing an exception.
- *
- * This method is equivalent to {@link #offerLast offerLast}.
- *
- * @param e the element to add
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offer(E e);
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque), waiting if necessary for
- * space to become available.
- *
- * This method is equivalent to {@link #putLast putLast}.
- *
- * @param e the element to add
- * @throws InterruptedException {@inheritDoc}
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void put(E e) throws InterruptedException;
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque), waiting up to the
- * specified wait time if necessary for space to become available.
- *
- * This method is equivalent to
- * {@link #offerLast offerLast}.
- *
- * @param e the element to add
- * @return true if the element was added to this deque, else
- * false
- * @throws InterruptedException {@inheritDoc}
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offer(E e, long timeout, TimeUnit unit)
- throws InterruptedException;
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque).
- * This method differs from {@link #poll poll} only in that it
- * throws an exception if this deque is empty.
- *
- * This method is equivalent to {@link #removeFirst() removeFirst}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E remove();
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), or returns
- * null if this deque is empty.
- *
- * This method is equivalent to {@link #pollFirst()}.
- *
- * @return the head of this deque, or null if this deque is empty
- */
- E poll();
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), waiting if
- * necessary until an element becomes available.
- *
- * This method is equivalent to {@link #takeFirst() takeFirst}.
- *
- * @return the head of this deque
- * @throws InterruptedException if interrupted while waiting
- */
- E take() throws InterruptedException;
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), waiting up to the
- * specified wait time if necessary for an element to become available.
- *
- * This method is equivalent to
- * {@link #pollFirst(long,TimeUnit) pollFirst}.
- *
- * @return the head of this deque, or null if the
- * specified waiting time elapses before an element is available
- * @throws InterruptedException if interrupted while waiting
- */
- E poll(long timeout, TimeUnit unit)
- throws InterruptedException;
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque).
- * This method differs from {@link #peek peek} only in that it throws an
- * exception if this deque is empty.
- *
- * This method is equivalent to {@link #getFirst() getFirst}.
- *
- * @return the head of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E element();
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque), or
- * returns null if this deque is empty.
- *
- * This method is equivalent to {@link #peekFirst() peekFirst}.
- *
- * @return the head of this deque, or null if this deque is empty
- */
- E peek();
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element e such that
- * o.equals(e) (if such an element exists).
- * Returns true if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * This method is equivalent to
- * {@link #removeFirstOccurrence removeFirstOccurrence}.
- *
- * @param o element to be removed from this deque, if present
- * @return true if this deque changed as a result of the call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
- */
- boolean remove(Object o);
-
- /**
- * Returns true if this deque contains the specified element.
- * More formally, returns true if and only if this deque contains
- * at least one element e such that o.equals(e).
- *
- * @param o object to be checked for containment in this deque
- * @return true if this deque contains the specified element
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
- */
- public boolean contains(Object o);
-
- /**
- * Returns the number of elements in this deque.
- *
- * @return the number of elements in this deque
- */
- public int size();
-
- /**
- * Returns an iterator over the elements in this deque in proper sequence.
- * The elements will be returned in order from first (head) to last (tail).
- *
- * @return an iterator over the elements in this deque in proper sequence
- */
- Iterator This method is equivalent to {@link #addFirst addFirst}.
- *
- * @throws IllegalStateException {@inheritDoc}
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified element is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- void push(E e);
-}
diff --git a/src/org/thoughtcrime/securesms/util/deque/Deque.java b/src/org/thoughtcrime/securesms/util/deque/Deque.java
deleted file mode 100644
index c260941c98..0000000000
--- a/src/org/thoughtcrime/securesms/util/deque/Deque.java
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Written by Doug Lea and Josh Bloch with assistance from members of
- * JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
- */
-
-package org.thoughtcrime.securesms.util.deque;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.Stack;
-
-// BEGIN android-note
-// removed link to collections framework docs
-// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc
-// END android-note
-
-/**
- * A linear collection that supports element insertion and removal at
- * both ends. The name deque is short for "double ended queue"
- * and is usually pronounced "deck". Most Deque
- * implementations place no fixed limits on the number of elements
- * they may contain, but this interface supports capacity-restricted
- * deques as well as those with no fixed size limit.
- *
- * This interface defines methods to access the elements at both
- * ends of the deque. Methods are provided to insert, remove, and
- * examine the element. Each of these methods exists in two forms:
- * one throws an exception if the operation fails, the other returns a
- * special value (either null or false, depending on
- * the operation). The latter form of the insert operation is
- * designed specifically for use with capacity-restricted
- * Deque implementations; in most implementations, insert
- * operations cannot fail.
- *
- * The twelve methods described above are summarized in the
- * following table:
- *
- *
- * This interface extends the {@link Queue} interface. When a deque is
- * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are
- * added at the end of the deque and removed from the beginning. The methods
- * inherited from the Queue interface are precisely equivalent to
- * Deque methods as indicated in the following table:
- *
- *
- * Deques can also be used as LIFO (Last-In-First-Out) stacks. This
- * interface should be used in preference to the legacy {@link Stack} class.
- * When a deque is used as a stack, elements are pushed and popped from the
- * beginning of the deque. Stack methods are precisely equivalent to
- * Deque methods as indicated in the table below:
- *
- *
- * Note that the {@link #peek peek} method works equally well when
- * a deque is used as a queue or a stack; in either case, elements are
- * drawn from the beginning of the deque.
- *
- * This interface provides two methods to remove interior
- * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
- * {@link #removeLastOccurrence removeLastOccurrence}.
- *
- * Unlike the {@link List} interface, this interface does not
- * provide support for indexed access to elements.
- *
- * While Deque implementations are not strictly required
- * to prohibit the insertion of null elements, they are strongly
- * encouraged to do so. Users of any Deque implementations
- * that do allow null elements are strongly encouraged not to
- * take advantage of the ability to insert nulls. This is so because
- * null is used as a special return value by various methods
- * to indicated that the deque is empty.
- *
- * Deque implementations generally do not define
- * element-based versions of the equals and hashCode
- * methods, but instead inherit the identity-based versions from class
- * Object.
- *
- * @author Doug Lea
- * @author Josh Bloch
- * @since 1.6
- * @param This method is equivalent to {@link #add}.
- *
- * @param e the element to add
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void addLast(E e);
-
- /**
- * Inserts the specified element at the front of this deque unless it would
- * violate capacity restrictions. When using a capacity-restricted deque,
- * this method is generally preferable to the {@link #addFirst} method,
- * which can fail to insert an element only by throwing an exception.
- *
- * @param e the element to add
- * @return true if the element was added to this deque, else
- * false
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offerFirst(E e);
-
- /**
- * Inserts the specified element at the end of this deque unless it would
- * violate capacity restrictions. When using a capacity-restricted deque,
- * this method is generally preferable to the {@link #addLast} method,
- * which can fail to insert an element only by throwing an exception.
- *
- * @param e the element to add
- * @return true if the element was added to this deque, else
- * false
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offerLast(E e);
-
- /**
- * Retrieves and removes the first element of this deque. This method
- * differs from {@link #pollFirst pollFirst} only in that it throws an
- * exception if this deque is empty.
- *
- * @return the head of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E removeFirst();
-
- /**
- * Retrieves and removes the last element of this deque. This method
- * differs from {@link #pollLast pollLast} only in that it throws an
- * exception if this deque is empty.
- *
- * @return the tail of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E removeLast();
-
- /**
- * Retrieves and removes the first element of this deque,
- * or returns null if this deque is empty.
- *
- * @return the head of this deque, or null if this deque is empty
- */
- E pollFirst();
-
- /**
- * Retrieves and removes the last element of this deque,
- * or returns null if this deque is empty.
- *
- * @return the tail of this deque, or null if this deque is empty
- */
- E pollLast();
-
- /**
- * Retrieves, but does not remove, the first element of this deque.
- *
- * This method differs from {@link #peekFirst peekFirst} only in that it
- * throws an exception if this deque is empty.
- *
- * @return the head of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E getFirst();
-
- /**
- * Retrieves, but does not remove, the last element of this deque.
- * This method differs from {@link #peekLast peekLast} only in that it
- * throws an exception if this deque is empty.
- *
- * @return the tail of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E getLast();
-
- /**
- * Retrieves, but does not remove, the first element of this deque,
- * or returns null if this deque is empty.
- *
- * @return the head of this deque, or null if this deque is empty
- */
- E peekFirst();
-
- /**
- * Retrieves, but does not remove, the last element of this deque,
- * or returns null if this deque is empty.
- *
- * @return the tail of this deque, or null if this deque is empty
- */
- E peekLast();
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element e such that
- * (o==null ? e==null : o.equals(e))
- * (if such an element exists).
- * Returns true if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return true if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean removeFirstOccurrence(Object o);
-
- /**
- * Removes the last occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the last element e such that
- * (o==null ? e==null : o.equals(e))
- * (if such an element exists).
- * Returns true if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return true if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean removeLastOccurrence(Object o);
-
- // *** Queue methods ***
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * true upon success and throwing an
- * IllegalStateException if no space is currently available.
- * When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer offer}.
- *
- * This method is equivalent to {@link #addLast}.
- *
- * @param e the element to add
- * @return true (as specified by {@link Collection#add})
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean add(E e);
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * true upon success and false if no space is currently
- * available. When using a capacity-restricted deque, this method is
- * generally preferable to the {@link #add} method, which can fail to
- * insert an element only by throwing an exception.
- *
- * This method is equivalent to {@link #offerLast}.
- *
- * @param e the element to add
- * @return true if the element was added to this deque, else
- * false
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offer(E e);
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque).
- * This method differs from {@link #poll poll} only in that it throws an
- * exception if this deque is empty.
- *
- * This method is equivalent to {@link #removeFirst()}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E remove();
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), or returns
- * null if this deque is empty.
- *
- * This method is equivalent to {@link #pollFirst()}.
- *
- * @return the first element of this deque, or null if
- * this deque is empty
- */
- E poll();
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque).
- * This method differs from {@link #peek peek} only in that it throws an
- * exception if this deque is empty.
- *
- * This method is equivalent to {@link #getFirst()}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E element();
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque), or
- * returns null if this deque is empty.
- *
- * This method is equivalent to {@link #peekFirst()}.
- *
- * @return the head of the queue represented by this deque, or
- * null if this deque is empty
- */
- E peek();
-
-
- // *** Stack methods ***
-
- /**
- * Pushes an element onto the stack represented by this deque (in other
- * words, at the head of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * true upon success and throwing an
- * IllegalStateException if no space is currently available.
- *
- * This method is equivalent to {@link #addFirst}.
- *
- * @param e the element to push
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void push(E e);
-
- /**
- * Pops an element from the stack represented by this deque. In other
- * words, removes and returns the first element of this deque.
- *
- * This method is equivalent to {@link #removeFirst()}.
- *
- * @return the element at the front of this deque (which is the top
- * of the stack represented by this deque)
- * @throws NoSuchElementException if this deque is empty
- */
- E pop();
-
-
- // *** Collection methods ***
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element e such that
- * (o==null ? e==null : o.equals(e))
- * (if such an element exists).
- * Returns true if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * This method is equivalent to {@link #removeFirstOccurrence}.
- *
- * @param o element to be removed from this deque, if present
- * @return true if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean remove(Object o);
-
- /**
- * Returns true if this deque contains the specified element.
- * More formally, returns true if and only if this deque contains
- * at least one element e such that
- * (o==null ? e==null : o.equals(e)).
- *
- * @param o element whose presence in this deque is to be tested
- * @return true if this deque contains the specified element
- * @throws ClassCastException if the type of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean contains(Object o);
-
- /**
- * Returns the number of elements in this deque.
- *
- * @return the number of elements in this deque
- */
- public int size();
-
- /**
- * Returns an iterator over the elements in this deque in proper sequence.
- * The elements will be returned in order from first (head) to last (tail).
- *
- * @return an iterator over the elements in this deque in proper sequence
- */
- Iterator The optional capacity bound constructor argument serves as a
- * way to prevent excessive expansion. The capacity, if unspecified,
- * is equal to {@link Integer#MAX_VALUE}. Linked nodes are
- * dynamically created upon each insertion unless this would bring the
- * deque above capacity.
- *
- * Most operations run in constant time (ignoring time spent
- * blocking). Exceptions include {@link #remove(Object) remove},
- * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link
- * #removeLastOccurrence removeLastOccurrence}, {@link #contains
- * contains}, {@link #iterator iterator.remove()}, and the bulk
- * operations, all of which run in linear time.
- *
- * This class and its iterator implement all of the
- * optional methods of the {@link Collection} and {@link
- * Iterator} interfaces.
- *
- * This class is a member of the
- *
- * Java Collections Framework.
- *
- * @since 1.6
- * @author Doug Lea
- * @param This method is equivalent to {@link #addLast}.
- *
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws NullPointerException if the specified element is null
- */
- @Override
- public boolean add(E e) {
- addLast(e);
- return true;
- }
-
- /**
- * @throws NullPointerException if the specified element is null
- */
- public boolean offer(E e) {
- return offerLast(e);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- * @throws InterruptedException {@inheritDoc}
- */
- public void put(E e) throws InterruptedException {
- putLast(e);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- * @throws InterruptedException {@inheritDoc}
- */
- public boolean offer(E e, long timeout, TimeUnit unit)
- throws InterruptedException {
- return offerLast(e, timeout, unit);
- }
-
- /**
- * Retrieves and removes the head of the queue represented by this deque.
- * This method differs from {@link #poll poll} only in that it throws an
- * exception if this deque is empty.
- *
- * This method is equivalent to {@link #removeFirst() removeFirst}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- @Override
- public E remove() {
- return removeFirst();
- }
-
- public E poll() {
- return pollFirst();
- }
-
- public E take() throws InterruptedException {
- return takeFirst();
- }
-
- public E poll(long timeout, TimeUnit unit) throws InterruptedException {
- return pollFirst(timeout, unit);
- }
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque. This method differs from {@link #peek peek} only in that
- * it throws an exception if this deque is empty.
- *
- * This method is equivalent to {@link #getFirst() getFirst}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- @Override
- public E element() {
- return getFirst();
- }
-
- public E peek() {
- return peekFirst();
- }
-
- /**
- * Returns the number of additional elements that this deque can ideally
- * (in the absence of memory or resource constraints) accept without
- * blocking. This is always equal to the initial capacity of this deque
- * less the current {@code size} of this deque.
- *
- * Note that you cannot always tell if an attempt to insert
- * an element will succeed by inspecting {@code remainingCapacity}
- * because it may be the case that another thread is about to
- * insert or remove an element.
- */
- public int remainingCapacity() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return capacity - count;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * @throws UnsupportedOperationException {@inheritDoc}
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public int drainTo(Collection super E> c) {
- return drainTo(c, Integer.MAX_VALUE);
- }
-
- /**
- * @throws UnsupportedOperationException {@inheritDoc}
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public int drainTo(Collection super E> c, int maxElements) {
- if (c == null)
- throw new NullPointerException();
- if (c == this)
- throw new IllegalArgumentException();
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int n = Math.min(maxElements, count);
- for (int i = 0; i < n; i++) {
- c.add(first.item); // In this order, in case add() throws.
- unlinkFirst();
- }
- return n;
- } finally {
- lock.unlock();
- }
- }
-
- // Stack methods
-
- /**
- * @throws IllegalStateException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- */
- public void push(E e) {
- addFirst(e);
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E pop() {
- return removeFirst();
- }
-
- // Collection methods
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element {@code e} such that
- * {@code o.equals(e)} (if such an element exists).
- * Returns {@code true} if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * This method is equivalent to
- * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
- *
- * @param o element to be removed from this deque, if present
- * @return {@code true} if this deque changed as a result of the call
- */
- @Override
- public boolean remove(Object o) {
- return removeFirstOccurrence(o);
- }
-
- /**
- * Returns the number of elements in this deque.
- *
- * @return the number of elements in this deque
- */
- @Override
- public int size() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return count;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Returns {@code true} if this deque contains the specified element.
- * More formally, returns {@code true} if and only if this deque contains
- * at least one element {@code e} such that {@code o.equals(e)}.
- *
- * @param o object to be checked for containment in this deque
- * @return {@code true} if this deque contains the specified element
- */
- @Override
- public boolean contains(Object o) {
- if (o == null) return false;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- for (Node The returned array will be "safe" in that no references to it are
- * maintained by this deque. (In other words, this method must allocate
- * a new array). The caller is thus free to modify the returned array.
- *
- * This method acts as bridge between array-based and collection-based
- * APIs.
- *
- * @return an array containing all of the elements in this deque
- */
- @Override
- @SuppressWarnings("unchecked")
- public Object[] toArray() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] a = new Object[count];
- int k = 0;
- for (Node If this deque fits in the specified array with room to spare
- * (i.e., the array has more elements than this deque), the element in
- * the array immediately following the end of the deque is set to
- * {@code null}.
- *
- * Like the {@link #toArray()} method, this method acts as bridge between
- * array-based and collection-based APIs. Further, this method allows
- * precise control over the runtime type of the output array, and may,
- * under certain circumstances, be used to save allocation costs.
- *
- * Suppose {@code x} is a deque known to contain only strings.
- * The following code can be used to dump the deque into a newly
- * allocated array of {@code String}:
- *
- * The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
- *
- * @return an iterator over the elements in this deque in proper sequence
- */
- @Override
- public Iterator The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
- *
- * @return an iterator over the elements in this deque in reverse order
- */
- public Iterator
- *
- *
- *
- *
- *
- * First Element (Head)
- * Last Element (Tail)
- *
- *
- *
- * Throws exception
- * Special value
- * Throws exception
- * Special value
- *
- *
- * Insert
- * {@link #addFirst addFirst(e)}
- * {@link #offerFirst offerFirst(e)}
- * {@link #addLast addLast(e)}
- * {@link #offerLast offerLast(e)}
- *
- *
- * Remove
- * {@link #removeFirst removeFirst()}
- * {@link #pollFirst pollFirst()}
- * {@link #removeLast removeLast()}
- * {@link #pollLast pollLast()}
- *
- *
- * Examine
- * {@link #getFirst getFirst()}
- * {@link #peekFirst peekFirst()}
- * {@link #getLast getLast()}
- * {@link #peekLast peekLast()}
- *
- *
- *
- *
- *
- * Queue Method
- * Equivalent Deque Method
- *
- *
- * {@link java.util.Queue#add add(e)}
- * {@link #addLast addLast(e)}
- *
- *
- * {@link java.util.Queue#offer offer(e)}
- * {@link #offerLast offerLast(e)}
- *
- *
- * {@link java.util.Queue#remove remove()}
- * {@link #removeFirst removeFirst()}
- *
- *
- * {@link java.util.Queue#poll poll()}
- * {@link #pollFirst pollFirst()}
- *
- *
- * {@link java.util.Queue#element element()}
- * {@link #getFirst getFirst()}
- *
- *
- * {@link java.util.Queue#peek peek()}
- * {@link #peek peekFirst()}
- *
- *
- *
- *
- *
- * Stack Method
- * Equivalent Deque Method
- *
- *
- * {@link #push push(e)}
- * {@link #addFirst addFirst(e)}
- *
- *
- * {@link #pop pop()}
- * {@link #removeFirst removeFirst()}
- *
- *
- * {@link #peek peek()}
- * {@link #peekFirst peekFirst()}
- *
- * String[] y = x.toArray(new String[0]);
- *
- * Note that {@code toArray(new Object[0])} is identical in function to
- * {@code toArray()}.
- *
- * @param a the array into which the elements of the deque are to
- * be stored, if it is big enough; otherwise, a new array of the
- * same runtime type is allocated for this purpose
- * @return an array containing all of the elements in this deque
- * @throws ArrayStoreException if the runtime type of the specified array
- * is not a supertype of the runtime type of every element in
- * this deque
- * @throws NullPointerException if the specified array is null
- */
- @Override
- @SuppressWarnings("unchecked")
- public