package fi.vtt.simantics.procore.internal; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Consumer; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.exception.ResourceNotFoundException; import org.simantics.db.impl.ResourceImpl; import org.simantics.db.impl.graph.ReadGraphImpl; import org.simantics.db.impl.query.IntSet; import org.simantics.db.service.CollectionSupport; import gnu.trove.impl.Constants; import gnu.trove.iterator.TIntIterator; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.procedure.TIntObjectProcedure; import gnu.trove.procedure.TIntProcedure; import gnu.trove.set.hash.TIntHashSet; public class CollectionSupportImpl implements CollectionSupport { final private SessionImplSocket session; CollectionSupportImpl(SessionImplSocket session) { this.session = session; } static final class IntResourceMap { final private SessionImplSocket session; final private TIntIntHashMap backend = new TIntIntHashMap(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1, 0); IntResourceMap(SessionImplSocket session) { this.session = session; } public int size() { return backend.size(); } public boolean isEmpty() { return backend.isEmpty(); } public boolean containsKey(int key) { return backend.containsKey(key); } public boolean containsValue(int value) { return backend.containsValue(value); } public Resource get(int key) { try { return session.getResourceByKey(backend.get(key)); } catch (ResourceNotFoundException e) { e.printStackTrace(); } return null; } public Resource put(int key, Resource value) { ResourceImpl impl = (ResourceImpl) value; int i = backend.put(key, impl.id); if (i == 0) return null; else try { return session.getResourceByKey(i); } catch (ResourceNotFoundException e) { e.printStackTrace(); } return null; } public Resource remove(int key) { throw new UnsupportedOperationException("remove not supported"); } @Override public int hashCode() { return backend.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; IntResourceMap other = (IntResourceMap) obj; return session == other.session && backend.equals(other.backend); } } public IntResourceMap createIntResourceMap() { return new IntResourceMap(session); } @SuppressWarnings("unchecked") @Override public T createObjectResourceMap(Class clazz) { return (T)new ObjectResourceMap(session); } @SuppressWarnings("unchecked") @Override public T createObjectResourceMap(Class clazz, int capacity) { return (T)new ObjectResourceMap(session, capacity); } static final class ResourceMap implements org.simantics.db.ResourceMap { final private SessionImplSocket session; final private TIntObjectHashMap backend = new TIntObjectHashMap(); ResourceMap(SessionImplSocket session) { this.session = session; } @Override public void clear() { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean containsKey(Object resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.containsKey(impl.id); } @SuppressWarnings("unchecked") @Override public boolean containsValue(Object value) { return backend.containsValue((T)value); } @Override public Set> entrySet() { final HashSet> result = new HashSet>(); backend.forEachEntry(new TIntObjectProcedure() { @Override public boolean execute(final int a, final T b) { result.add(new Map.Entry() { @Override public Resource getKey() { return new ResourceImpl(session.resourceSupport, a); } @Override public T getValue() { return b; } @Override public T setValue(T arg0) { throw new UnsupportedOperationException("Not supported"); } }); return true; } }); return result; } @Override public T get(Object resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.get(impl.id); } @Override public boolean isEmpty() { return backend.isEmpty(); } public class CallbackEntry implements ResourceMapEntry { int id; E value; @Override public Resource getKey() { return new ResourceImpl(session.resourceSupport, id); } @Override public E getValue() { return value; } } @Override public void iterateEntries(final Consumer> callback) { final CallbackEntry entry = new CallbackEntry(); backend.forEach(new TIntProcedure() { @Override public boolean execute(int value) { entry.id = value; entry.value = backend.get(value); callback.accept(entry); return true; } }); } @Override public Set keySet() { final ResourceSet result = new ResourceSet(session); backend.forEach(new TIntProcedure() { @Override public boolean execute(int value) { result.add(value); return true; } }); return result; } @Override public T put(Resource resource, T value) { ResourceImpl impl = (ResourceImpl)resource; return backend.put(impl.id, value); } @Override public void putAll(Map map) { @SuppressWarnings("unchecked") ResourceMap other = (ResourceMap)map; other.backend.forEachEntry(new TIntObjectProcedure() { @Override public boolean execute(int a, T b) { backend.put(a, b); return true; } }); } @Override public T remove(Object key) { if (key instanceof ResourceImpl) { ResourceImpl impl = (ResourceImpl)key; return backend.remove(impl.id); } return null; } @Override public int size() { return backend.size(); } @SuppressWarnings("unchecked") @Override public Collection values() { ArrayList result = new ArrayList<>(); for(Object o : backend.values()) result.add((T)o); return result; } @Override public int hashCode() { return backend.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) { if (obj instanceof Map) { // Nonoptimal fallback for comparing against generic Map Map m = (Map) obj; if (m.size() != size()) return false; try { Iterator> i = entrySet().iterator(); while (i.hasNext()) { Entry e = i.next(); Resource key = e.getKey(); T value = e.getValue(); if (value == null) { if (!(m.get(key)==null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } return true; } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } } return false; } ResourceMap other = (ResourceMap) obj; return session == other.session && backend.equals(other.backend); } } @SuppressWarnings("unchecked") @Override public T createMap(Class clazz) { return (T)new ResourceMap(session); } static final class ResourceSet implements Set { final private SessionImplSocket session; final private TIntHashSet backend; ResourceSet(SessionImplSocket session) { this.session = session; backend = new TIntHashSet(); } ResourceSet(SessionImplSocket session, int capacity) { this.session = session; backend = new TIntHashSet(capacity); } @Override public void clear() { backend.clear(); } @Override public int size() { return backend.size(); } @Override public boolean add(Resource resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.add(impl.id); } boolean add(int id) { return backend.add(id); } @Override public boolean addAll(Collection rs) { boolean result = false; for(Resource r : rs) result |= add(r); return result; } @Override public boolean contains(Object resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.contains(impl.id); } @Override public boolean containsAll(Collection rs) { for (Object r : rs) if (!contains(r)) return false; return true; } @Override public boolean isEmpty() { return backend.isEmpty(); } @Override public Iterator iterator() { return new Iterator() { TIntIterator it = backend.iterator(); @Override public boolean hasNext() { return it.hasNext(); } @Override public Resource next() { return new ResourceImpl(session.resourceSupport, it.next()); } @Override public void remove() { it.remove(); } }; } @Override public boolean remove(Object resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.remove(impl.id); } @Override public boolean removeAll(Collection rs) { boolean result = false; for(Object r : rs) result |= remove(r); return result; } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException("Not implemented"); } @Override public Object[] toArray() { return toArray(new Object[backend.size()]); } @SuppressWarnings("unchecked") @Override public T[] toArray(T[] a) { int size = backend.size(); T[] r = a.length >= size ? a : (T[])Array.newInstance(a.getClass().getComponentType(), size); backend.forEach(new TIntProcedure() { int index = 0; @Override public boolean execute(int value) { r[index++] = (T)new ResourceImpl(session.resourceSupport, value); return true; } }); return r; } @Override public int hashCode() { return backend.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) { if (obj instanceof Set) { // Nonoptimal fallback for comparing against generic Set Collection c = (Collection) obj; if (c.size() != size()) return false; try { return containsAll(c); } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } } return false; } ResourceSet other = (ResourceSet) obj; return session == other.session && backend.equals(other.backend); } } @Override public Set createSet() { return new ResourceSet(session); } @Override public Set createSet(int capacity) { return new ResourceSet(session, capacity); } static final class ResourceList implements List { final private SessionImplSocket session; final private TIntArrayList backend; ResourceList(SessionImplSocket session) { this.session = session; this.backend = new TIntArrayList(); } ResourceList(SessionImplSocket session, int capacity) { this.session = session; this.backend = new TIntArrayList(capacity); } ResourceList(SessionImplSocket session, Collection rs) { this.session = session; this.backend = new TIntArrayList(rs.size()); addAll(rs); } @Override public void clear() { throw new UnsupportedOperationException("Not implemented"); } @Override public int size() { return backend.size(); } @Override public boolean add(Resource resource) { if(resource == null) { backend.add(0); } else { ResourceImpl impl = (ResourceImpl)resource; backend.add(impl.id); } return true; } @Override public boolean addAll(Collection rs) { if(rs instanceof ResourceList) { ResourceList rl = (ResourceList)rs; backend.addAll(rl.backend); return !rl.isEmpty(); } boolean result = true; for(Resource r : rs) result &= add(r); return result; } @Override public boolean contains(Object resource) { ResourceImpl impl = (ResourceImpl)resource; return backend.contains(impl.id); } @Override public boolean containsAll(Collection rs) { boolean result = true; for(Object r : rs) result &= contains(r); return result; } @Override public boolean isEmpty() { return backend.isEmpty(); } @Override public Iterator iterator() { return new Iterator() { int index = 0; int max = backend.size(); @Override public boolean hasNext() { return index < max; } @Override public Resource next() { int i = index; if (i >= max) throw new NoSuchElementException(); int id = backend.getQuick(i); index = i + 1; return new ResourceImpl(session.resourceSupport, id); } @Override public void remove() { throw new UnsupportedOperationException("remove not supported"); } }; } @Override public boolean remove(Object resource) { if(!(resource instanceof ResourceImpl)) return false; ResourceImpl impl = (ResourceImpl)resource; return backend.remove(impl.id); } @Override public boolean removeAll(Collection rs) { boolean modified = false; for(Object o : rs) modified |= remove(o); return modified; } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException("Not implemented"); } @Override public Object[] toArray() { return toArray(new Object[backend.size()]); } @SuppressWarnings("unchecked") @Override public T[] toArray(T[] a) { int size = backend.size(); T[] r = a.length >= size ? a : (T[])Array.newInstance(a.getClass().getComponentType(), size); backend.forEach(new TIntProcedure() { int index = 0; @Override public boolean execute(int value) { r[index++] = (T)new ResourceImpl(session.resourceSupport, value); return true; } }); return r; } void sort() { backend.sort(); } @Override public boolean addAll(int index, Collection rs) { if(rs.isEmpty()) return false; int i = index; for(Resource r : rs) { add(i++, r); } return true; } @Override public Resource get(int index) { int id = backend.get(index); if(id == 0) return null; return new ResourceImpl(session.resourceSupport, id); } @Override public Resource set(int index, Resource resource) { ResourceImpl impl = (ResourceImpl)resource; int old = backend.set(index, impl.id); if(old == 0) return null; return new ResourceImpl(session.resourceSupport, old); } @Override public void add(int index, Resource resource) { ResourceImpl impl = (ResourceImpl)resource; backend.insert(index, impl.id); } @Override public Resource remove(int index) { int id = backend.removeAt(index); return new ResourceImpl(session.resourceSupport, id); } @Override public int indexOf(Object o) { if(!(o instanceof ResourceImpl)) return -1; ResourceImpl impl = (ResourceImpl)o; return backend.indexOf(impl.id); } @Override public int lastIndexOf(Object o) { if(!(o instanceof ResourceImpl)) return -1; ResourceImpl impl = (ResourceImpl)o; return backend.lastIndexOf(impl.id); } private class ListItr implements ListIterator { int index; int max; public ListItr(int index) { this.index = index; this.max = size(); } @Override public boolean hasNext() { return index < max; } @Override public Resource next() { int i = index; if (i >= max) throw new NoSuchElementException(); int id = backend.getQuick(index); index = i + 1; return new ResourceImpl(session.resourceSupport, id); } @Override public boolean hasPrevious() { return index != 0; } @Override public Resource previous() { int i = index - 1; if (i < 0) throw new NoSuchElementException(); int id = backend.getQuick(index); index = i; return new ResourceImpl(session.resourceSupport, id); } @Override public int nextIndex() { return index; } @Override public int previousIndex() { return index - 1; } @Override public void remove() { throw new UnsupportedOperationException("remove not supported"); } @Override public void set(Resource e) { throw new UnsupportedOperationException("set not supported"); } @Override public void add(Resource e) { throw new UnsupportedOperationException("add not supported"); } } @Override public ListIterator listIterator() { return new ListItr(0); } @Override public ListIterator listIterator(int index) { if (index < 0 || index > backend.size()) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); } @Override public List subList(int fromIndex, int toIndex) { ResourceList result = new ResourceList(session); for(int i=fromIndex;i e1 = listIterator(); ListIterator e2 = ((List) obj).listIterator(); while (e1.hasNext() && e2.hasNext()) { Object o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); } return false; } ResourceList other = (ResourceList) obj; return session == other.session && backend.equals(other.backend); } } @Override public List createList() { return new ResourceList(session); } @Override public List createList(int capacity) { return new ResourceList(session, capacity); } static final class StatementList implements Collection { final private SessionImplSocket session; final private TIntArrayList backend = new TIntArrayList(); StatementList(SessionImplSocket session) { this.session = session; } @Override public void clear() { throw new UnsupportedOperationException("Not implemented"); } @Override public int size() { return backend.size() / 3; } @Override public boolean add(Statement stm) { ResourceImpl s = (ResourceImpl)stm.getSubject(); ResourceImpl p = (ResourceImpl)stm.getPredicate(); ResourceImpl o = (ResourceImpl)stm.getObject(); backend.add(s.id); backend.add(p.id); backend.add(o.id); return true; } @Override public boolean addAll(Collection rs) { boolean result = false; for(Statement r : rs) result |= add(r); return result; } @Override public boolean contains(Object statement) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean containsAll(Collection rs) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean isEmpty() { return backend.isEmpty(); } @Override public Iterator iterator() { return new Iterator() { int index = 0; int max = backend.size(); @Override public boolean hasNext() { return index < max; } @Override public Statement next() { return new DirectStatementImpl(session.resourceSupport, backend.getQuick(index++), backend.getQuick(index++), backend.getQuick(index++)); } @Override public void remove() { throw new UnsupportedOperationException("Not supported"); } }; } @Override public boolean remove(Object resource) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean removeAll(Collection rs) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException("Not implemented"); } @Override public Object[] toArray() { int stms = backend.size() / 3; return toArray(new Object[stms]); } @SuppressWarnings("unchecked") @Override public T[] toArray(T[] a) { int size = backend.size(); int stms = size / 3; T[] r = a.length >= size ? a : (T[])Array.newInstance(a.getClass().getComponentType(), stms); int index = 0; for (int i = 0; i < size; i += 3) { r[index++] = (T) new DirectStatementImpl(session.resourceSupport, backend.getQuick(i), backend.getQuick(i+1), backend.getQuick(i+2)); } return r; } @Override public int hashCode() { return backend.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) { if (obj instanceof Collection) { // Nonoptimal fallback for comparing against generic Collection Iterator e1 = iterator(); Iterator e2 = ((Collection) obj).iterator(); while (e1.hasNext() && e2.hasNext()) { Object o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); } return false; } StatementList other = (StatementList) obj; return session == other.session && backend.equals(other.backend); } } @Override public Collection createStatementList() { return new StatementList(session); } private static Comparator RESOURCE_COMPARATOR = (o1, o2) -> { ResourceImpl r1 = (ResourceImpl)o1; ResourceImpl r2 = (ResourceImpl)o2; return Integer.compare(r1.id, r2.id); }; @Override public void sort(List list) { if(list instanceof ResourceList) { ((ResourceList) list).sort(); } else { Collections.sort(list, RESOURCE_COMPARATOR); } } @Override public List asSortedList(Collection rs) { ResourceList result = new ResourceList(session, rs); result.sort(); return result; } @Override public org.simantics.db.ResourceSet getResourceSet(ReadGraph graph, Collection resources) { if(resources instanceof ResourceSet) return (org.simantics.db.ResourceSet)resources; org.simantics.db.ResourceSet result = new IntSet(((ReadGraphImpl)graph).processor.querySupport); for(Resource r : resources) result.add(r); return result; } @Override public org.simantics.db.ResourceSet getResourceSet(ReadGraph graph, Resource ... rs) { org.simantics.db.ResourceSet result = new IntSet(((ReadGraphImpl)graph).processor.querySupport); for(Resource r : rs) result.add(r); return result; } }