All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.joda.beans.ser.GuavaSerIteratorFactory Maven / Gradle / Ivy

There is a newer version: 2.11.1
Show newest version
/*
 *  Copyright 2001-present Stephen Colebourne
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.joda.beans.ser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaProperty;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.SortedMultiset;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import com.google.common.collect.TreeMultiset;

/**
 * Guava factory used to create wrappers around collection-like objects.
 *
 * @author Stephen Colebourne
 */
public class GuavaSerIteratorFactory extends SerIteratorFactory {

    /**
     * Creates an iterator wrapper for a meta-property value.
     * 
     * @param value  the possible collection-like object, not null
     * @param prop  the meta-property defining the value, not null
     * @param beanClass  the class of the bean, not the meta-property, for better generics, not null
     * @return the iterator, null if not a collection-like type
     */
    @Override
    public SerIterator create(final Object value, final MetaProperty prop, Class beanClass) {
        Class declaredType = prop.propertyType();
        if (value instanceof BiMap) {
            Class keyType = JodaBeanUtils.mapKeyType(prop, beanClass);
            Class valueType = JodaBeanUtils.mapValueType(prop, beanClass);
            List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass);
            return biMap((BiMap) value, declaredType, keyType, valueType, valueTypeTypes);
        }
        if (value instanceof Multiset) {
            Class valueType = JodaBeanUtils.collectionType(prop, beanClass);
            List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass);
            return multiset((Multiset) value, declaredType, valueType, valueTypeTypes);
        }
        if (value instanceof Multimap) {
            Class keyType = JodaBeanUtils.mapKeyType(prop, beanClass);
            Class valueType = JodaBeanUtils.mapValueType(prop, beanClass);
            List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass);
            return multimap((Multimap) value, declaredType, keyType, valueType, valueTypeTypes);
        }
        if (value instanceof Table) {
            Class rowType = JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 0);
            Class colType = JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 1);
            Class valueType = JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 2);
            return table((Table) value, declaredType, rowType, colType, valueType, EMPTY_VALUE_TYPES);
        }
        return super.create(value, prop, beanClass);
    }

    /**
     * Creates an iterator wrapper for a value retrieved from a parent iterator.
     * 

* Allows the parent iterator to define the child iterator using generic type information. * This handles cases such as a {@code List} as the value in a {@code Map}. * * @param value the possible collection-like object, not null * @param parent the parent iterator, not null * @return the iterator, null if not a collection-like type */ @Override public SerIterator createChild(final Object value, final SerIterator parent) { Class declaredType = parent.valueType(); List> childGenericTypes = parent.valueTypeTypes(); if (value instanceof BiMap) { if (childGenericTypes.size() == 2) { return biMap((BiMap) value, declaredType, childGenericTypes.get(0), childGenericTypes.get(1), EMPTY_VALUE_TYPES); } return biMap((BiMap) value, Object.class, Object.class, Object.class, EMPTY_VALUE_TYPES); } if (value instanceof Multimap) { if (childGenericTypes.size() == 2) { return multimap((Multimap) value, declaredType, childGenericTypes.get(0), childGenericTypes.get(1), EMPTY_VALUE_TYPES); } return multimap((Multimap) value, Object.class, Object.class, Object.class, EMPTY_VALUE_TYPES); } if (value instanceof Multiset) { if (childGenericTypes.size() == 1) { return multiset((Multiset) value, declaredType, childGenericTypes.get(0), EMPTY_VALUE_TYPES); } return multiset((Multiset) value, Object.class, Object.class, EMPTY_VALUE_TYPES); } if (value instanceof Table) { if (childGenericTypes.size() == 3) { return table((Table) value, declaredType, childGenericTypes.get(0), childGenericTypes.get(1), childGenericTypes.get(2), EMPTY_VALUE_TYPES); } return table((Table) value, Object.class, Object.class, Object.class, Object.class, EMPTY_VALUE_TYPES); } return super.createChild(value, parent); } //----------------------------------------------------------------------- /** * Creates an iterator wrapper for a meta-property value. * * @param metaTypeDescription the description of the collection type, not null * @param settings the settings object, not null * @param knownTypes the known types map, null if not using known type shortening * @return the iterator, null if not a collection-like type */ @Override public SerIterable createIterable(final String metaTypeDescription, final JodaBeanSer settings, final Map> knownTypes) { if (metaTypeDescription.equals("BiMap")) { return biMap(Object.class, Object.class, EMPTY_VALUE_TYPES); } if (metaTypeDescription.equals("SetMultimap")) { return setMultimap(Object.class, Object.class, EMPTY_VALUE_TYPES); } if (metaTypeDescription.equals("ListMultimap")) { return listMultimap(Object.class, Object.class, EMPTY_VALUE_TYPES); } if (metaTypeDescription.equals("Multimap")) { return listMultimap(Object.class, Object.class, EMPTY_VALUE_TYPES); } if (metaTypeDescription.equals("Multiset")) { return multiset(Object.class, EMPTY_VALUE_TYPES); } if (metaTypeDescription.equals("Table")) { return table(Object.class, Object.class, Object.class, EMPTY_VALUE_TYPES); } return super.createIterable(metaTypeDescription, settings, knownTypes); } /** * Creates an iterator wrapper for a meta-property value. * * @param prop the meta-property defining the value, not null * @param beanClass the class of the bean, not the meta-property, for better generics, not null * @return the iterator, null if not a collection-like type */ @Override public SerIterable createIterable(final MetaProperty prop, Class beanClass) { if (BiMap.class.isAssignableFrom(prop.propertyType())) { Class keyType = defaultToObjectClass(JodaBeanUtils.mapKeyType(prop, beanClass)); Class valueType = defaultToObjectClass(JodaBeanUtils.mapValueType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return biMap(keyType, valueType, valueTypeTypes); } if (SortedMultiset.class.isAssignableFrom(prop.propertyType())) { Class valueType = defaultToObjectClass(JodaBeanUtils.collectionType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass); return sortedMultiset(valueType, valueTypeTypes); } if (Multiset.class.isAssignableFrom(prop.propertyType())) { Class valueType = defaultToObjectClass(JodaBeanUtils.collectionType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass); return multiset(valueType, valueTypeTypes); } if (SetMultimap.class.isAssignableFrom(prop.propertyType())) { Class keyType = defaultToObjectClass(JodaBeanUtils.mapKeyType(prop, beanClass)); Class valueType = defaultToObjectClass(JodaBeanUtils.mapValueType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return setMultimap(keyType, valueType, valueTypeTypes); } if (ListMultimap.class.isAssignableFrom(prop.propertyType())) { Class keyType = defaultToObjectClass(JodaBeanUtils.mapKeyType(prop, beanClass)); Class valueType = defaultToObjectClass(JodaBeanUtils.mapValueType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return listMultimap(keyType, valueType, valueTypeTypes); } if (Multimap.class.isAssignableFrom(prop.propertyType())) { Class keyType = defaultToObjectClass(JodaBeanUtils.mapKeyType(prop, beanClass)); Class valueType = defaultToObjectClass(JodaBeanUtils.mapValueType(prop, beanClass)); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return listMultimap(keyType, valueType, valueTypeTypes); } if (Table.class.isAssignableFrom(prop.propertyType())) { Class rowType = defaultToObjectClass(JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 0)); Class colType = defaultToObjectClass(JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 1)); Class valueType = defaultToObjectClass(JodaBeanUtils.extractTypeClass(prop, beanClass, 3, 2)); return table(rowType, colType, valueType, EMPTY_VALUE_TYPES); } if (ImmutableList.class.isAssignableFrom(prop.propertyType())) { Class valueType = JodaBeanUtils.collectionType(prop, beanClass); List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass); return immutableList(valueType, valueTypeTypes); } if (ImmutableSortedSet.class.isAssignableFrom(prop.propertyType())) { Class valueType = JodaBeanUtils.collectionType(prop, beanClass); List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass); return immutableSortedSet(valueType, valueTypeTypes); } if (ImmutableSet.class.isAssignableFrom(prop.propertyType())) { Class valueType = JodaBeanUtils.collectionType(prop, beanClass); List> valueTypeTypes = JodaBeanUtils.collectionTypeTypes(prop, beanClass); return immutableSet(valueType, valueTypeTypes); } if (ImmutableSortedMap.class.isAssignableFrom(prop.propertyType())) { Class keyType = JodaBeanUtils.mapKeyType(prop, beanClass); Class valueType = JodaBeanUtils.mapValueType(prop, beanClass); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return immutableSortedMap(keyType, valueType, valueTypeTypes); } if (ImmutableMap.class.isAssignableFrom(prop.propertyType())) { Class keyType = JodaBeanUtils.mapKeyType(prop, beanClass); Class valueType = JodaBeanUtils.mapValueType(prop, beanClass); List> valueTypeTypes = JodaBeanUtils.mapValueTypeTypes(prop, beanClass); return immutableMap(keyType, valueType, valueTypeTypes); } return super.createIterable(prop, beanClass); } //----------------------------------------------------------------------- /** * Gets an iterable wrapper for {@code BiMap}. * * @param keyType the value type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable biMap(final Class keyType, final Class valueType, final List> valueTypeTypes) { final BiMap map = HashBiMap.create(); return map(keyType, valueType, valueTypeTypes, map); } /** * Gets an iterable wrapper for {@code Multiset}. * * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable multiset(final Class valueType, final List> valueTypeTypes) { final Multiset coll = HashMultiset.create(); return multiset(valueType, valueTypeTypes, coll); } /** * Gets an iterable wrapper for {@code SortedMultiset}. * * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static final SerIterable sortedMultiset(final Class valueType, final List> valueTypeTypes) { Ordering natural = Ordering.natural(); final SortedMultiset coll = TreeMultiset.create(natural); return multiset(valueType, valueTypeTypes, coll); } private static SerIterable multiset(final Class valueType, final List> valueTypeTypes, final Multiset coll) { return new SerIterable() { @Override public SerIterator iterator() { return multiset(coll, Object.class, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key != null) { throw new IllegalArgumentException("Unexpected key"); } coll.add(value, count); } @Override public Object build() { return coll; } @Override public SerCategory category() { return SerCategory.COUNTED; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterator wrapper for {@code Multiset}. * * @param multiset the collection, not null * @param declaredType the declared type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterator, not null */ @SuppressWarnings("rawtypes") public static final SerIterator multiset( final Multiset multiset, final Class declaredType, final Class valueType, final List> valueTypeTypes) { return new SerIterator() { private final Iterator it = multiset.entrySet().iterator(); private Multiset.Entry current; @Override public String metaTypeName() { return "Multiset"; } @Override public boolean metaTypeRequired() { return Multiset.class.isAssignableFrom(declaredType) == false; } @Override public SerCategory category() { return SerCategory.COUNTED; } @Override public int size() { return multiset.entrySet().size(); } @Override public boolean hasNext() { return it.hasNext(); } @Override public void next() { current = (Multiset.Entry) it.next(); } @Override public int count() { return current.getCount(); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } @Override public Object value() { return current.getElement(); } }; } //----------------------------------------------------------------------- /** * Gets an iterable wrapper for {@code ListMultimap}. * * @param keyType the key type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable listMultimap(final Class keyType, final Class valueType, final List> valueTypeTypes) { final ListMultimap map = ArrayListMultimap.create(); return new SerIterable() { @Override public SerIterator iterator() { return multimap(map, Object.class, keyType, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key == null) { throw new IllegalArgumentException("Missing key"); } if (count != 1) { throw new IllegalArgumentException("Unexpected count"); } map.put(key, value); } @Override public Object build() { return map; } @Override public SerCategory category() { return SerCategory.MAP; } @Override public Class keyType() { return keyType; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterable wrapper for {@code SetMultimap}. * * @param keyType the key type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable setMultimap(final Class keyType, final Class valueType, final List> valueTypeTypes) { final SetMultimap map = HashMultimap.create(); return new SerIterable() { @Override public SerIterator iterator() { return multimap(map, Object.class, keyType, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key == null) { throw new IllegalArgumentException("Missing key"); } if (count != 1) { throw new IllegalArgumentException("Unexpected count"); } map.put(key, value); } @Override public Object build() { return map; } @Override public SerCategory category() { return SerCategory.MAP; } @Override public Class keyType() { return keyType; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterator wrapper for {@code Multimap}. * * @param map the collection, not null * @param declaredType the declared type, not null * @param keyType the key type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterator, not null */ @SuppressWarnings("rawtypes") public static final SerIterator multimap( final Multimap map, final Class declaredType, final Class keyType, final Class valueType, final List> valueTypeTypes) { return new SerIterator() { private final Iterator it = map.entries().iterator(); private Map.Entry current; @Override public String metaTypeName() { if (map instanceof SetMultimap) { return "SetMultimap"; } if (map instanceof ListMultimap) { return "ListMultimap"; } return "Multimap"; } @Override public boolean metaTypeRequired() { if (map instanceof SetMultimap) { return SetMultimap.class.isAssignableFrom(declaredType) == false; } if (map instanceof ListMultimap) { return ListMultimap.class.isAssignableFrom(declaredType) == false; } return Multimap.class.isAssignableFrom(declaredType) == false; } @Override public SerCategory category() { return SerCategory.MAP; } @Override public int size() { return map.size(); } @Override public boolean hasNext() { return it.hasNext(); } @Override public void next() { current = (Map.Entry) it.next(); } @Override public Class keyType() { return keyType; } @Override public Object key() { return current.getKey(); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } @Override public Object value() { return current.getValue(); } }; } //----------------------------------------------------------------------- /** * Gets an iterable wrapper for {@code Table}. * * @param rowType the row type, not null * @param colType the column type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable table(final Class rowType, final Class colType, final Class valueType, final List> valueTypeTypes) { final Table table = HashBasedTable.create(); return new SerIterable() { @Override public SerIterator iterator() { return table(table, Object.class, rowType, colType, valueType, valueTypeTypes); } @Override public void add(Object row, Object column, Object value, int count) { if (row == null) { throw new IllegalArgumentException("Missing row"); } if (column == null) { throw new IllegalArgumentException("Missing column"); } if (count != 1) { throw new IllegalArgumentException("Unexpected count"); } table.put(row, column, value); } @Override public Object build() { return table; } @Override public SerCategory category() { return SerCategory.TABLE; } @Override public Class keyType() { return rowType; } @Override public Class columnType() { return colType; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterator wrapper for {@code Table}. * * @param table the collection, not null * @param declaredType the declared type, not null * @param rowType the row type, not null * @param colType the col type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterator, not null */ @SuppressWarnings("rawtypes") public static final SerIterator table( final Table table, final Class declaredType, final Class rowType, final Class colType, final Class valueType, final List> valueTypeTypes) { return new SerIterator() { private final Iterator it = table.cellSet().iterator(); private Cell current; @Override public String metaTypeName() { return "Table"; } @Override public boolean metaTypeRequired() { return Table.class.isAssignableFrom(declaredType) == false; } @Override public SerCategory category() { return SerCategory.TABLE; } @Override public int size() { return table.size(); } @Override public boolean hasNext() { return it.hasNext(); } @Override public void next() { current = (Cell) it.next(); } @Override public Class keyType() { return rowType; } @Override public Object key() { return current.getRowKey(); } @Override public Class columnType() { return colType; } @Override public Object column() { return current.getColumnKey(); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } @Override public Object value() { return current.getValue(); } }; } /** * Gets an iterator wrapper for {@code BiMap}. * * @param map the collection, not null * @param declaredType the declared type, not null * @param keyType the value type, not null * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterator, not null */ @SuppressWarnings("rawtypes") public static final SerIterator biMap( final BiMap map, final Class declaredType, final Class keyType, final Class valueType, final List> valueTypeTypes) { return new SerIterator() { private final Iterator it = map.entrySet().iterator(); private Entry current; @Override public String metaTypeName() { return "BiMap"; } @Override public boolean metaTypeRequired() { // hack around Guava annoyance by assuming that size 0 and 1 ImmutableBiMap // was actually meant to be an ImmutableMap if ((declaredType == Map.class || declaredType == ImmutableMap.class) && map.size() < 2) { return false; } return BiMap.class.isAssignableFrom(declaredType) == false; } @Override public SerCategory category() { return SerCategory.MAP; } @Override public int size() { return map.size(); } @Override public boolean hasNext() { return it.hasNext(); } @Override public void next() { current = (Entry) it.next(); } @Override public Class keyType() { return keyType; } @Override public Object key() { return current.getKey(); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } @Override public Object value() { return current.getValue(); } }; } /** * Gets an iterable wrapper for {@code ImmutableList}. * * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable immutableList( final Class valueType, final List> valueTypeTypes) { final List coll = new ArrayList<>(); return new SerIterable() { @Override public SerIterator iterator() { return collection(coll, Object.class, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key != null) { throw new IllegalArgumentException("Unexpected key"); } for (int i = 0; i < count; i++) { coll.add(value); } } @Override public Object build() { return ImmutableList.copyOf(coll); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterable wrapper for {@code ImmutableSortedSet}. * * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable immutableSortedSet( final Class valueType, final List> valueTypeTypes) { final Set coll = new LinkedHashSet<>(); return new SerIterable() { @Override public SerIterator iterator() { return collection(coll, Object.class, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key != null) { throw new IllegalArgumentException("Unexpected key"); } for (int i = 0; i < count; i++) { coll.add(value); } } @Override public Object build() { return ImmutableSortedSet.copyOf(coll); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } /** * Gets an iterable wrapper for {@code ImmutableSet}. * * @param valueType the value type, not null * @param valueTypeTypes the generic parameters of the value type * @return the iterable, not null */ public static final SerIterable immutableSet( final Class valueType, final List> valueTypeTypes) { final Set coll = new LinkedHashSet<>(); return new SerIterable() { @Override public SerIterator iterator() { return collection(coll, Object.class, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key != null) { throw new IllegalArgumentException("Unexpected key"); } for (int i = 0; i < count; i++) { coll.add(value); } } @Override public Object build() { return ImmutableSet.copyOf(coll); } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } static SerIterable immutableMap( final Class keyType, final Class valueType, final List> valueTypeTypes) { final Map map = new LinkedHashMap<>(); return new SerIterable() { @Override public SerIterator iterator() { return map(map, Object.class, keyType, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key == null) { throw new IllegalArgumentException("Missing key"); } if (count != 1) { throw new IllegalArgumentException("Unexpected count"); } map.put(key, value); } @Override public Object build() { return ImmutableMap.copyOf(map); } @Override public SerCategory category() { return SerCategory.MAP; } @Override public Class keyType() { return keyType; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } static SerIterable immutableSortedMap( final Class keyType, final Class valueType, final List> valueTypeTypes) { final Map map = new LinkedHashMap<>(); return new SerIterable() { @Override public SerIterator iterator() { return map(map, Object.class, keyType, valueType, valueTypeTypes); } @Override public void add(Object key, Object column, Object value, int count) { if (key == null) { throw new IllegalArgumentException("Missing key"); } if (count != 1) { throw new IllegalArgumentException("Unexpected count"); } map.put(key, value); } @Override public Object build() { return ImmutableSortedMap.copyOf(map); } @Override public SerCategory category() { return SerCategory.MAP; } @Override public Class keyType() { return keyType; } @Override public Class valueType() { return valueType; } @Override public List> valueTypeTypes() { return valueTypeTypes; } }; } }