Long time no code.
Example use
import ListMap; import javax.annotation.Nonnull; import java.util.ArrayList; import static java.util.Arrays.asList; public class AttributesMap extends ListMap<FooItem, String, String> { public AttributesMap(final FooOperations foos, final String cookie) { super(new FooItems(foos, cookie), new FooItemConverter()); } private static class FooItems extends ArrayList<FooItem> { private final FooOperations foos; private final String cookie; private FooItems(final FooOperations foos, final String cookie) { super(asList(foos.getAttributes(cookie))); this.foos = foos; this.cookie = cookie; } @Override public FooItem set(final int index, final FooItem element) { final FooItem oldElement = super.set(index, element); save(); return oldElement; } @Override public void add(final int index, final FooItem element) { super.add(index, element); save(); } @Override public FooItem remove(final int index) { final FooItem oldElement = super.remove(index); save(); return oldElement; } private void save() { try { foos.setAttributes(cookie, toArray(new FooItem[size()])); } catch (final BadDataRef e) { throw new UserRuntimeException(e); } } } private static final class FooItemConverter implements Converter<FooItem, String, String> { @Nonnull @Override public FooItem toElement(@Nonnull final String key, final String value) { return new FooItem(key, value); } @Nonnull @Override public Entry<String, String> toEntry(@Nonnull final FooItem element) { return new SimpleEntry<>(element.tag, element.value); } } }
ListMap
utility class
import javax.annotation.Nonnull; import javax.annotation.concurrent.NotThreadSafe; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.List; import java.util.ListIterator; /** * {@code ListMap} provides a read-write map view over a list of elements. The elements must * provide a key-value pair structure; keys must {@link #equals(Object)} and {@link #hashCode()}. * <p/> * All map mutating methods also mutate the underlying list. Mutating the underlying list also * mutates the map view. * <p/> * There is no synchronization; instances are not thread safe. * * @param <T> the type of list elements * @param <K> the type of map keys * @param <V> the type of map values * * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a> */ @NotThreadSafe public class ListMap<T, K, V> extends AbstractMap<K, V> { private final List<T> elements; private final Converter<T, K, V> converter; /** * Creates a new {@code ListMap} for the given <var>elements</var> and <var>converter</var>. * * @param elements the list of elements underlying the map, never missing * @param converter the converter between elements and entry objects, never missing */ public ListMap(@Nonnull final List<T> elements, @Nonnull final Converter<T, K, V> converter) { this.elements = elements; this.converter = converter; } /** * {@inheritDoc} * <p/> * Updates the underlying list of elements. */ @Override public V put(final K key, final V value) { final ListIterator<T> lit = elements.listIterator(); while (lit.hasNext()) { final T element = lit.next(); final Entry<K, V> entry = converter.toEntry(element); if (entry.getKey().equals(key)) { elements.set(lit.nextIndex() - 1, converter.toElement(key, value)); return entry.getValue(); } } lit.add(converter.toElement(key, value)); return null; } /** * {@inheritDoc} * * @return a specialized set view of the entries over the element list */ @Override @Nonnull public ListMapSet entrySet() { return new ListMapSet(); } /** * Exposes the underlying list of elements backing the map view. * * @return the element list, never missing */ @Nonnull public List<T> elements() { return elements; } /** * Converts between elements and entry objects. * * @param <T> the type of list elements * @param <K> the type of map keys * @param <V> the type of map values */ public static interface Converter<T, K, V> { /** * Converts to an element instance from a map <var>key</var>-<var>value</var> pair. * * @param key the map key, never missing * @param value the map value, optional * * @return the corresponding list element, never missing */ @Nonnull T toElement(@Nonnull final K key, final V value); /** * Converts to a map entry instance from a list <var>element</var>. * * @param element the list element, never missing * * @return the corresponding map entry, never missing */ @Nonnull Entry<K, V> toEntry(@Nonnull final T element); } /** * Backing set for {@link ListMap} exposed as a separate type so callers may access a {@link * #iterator() list iterator} and a list iterator {@link #iterator(int) offset by an index}. */ public final class ListMapSet extends AbstractSet<Entry<K, V>> { /** * {@inheritDoc} * * @see java.util.List#listIterator() */ @Override @Nonnull public ListMapIterator iterator() { return new ListMapIterator(); } @Override public int size() { return elements.size(); } @Override public boolean add(final Entry<K, V> entry) { return elements.add(converter.toElement(entry.getKey(), entry.getValue())); } /** @see List#listIterator(int) */ @Nonnull public ListMapIterator iterator(final int index) { return new ListMapIterator(index); } } /** * Entry set iterator for {@link ListMapSet} exposed as a separate type. * * @see ListMapSet#iterator() list iterator * @see ListMapSet#iterator(int) offset by an index */ public final class ListMapIterator implements ListIterator<Entry<K, V>> { private final ListIterator<T> it; public ListMapIterator() { it = elements.listIterator(); } public ListMapIterator(final int index) { it = elements.listIterator(index); } @Override public boolean hasNext() { return it.hasNext(); } @Override public Entry<K, V> next() { return new ListMapEntry(it.next()); } @Override public boolean hasPrevious() { return it.hasPrevious(); } @Override public Entry<K, V> previous() { return new ListMapEntry(it.previous()); } @Override public int nextIndex() { return it.nextIndex(); } @Override public int previousIndex() { return it.previousIndex(); } @Override public void remove() { it.remove(); } @Override public void set(final Entry<K, V> entry) { it.set(converter.toElement(entry.getKey(), entry.getValue())); } @Override public void add(final Entry<K, V> entry) { it.add(converter.toElement(entry.getKey(), entry.getValue())); } private class ListMapEntry implements Entry<K, V> { private final T element; ListMapEntry(final T element) { this.element = element; } @Override public K getKey() { return converter.toEntry(element).getKey(); } @Override public V getValue() { return converter.toEntry(element).getValue(); } @Override public V setValue(final V value) { final Entry<K, V> oldEntry = converter.toEntry(element); elements.set(nextIndex() - 1, converter.toElement(oldEntry.getKey(), value)); return oldEntry.getValue(); } } } }
No comments:
Post a Comment