/**
* 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();
}
}
}
}