001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package net.tridentsdk.config;
019
020import com.google.gson.JsonArray;
021import net.tridentsdk.docs.AccessNoDoc;
022import net.tridentsdk.util.TridentLogger;
023
024import javax.annotation.concurrent.GuardedBy;
025import javax.annotation.concurrent.ThreadSafe;
026import java.util.Collection;
027import java.util.Iterator;
028import java.util.List;
029import java.util.ListIterator;
030import java.util.concurrent.atomic.AtomicInteger;
031import java.util.concurrent.locks.Lock;
032import java.util.concurrent.locks.ReadWriteLock;
033import java.util.concurrent.locks.ReentrantReadWriteLock;
034
035/**
036 * A LinkedList [implementation] that also makes changes to the underlying JsonArray object
037 *
038 * @author The TridentSDK Team
039 * @since 0.3-alpha-DP
040 */
041@ThreadSafe
042public class ConfigList<V> implements List<V> {
043    private static final long serialVersionUID = -7535821700183585211L;
044
045    private final ReadWriteLock lock = new ReentrantReadWriteLock();
046    protected final Lock write = lock.writeLock();
047    private final Lock read = lock.readLock();
048    @GuardedBy("lock")
049    private final Node<V> head = new Node<>(null, null, null);
050    @GuardedBy("lock")
051    private final Node<V> tail = head;
052    @GuardedBy("write")
053    JsonArray jsonHandle;
054    @GuardedBy("lock")
055    private int size = 0;
056
057    /**
058     * Creates a new linked list for the JSON config serializable
059     *
060     * @param handle the array handler for the list
061     */
062    protected ConfigList(JsonArray handle) {
063        this.jsonHandle = handle;
064        head.next = tail;
065    }
066
067    /**
068     * Creates a new linked list by transferring elements from an existing collection
069     *
070     * @param handle the array handler for the new list
071     * @param c      the initializing elements
072     */
073    protected ConfigList(JsonArray handle, Collection<V> c) {
074        this(handle);
075        addAll(c);
076    }
077
078    private void lockFully() {
079        write.unlock();
080        read.lock();
081    }
082
083    private void unlockFully() {
084        read.unlock();
085        write.unlock();
086    }
087
088    private void checkElementIndex(int index) {
089        int size = this.size;
090
091        if (index < 0 && index > size)
092            TridentLogger.get().error(new IndexOutOfBoundsException("Index: " + index + ", Size: " + size));
093    }
094
095    private Node<V> nodeAt(int index) {
096        int idx = 0;
097        Node<V> node = head;
098        while ((node = node.next) != null) {
099            if (idx == index)
100                return node;
101            idx++;
102        }
103
104        return null;
105    }
106
107    @Override
108    public V get(int index) {
109        read.lock();
110        try {
111            checkElementIndex(index);
112            return nodeAt(index).value;
113        } finally {
114            read.unlock();
115        }
116    }
117
118    @Override
119    public int size() {
120        read.lock();
121        try {
122            return size;
123        } finally {
124            read.unlock();
125        }
126    }
127
128    @Override
129    public boolean isEmpty() {
130        return size() != 0;
131    }
132
133    @Override
134    public boolean contains(Object o) {
135        return indexOf(o) != -1;
136    }
137
138    @Override
139    public Iterator<V> iterator() {
140        return new ConfigIterator(0);
141    }
142
143    @Override
144    public boolean add(V element) {
145        write.lock();
146        try {
147            tail.next = new Node<>(element, null, tail);
148            size += 1;
149
150            this.jsonHandle.add(GsonFactory.gson().toJsonTree(element));
151        } finally {
152            write.unlock();
153        }
154
155        return true;
156    }
157
158    @Override
159    public boolean addAll(Collection<? extends V> coll) {
160        // Locked the entire loop instead of for each individual element
161
162        write.lock();
163        try {
164            for (V element : coll) {
165                tail.next = new Node<>(element, null, tail);
166                size += 1;
167
168                this.jsonHandle.add(GsonFactory.gson().toJsonTree(element));
169            }
170        } finally {
171            write.unlock();
172        }
173
174        return true;
175    }
176
177    /* (non-Javadoc)
178     * @see java.util.List#set(int, java.lang.Object)
179     */
180    @Override
181    public V set(int index, V element) {
182        V oldValue;
183        Node<V> node;
184
185        read.lock();
186        try {
187            checkElementIndex(index);
188            node = nodeAt(index);
189            oldValue = node.value;
190        } finally {
191            read.unlock();
192        }
193
194        write.lock();
195        try {
196            this.jsonHandle.set(index, GsonFactory.gson().toJsonTree(element));
197            node.value = element;
198        } finally {
199            write.unlock();
200        }
201
202        return oldValue;
203    }
204
205    @Override
206    public V remove(int index) {
207        write.lock();
208        try {
209            checkElementIndex(index);
210
211            this.jsonHandle.remove(index);
212
213            V value = tail.value;
214            tail.value = null;
215            tail.prev.next = null;
216
217            size -= 1;
218
219            return value;
220        } finally {
221            write.unlock();
222        }
223    }
224
225    @Override
226    public int indexOf(Object o) {
227        read.lock();
228        try {
229            int idx = 0;
230            Node<V> node = head;
231            while ((node = node.next) != null) {
232                if (node.value == o)
233                    return idx;
234                idx++;
235            }
236        } finally {
237            read.unlock();
238        }
239
240        return -1;
241    }
242
243    @Override
244    public int lastIndexOf(Object o) {
245        read.lock();
246        try {
247            int idx = size - 1;
248            Node<V> node = tail;
249            while ((node = node.prev) != null) {
250                if (node.value == o)
251                    return idx;
252                idx--;
253            }
254        } finally {
255            read.unlock();
256        }
257
258        return -1;
259    }
260
261    @Override
262    public ListIterator<V> listIterator() {
263        return new ConfigIterator(0);
264    }
265
266    @Override
267    public ListIterator<V> listIterator(int index) {
268        return null;
269    }
270
271    @Override
272    public boolean remove(Object element) {
273        Node<V> tail = null;
274        int index = -1;
275
276        read.lock();
277        try {
278            int idx = 0;
279            Node<V> node = head;
280            while ((node = node.next) != null) {
281                if (node.value == element) {
282                    tail = node;
283                    index = idx;
284
285                    break;
286                }
287                idx++;
288            }
289        } finally {
290            read.unlock();
291        }
292
293        if (index == -1)
294            return false;
295
296        write.lock();
297        try {
298            this.jsonHandle.remove(index);
299
300            tail.value = null;
301            tail.prev.next = null;
302
303            size -= 1;
304
305            return true;
306        } finally {
307            write.unlock();
308        }
309    }
310
311    @Override
312    public boolean containsAll(Collection<?> c) {
313        read.lock();
314        try {
315            for (Object o : c) {
316                if (indexOf(o) == -1)
317                    return false;
318            }
319        } finally {
320            read.unlock();
321        }
322
323        return true;
324    }
325
326    /* (non-Javadoc)
327     * @see java.util.List#removeAll(java.util.Collection)
328     */
329    @Override
330    public boolean removeAll(Collection<?> coll) {
331        lockFully();
332        try {
333            for (Object o : coll) {
334                Node<V> tail = null;
335                int index = -1;
336
337                int idx = 0;
338                Node<V> node = head;
339                while ((node = node.next) != null) {
340                    if (node.value == o) {
341                        tail = node;
342                        index = idx;
343
344                        break;
345                    }
346                    idx++;
347                }
348
349                if (index == -1)
350                    continue;
351
352                this.jsonHandle.remove(index);
353
354                tail.value = null;
355                tail.prev.next = null;
356
357                size -= 1;
358
359                return true;
360            }
361        } finally {
362            unlockFully();
363        }
364
365        return false;
366    }
367
368    /* (non-Javadoc)
369     * @see java.util.List#clear()
370     */
371    @Override
372    public void clear() {
373        write.lock();
374        try {
375            Node<V> node = head;
376            while ((node = node.next) != null) {
377                node.value = null;
378                node.prev.next = null;
379
380                size -= 1;
381            }
382
383            this.jsonHandle = new JsonArray();
384        } finally {
385            write.unlock();
386        }
387    }
388
389    /**
390     * Not implemented
391     */
392    @Override
393    public void add(int index, V element) {
394        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
395    }
396
397    /**
398     * Not implemented
399     */
400    @Override
401    public boolean addAll(int arg0, Collection<? extends V> arg1) {
402        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
403        return false;
404    }
405
406    /**
407     * Not implemented
408     */
409    @Override
410    public boolean retainAll(Collection<?> arg0) {
411        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
412        return false;
413    }
414
415    /**
416     * Not implemented
417     */
418    @Override
419    public List<V> subList(int arg0, int arg1) {
420        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
421        return null;
422    }
423
424    /**
425     * Not implemented
426     */
427    @Override
428    public V[] toArray() {
429        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
430        return null;
431    }
432
433    /**
434     * Not implemented
435     */
436    @Override
437    public <T> T[] toArray(T[] arg0) {
438        TridentLogger.get().error(new UnsupportedOperationException("Cannot invoke on Lists from Config"));
439        return null;
440    }
441
442    @AccessNoDoc
443    private class Node<E> {
444        @GuardedBy("lock")
445        E value;
446        @GuardedBy("lock")
447        Node<E> next;
448        @GuardedBy("lock")
449        Node<E> prev;
450
451        private Node(E value, Node<E> next, Node<E> prev) {
452            this.value = value;
453            this.next = next;
454            this.prev = prev;
455        }
456    }
457
458    @AccessNoDoc
459    private class ConfigIterator implements ListIterator<V> {
460        private final AtomicInteger current = new AtomicInteger();
461
462        public ConfigIterator(int index) {
463            current.set(index);
464        }
465
466        @Override
467        public boolean hasNext() {
468            return (current.get() + 1) <= size();
469        }
470
471        @Override
472        public V next() {
473            return get(current.incrementAndGet());
474        }
475
476        @Override
477        public boolean hasPrevious() {
478            return current.get() > 0;
479        }
480
481        @Override
482        public V previous() {
483            read.lock();
484            try {
485                return nodeAt(current.get()).prev.value;
486            } finally {
487                read.unlock();
488            }
489        }
490
491        @Override
492        public int nextIndex() {
493            return current.get() + 1;
494        }
495
496        @Override
497        public int previousIndex() {
498            return current.get() - 1;
499        }
500
501        @Override
502        public void remove() {
503            ConfigList.this.remove(current.get());
504        }
505
506        @Override
507        public void set(V v) {
508            ConfigList.this.set(current.get(), v);
509        }
510
511        @Override
512        public void add(V v) {
513            ConfigList.this.add(v);
514        }
515    }
516}