// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.key; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.SortedSet; import java.util.Spliterator; public final class ResourceTreeFolder implements Comparable<ResourceTreeFolder> { private final SortedListSet<ResourceEntry> resourceEntries = new SortedListSet<>(); private final List<ResourceTreeFolder> folders = new ArrayList<ResourceTreeFolder>(); private final ResourceTreeFolder parentFolder; private final String folderName; public ResourceTreeFolder(ResourceTreeFolder parentFolder, String folderName) { this.parentFolder = parentFolder; this.folderName = folderName; } // --------------------- Begin Interface Comparable --------------------- @Override public int compareTo(ResourceTreeFolder o) { return folderName.compareToIgnoreCase(o.folderName); } // --------------------- End Interface Comparable --------------------- @Override public String toString() { return folderName + " - " + getChildCount(); } public String folderName() { return folderName; } public List<ResourceEntry> getResourceEntries() { return Collections.unmodifiableList(new ArrayList<>(resourceEntries)); } public List<ResourceEntry> getResourceEntries(String type) { List<ResourceEntry> list = new ArrayList<ResourceEntry>(); resourceEntries.forEach((entry) -> { if (entry.getExtension().equalsIgnoreCase(type)) { list.add(entry); } }); folders.forEach((folder) -> list.addAll(folder.getResourceEntries(type))); return list; } public void addFolder(ResourceTreeFolder folder) { folders.add(folder); } public void addResourceEntry(ResourceEntry entry, boolean overwrite) { if (entry.isVisible()) { if (overwrite) { resourceEntries.remove(entry); } resourceEntries.add(entry); } } public Object getChild(int index) { if (index >= 0) { if (index < folders.size()) { return folders.get(index); } index -= folders.size(); if (index < resourceEntries.size()) { return resourceEntries.get(index); } } return null; } public int getChildCount() { return folders.size() + resourceEntries.size(); } public List<ResourceTreeFolder> getFolders() { return Collections.unmodifiableList(folders); } public int getIndexOfChild(Object node) { if (node instanceof ResourceTreeFolder) { return folders.indexOf(node); } int index = resourceEntries.indexOf(node); if (index >= 0) { return folders.size() + index; } return -1; } public ResourceTreeFolder getParentFolder() { return parentFolder; } public void removeFolder(ResourceTreeFolder folder) { folders.remove(folder); } public void removeResourceEntry(ResourceEntry entry) { resourceEntries.remove(entry); } public void sortChildren(boolean recursive) { Collections.sort(folders); if (recursive) { folders.forEach((folder) -> folder.sortChildren(recursive)); } } //-------------------------- INNER CLASSES -------------------------- // A thread-safe sorted set using an ArrayList as backend for indexed element access private static class SortedListSet<T extends Comparable<? super T>> extends ArrayList<T> implements SortedSet<T> { public SortedListSet() { super(); } @Override public synchronized boolean add(T item) { int index = Collections.binarySearch(this, item); if (index >= 0) { return false; } super.add(~index, item); return true; } @Override public void add(int index, T element) { throw new UnsupportedOperationException(); } @Override public synchronized boolean addAll(Collection<? extends T> c) { boolean bRet = false; for (final T o: c) { bRet |= add(o); } return bRet; } @Override public boolean contains(Object o) { return (indexOf(o) != -1); } @Override public boolean containsAll(Collection<?> c) { boolean bRet = true; for (final Object o: c) { bRet &= contains(o); if (!bRet) { break; } } return bRet; } @Override public int indexOf(Object o) { @SuppressWarnings("unchecked") int index = Collections.binarySearch(this, (T)o); return (index >= 0) ? index : -1; } @Override public synchronized boolean remove(Object o) { int idx = indexOf(o); if (idx >= 0) { remove(idx); return true; } return false; } @Override public synchronized boolean removeAll(Collection<?> c) { boolean bRet = false; for (final Object o: c) { bRet |= remove(o); } return bRet; } @Override public synchronized boolean retainAll(Collection<?> c) { boolean bRet = false; int idx = size() - 1; while (idx >= 0) { Object o = get(idx); if (!c.contains(o)) { remove(o); bRet = true; } idx--; } return bRet; } /** * Replaces the element at the specified position in this set with the specified one. * The new element will be moved to the correct location to preserve the sorted state * of the list. * @param index index of the element to replace. * @param element element to be stored in the list. * @return element previously at the specified position. */ @Override public T set(int index, T element) { T o = remove(index); add(element); return o; } @Override public Spliterator<T> spliterator() { throw new UnsupportedOperationException(); } @Override public Comparator<? super T> comparator() { return new Comparator<T>() { @Override public int compare(T o1, T o2) { return o1.compareTo(o2); } }; } @Override public T first() { if (size() == 0) { throw new NoSuchElementException(); } return get(0); } @Override public synchronized SortedSet<T> headSet(T toElement) { int toIdx = Collections.binarySearch(this, (T)toElement); if (toIdx < 0) { toIdx = ~toIdx; } else { toIdx--; } return getSortedSet(0, toIdx); } @Override public T last() { if (size() == 0) { throw new NoSuchElementException(); } return get(size() - 1); } @Override public synchronized SortedSet<T> subSet(T fromElement, T toElement) { int fromIdx = Collections.binarySearch(this, (T)fromElement); if (fromIdx < 0) { fromIdx = ~fromIdx; } int toIdx = Collections.binarySearch(this, (T)toElement); if (toIdx < 0) { toIdx = ~toIdx; } else { toIdx--; } return getSortedSet(fromIdx, toIdx); } @Override public synchronized SortedSet<T> tailSet(T fromElement) { int fromIdx = Collections.binarySearch(this, (T)fromElement); if (fromIdx < 0) { fromIdx = ~fromIdx; } return getSortedSet(fromIdx, size() - 1); } private SortedSet<T> getSortedSet(int fromIdx, int toIdx) { SortedListSet<T> retVal = new SortedListSet<T>(); for (int idx = fromIdx; idx <= toIdx; idx++) { retVal.add(get(idx)); } return retVal; } } }