com.vk.sdk.api.model.VKList.java Source code

Java tutorial

Introduction

Here is the source code for com.vk.sdk.api.model.VKList.java

Source

//
//  Copyright (c) 2014 VK.com
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy of
//  this software and associated documentation files (the "Software"), to deal in
//  the Software without restriction, including without limitation the rights to
//  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//  the Software, and to permit persons to whom the Software is furnished to do so,
//  subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

package com.vk.sdk.api.model;

import android.os.Parcel;
import android.os.Parcelable;

import com.vk.sdk.VKSdk;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.regex.Pattern;
import java.util.*;

/**
 * Universal data list for VK API.
 * This class is not thread-safe.
 * @param <T> type of stored values.
 * @see <a href="http://vk.com/dev/list">http://vk.com/dev/list</a>
 */
@SuppressWarnings({ "unchecked", "UnusedDeclaration" })
public class VKList<T extends com.vk.sdk.api.model.VKApiModel & android.os.Parcelable & com.vk.sdk.api.model.Identifiable>
        extends VKApiModel implements java.util.List<T>, Parcelable, List<T>, List<T>, List<T> {

    /**
     * The server did not return the count field.
     */
    private final static int NO_COUNT = -1;

    /**
     * Decorated list
     */
    private ArrayList<T> items = new ArrayList<T>();

    /**
     * Field {@code count} which returned by server.
     */
    private int count = NO_COUNT;

    /**
     * Creates empty list.
     */
    public VKList() {

    }

    /**
     * Creates list and fills it according with given data.
     */
    public VKList(java.util.List<? extends T> data) {
        assert data != null;
        items = new ArrayList<T>(data);
    }

    /**
     * Creates list and fills it according with data in {@code from}.
     * @param from an object that represents a list adopted in accordance with VK API format. You can use null.
     * @param clazz class represents a model that has a public constructor with {@link org.json.JSONObject} argument.
     */
    public VKList(JSONObject from, Class<? extends T> clazz) {
        fill(from, clazz);
    }

    /**
     * Creates list and fills it according with data in {@code from}.
     * @param from an array of items in the list. You can use null.
     * @param clazz class represents a model that has a public constructor with {@link org.json.JSONObject} argument.
     */
    public VKList(JSONArray from, Class<? extends T> clazz) {
        fill(from, clazz);
    }

    /**
     * Creates list and fills it according with data in {@code from}.
     * @param from an object that represents a list adopted in accordance with VK API format. You can use null.
     * @param creator interface implementation to parse objects.
     */
    public VKList(JSONObject from, Parser<T> creator) {

        fill(from, creator);
    }

    /**
     * Creates list and fills it according with data in {@code from}.
     * @param from an array of items in the list. You can use null.
     * @param creator interface implementation to parse objects.
     */
    public VKList(JSONArray from, Parser<T> creator) {

        fill(from, creator);
    }

    /**
     * Fills list according with data in {@code from}.
     * @param from an object that represents a list adopted in accordance with VK API format. You can use null.
     * @param clazz class represents a model that has a public constructor with {@link org.json.JSONObject} argument.
     */
    public void fill(JSONObject from, Class<? extends T> clazz) {
        if (from.has("response")) {
            JSONArray array = from.optJSONArray("response");
            if (array != null) {
                fill(array, clazz);
            } else {
                fill(from.optJSONObject("response"), clazz);
            }
        } else {
            fill(from, new ReflectParser<T>(clazz));
        }
    }

    /**
     * Creates list and fills it according with data in {@code from}.
     * @param from an array of items in the list. You can use null.
     * @param clazz class represents a model that has a public constructor with {@link org.json.JSONObject} argument.
     */
    public void fill(JSONArray from, Class<? extends T> clazz) {
        fill(from, new ReflectParser<T>(clazz));
    }

    /**
     * Fills list according with data in {@code from}.
     * @param from an object that represents a list adopted in accordance with VK API format. You can use null.
     * @param creator interface implementation to parse objects.
     */
    public void fill(JSONObject from, Parser<? extends T> creator) {
        if (from != null) {
            fill(from.optJSONArray("items"), creator);
            count = from.optInt("count", count);
        }
    }

    /**
     * Fills list according with data in {@code from}.
     * @param from an array of items in the list. You can use null.
     * @param creator interface implementation to parse objects.
     */
    public void fill(JSONArray from, Parser<? extends T> creator) {
        if (from != null) {
            for (int i = 0; i < from.length(); i++) {
                try {
                    T object = creator.parseObject(from.getJSONObject(i));
                    if (object != null) {
                        items.add(object);
                    }
                } catch (Exception e) {
                    if (VKSdk.DEBUG)
                        e.printStackTrace();
                }
            }
        }
    }

    /**
     * Adds the element before the element with the specified id.
     * If an element with the specified id is not found, adds an element to the end of the list.
     * @param id element identifier to add element before it.
     * @param data element to add
     */
    public void addBefore(int id, T data) {
        int size = size();
        for (int i = 0; i < size; i++) {
            if (get(i).getId() > id || i == size - 1) {
                add(i, data);
                break;
            }
        }
    }

    /**
     * Adds the element after the element with the specified id.
     * If an element with the specified id is not found, adds an element to the end of the list.
     * @param id element identifier to add element after it.
     * @param data element to add
     */
    public void addAfter(int id, T data) {
        int size = size();
        for (int i = 0; i < size; i++) {
            if (get(i).getId() > id || i == size - 1) {
                add(i + 1, data);
                break;
            }
        }
    }

    /**
     * Returns element according with id.
     * If nothing found, returns null.
     */
    public T getById(int id) {
        for (T item : this) {
            if (item.getId() == id) {
                return item;
            }
        }
        return null;
    }

    /**
     * Searches through the list of available items. <br />
     * <br />
     * The search will be carried out not by the content of characters per line, and the content of them in separate words. <br />
     * <br />
     * Search is not case sensitive.  <br />
     * <br />
     * To support search class {@code T} must have overridden method {@link #toString()},
     * search will be carried out exactly according to the result of calling this method. <br />
     * <br />
     * <br />
     * Suppose there are elements in the list of contents:
     * <code><pre>
     * - Hello world
     * - Hello test
     * </pre></code>
     * In this case, the matches will be on search phrases {@code 'Hel'}, {@code 'Hello'}, {@code 'test'}, but not on {@code 'llo'}, {@code 'llo world'}
     *
     * @param query search query can not be equal to {@code null}, but can be an empty string.
     * @return created based on the search results new list. If no matches are found, the list will be empty.
     */
    public VKList<T> search(String query) {
        VKList<T> result = new VKList<T>();
        final Pattern pattern = Pattern.compile("(?i).*\\b" + query + ".*");
        for (T item : this) {
            if (pattern.matcher(item.toString()).find()) {
                result.add(item);
            }
        }
        return result;
    }

    /**
     * Returns the return value of the field VK API {@code count}, if it has been returned, and the size of the list, if not.
     */
    public int getCount() {
        return count != NO_COUNT ? count : size();
    }

    @Override
    public void add(int location, T object) {
        items.add(location, object);
    }

    @Override
    public boolean add(T object) {
        return items.add(object);
    }

    @Override
    public boolean addAll(int location, Collection<? extends T> collection) {
        return items.addAll(location, collection);
    }

    @Override
    public boolean addAll(Collection<? extends T> collection) {
        return items.addAll(collection);
    }

    @Override
    public void clear() {
        items.clear();
    }

    @Override
    public boolean contains(Object object) {
        return items.contains(object);
    }

    @Override
    public boolean containsAll(Collection<?> collection) {
        assert collection != null;
        return items.containsAll(collection);
    }

    @Override
    public boolean equals(Object object) {
        return ((Object) this).getClass().equals(object.getClass()) && items.equals(object);
    }

    @Override
    public T get(int location) {
        return items.get(location);
    }

    @Override
    public int indexOf(Object object) {
        return items.indexOf(object);
    }

    @Override
    public boolean isEmpty() {
        return items.isEmpty();
    }

    @Override
    public Iterator<T> iterator() {
        return items.iterator();
    }

    @Override
    public int lastIndexOf(Object object) {
        return items.lastIndexOf(object);
    }

    @Override
    public ListIterator<T> listIterator() {
        return items.listIterator();
    }

    @Override
    public ListIterator<T> listIterator(int location) {
        return items.listIterator(location);
    }

    @Override
    public T remove(int location) {
        return items.remove(location);
    }

    @Override
    public boolean remove(Object object) {
        return items.remove(object);
    }

    @Override
    public boolean removeAll(Collection<?> collection) {
        assert collection != null;
        return items.removeAll(collection);
    }

    @Override
    public boolean retainAll(Collection<?> collection) {
        return items.retainAll(collection);
    }

    @Override
    public T set(int location, T object) {
        return items.set(location, object);
    }

    @Override
    public int size() {
        return items.size();
    }

    @Override
    public java.util.List<T> subList(int start, int end) {
        return items.subList(start, end);
    }

    @Override
    public Object[] toArray() {
        return items.toArray();
    }

    @Override
    public <T1> T1[] toArray(T1[] array) {
        assert array != null;
        return items.toArray(array);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(items.size());
        for (T item : this) {
            dest.writeParcelable(item, flags);
        }
        dest.writeInt(this.count);
    }

    /**
     * Creates list from Parcel
     */
    public VKList(Parcel in) {
        int size = in.readInt();
        for (int i = 0; i < size; i++) {
            items.add(((T) in.readParcelable(((Object) this).getClass().getClassLoader())));
        }
        this.count = in.readInt();
    }

    public static Creator<VKList> CREATOR = new Creator<VKList>() {
        public VKList createFromParcel(Parcel source) {
            return new VKList(source);
        }

        public VKList[] newArray(int size) {
            return new VKList[size];
        }
    };

    /**
     * Used when parsing the list objects as interator created from {@link org.json.JSONArray} a instances of items of the list.
     * @param <D> list item type.
     */
    public static interface Parser<D> {

        /**
         * Creates a list item of its representation return VK API from {@link org.json.JSONArray}
         * @param source representation of the object in the format returned by VK API.
         * @return created element to add to the list.
         * @throws Exception if the exception is thrown, the element iterated this method will not be added to the list.
         */
        D parseObject(JSONObject source) throws Exception;
    }

    /**
     * Parser list items using reflection mechanism.
     * To use an object class must have a public constructor that accepts {@link org.json.JSONObject}.
     * If, during the creation of the object constructor will throw any exception, the element will not be added to the list.
     * @param <D> list item type.
     */
    public final static class ReflectParser<D extends VKApiModel> implements Parser<D> {

        private final Class<? extends D> clazz;

        public ReflectParser(Class<? extends D> clazz) {
            this.clazz = clazz;
        }

        @Override
        public D parseObject(JSONObject source) throws Exception {
            try {
                Constructor<? extends D> jsonConstructor = clazz.getConstructor(JSONObject.class);
                if (jsonConstructor != null) {
                    return jsonConstructor.newInstance(source);
                }
            } catch (Exception ignored) {
                //Ignored. Try default constructor
            }

            return (D) clazz.newInstance().parse(source);
        }
    }

    @Override
    public VKApiModel parse(JSONObject response) throws JSONException {
        throw new JSONException("Operation is not supported while class is generic");
    }
}