/** * Copyright (c) 2013, Redsolution LTD. All rights reserved. * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.entity; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; /** * Map of map with string value as keys for both maps. * * @param <T> * @author alexander.ivanov */ public class NestedMap<T> implements Iterable<NestedMap.Entry<T>> { private final Map<String, Map<String, T>> map; public NestedMap() { map = new HashMap<>(); } /** * @return <code>null</code> if there is no such first or second level. */ public T get(String first, String second) { Map<String, T> nested = map.get(first); if (nested == null) return null; return nested.get(second); } /** * Puts value. Nested map will be created if necessary. */ synchronized public void put(String first, String second, T value) { Map<String, T> nested = map.get(first); if (nested == null) { nested = new HashMap<>(); map.put(first, nested); } nested.put(second, value); } /** * Removes value. Nested map will be removed if necessary. */ synchronized public T remove(String first, String second) { Map<String, T> nested = map.get(first); if (nested == null) return null; T value = nested.remove(second); if (nested.isEmpty()) map.remove(first); return value; } /** * Removes all information associated with first level. */ synchronized public void clear(String first) { map.remove(first); } /** * Removes all information. */ synchronized public void clear() { map.clear(); } /** * @return Whether there is no values. */ synchronized public boolean isEmpty() { return map.isEmpty(); } /** * Returns an {@link Iterator} for the elements in this object. * <p/> * Iterators are designed to be used by only one thread at a time. */ @Override public Iterator<Entry<T>> iterator() { return new EntryIterator(); } /** * Returns nested map. * * @return empty map if there is no such first level. */ public Map<String, T> getNested(String first) { Map<String, T> nested = map.get(first); if (nested == null) return Collections.emptyMap(); return Collections.unmodifiableMap(nested); } /** * Collection with values. * <p/> * ONLY {@link Collection#iterator()} FUNCTION IS SUPPORTED. */ public Collection<T> values() { return new Values(); } /** * Adds all elements from another {@link NestedMap}. */ public void addAll(NestedMap<T> nestedMap) { for (NestedMap.Entry<T> entry : nestedMap) put(entry.getFirst(), entry.getSecond(), entry.getValue()); } /** * Entry stored in {@link NestedMap}. * * @param <T> * @author alexander.ivanov */ public static class Entry<T> { private final String first; private final String second; private final T value; Entry(String first, String second, T value) { super(); this.first = first; this.second = second; this.value = value; } public String getFirst() { return first; } public String getSecond() { return second; } public T getValue() { return value; } } private class EntryIterator implements Iterator<Entry<T>> { private final Iterator<java.util.Map.Entry<String, Map<String, T>>> firstIterator; private java.util.Map.Entry<String, Map<String, T>> nested; private Iterator<java.util.Map.Entry<String, T>> secondIterator; private EntryIterator() { firstIterator = map.entrySet().iterator(); nested = null; secondIterator = null; } @Override public boolean hasNext() { if (secondIterator != null && secondIterator.hasNext()) return true; while (firstIterator.hasNext()) { nested = firstIterator.next(); secondIterator = nested.getValue().entrySet().iterator(); if (secondIterator.hasNext()) { return true; } } return false; } @Override public Entry<T> next() throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException(); java.util.Map.Entry<String, T> entry = secondIterator.next(); return new Entry<T>(nested.getKey(), entry.getKey(), entry.getValue()); } @Override public void remove() throws IllegalStateException { if (secondIterator == null) throw new IllegalStateException(); secondIterator.remove(); if (nested.getValue().isEmpty()) firstIterator.remove(); } } private class Values implements Collection<T> { @Override public boolean add(T object) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends T> arg0) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public boolean contains(Object object) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection<?> arg0) { throw new UnsupportedOperationException(); } @Override public boolean isEmpty() { return iterator().hasNext(); } @Override public Iterator<T> iterator() { return new ValuesIterator(); } @Override public boolean remove(Object object) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection<?> arg0) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection<?> arg0) { throw new UnsupportedOperationException(); } @Override public int size() { throw new UnsupportedOperationException(); } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public <Type> Type[] toArray(Type[] array) { throw new UnsupportedOperationException(); } private class ValuesIterator implements Iterator<T> { private final Iterator<Entry<T>> iterator; private ValuesIterator() { this.iterator = NestedMap.this.iterator(); } @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { return iterator.next().getValue(); } @Override public void remove() { iterator.remove(); } } } }