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}