com.fasterxml.jackson.jr.ob.api.MapBuilder Maven / Gradle / Ivy
Show all versions of jackson-jr-objects Show documentation
package com.fasterxml.jackson.jr.ob.api;
import java.util.*;
import com.fasterxml.jackson.jr.ob.JSON;
import com.fasterxml.jackson.jr.ob.JSON.Feature;
import com.fasterxml.jackson.jr.ob.impl.DeferredMap;
import com.fasterxml.jackson.jr.ob.JSONObjectException;
/**
* Helper class that is used for constructing {@link java.util.Map}s
* to map JSON Object values in.
*
* Objects server both as "factories" for creating new builders (blueprint
* style), and as actual builders. For each distinct read operation,
* {@link #newBuilder} will be called at least once; this instance
* may be used and reused multiple times, as calling {@link #start}
* will reset the state so that more {@link List}s may be built.
*/
public abstract class MapBuilder
{
protected final int _features;
protected final boolean _checkDups;
/**
* Optional {@link Map} implementation class, used when specific
* implementation is desired.
*/
protected final Class> _mapType;
protected MapBuilder(int features, Class> type) {
_features = features;
_checkDups = JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS.isEnabled(features);
_mapType = type;
}
/**
* Factory method for getting a blueprint instance of the default
* {@link MapBuilder} implementation.
*/
public static MapBuilder defaultImpl() {
return new Default(0, null);
}
public abstract MapBuilder newBuilder(int features);
public abstract MapBuilder newBuilder(Class> mapImpl);
public MapBuilder newBuilder() {
return newBuilder(_features);
}
public final boolean isEnabled(JSON.Feature f) {
return f.isEnabled(_features);
}
public abstract MapBuilder start();
public abstract MapBuilder put(String key, Object value);
public abstract Map build();
/**
* Specialized method that is called when an empty list needs to
* be constructed; this may be a new list, or an immutable shared
* List, depending on implementation.
*
* Default implementation simply calls:
*
* start().build();
*
* which assumes that a builder has been constructed with {@link #newBuilder}
*/
public Map emptyMap() throws JSONObjectException {
return start().build();
}
/**
* Specialized method that is called when an empty list needs to
* be constructed; this may be a new list, or an immutable shared
* List, depending on implementation.
*
* Default implementation simply calls:
*
* start().put(key, value).build();
*
*/
public Map singletonMap(String key, Object value) throws JSONObjectException {
return start().put(key, value).build();
}
/*
/**********************************************************
/* Default implementation
/**********************************************************
*/
/**
* Default {@link MapBuilder} implementation which builds
* either {@link HashMap} or {@link LinkedHashMap} instances
* (depending on {@link Feature#PRESERVE_FIELD_ORDERING}).
* It may also, with alternate configuration,
* use {@link Collections#emptyMap()} for empty Maps,
* if allowed with {@link Feature#READ_ONLY}.
*/
public static class Default extends MapBuilder
{
protected Map _current;
protected Default(int features, Class> type) {
super(features, type);
}
@Override
public MapBuilder newBuilder(int features) {
return new Default(features, _mapType);
}
@Override
public MapBuilder newBuilder(Class> mapImpl) {
return new Default(_features, mapImpl);
}
@Override
public MapBuilder start() {
// If this builder is "busy", create a new one...
if (_current != null) {
return newBuilder().start();
}
_current = _map(12);
return this;
}
@Override
public Map build() {
Map result = _current;
_current = null;
return result;
}
@Override
public MapBuilder put(String key, Object value) {
if (_checkDups) {
if (_current.containsKey(key)) {
// 14-Apr-2017, tatu: Note that choice of `IllegalArgumentException` is arbitrary
// but not random: caller catches and re-packages it to give context
throw new IllegalArgumentException("Duplicate key (key '"+key+"')");
}
}
_current.put(key, value);
return this;
}
@Override
public Map emptyMap() {
if ((_mapType == null) && isEnabled(Feature.READ_ONLY)) {
return Collections.emptyMap();
}
return _map(4);
}
private final Map _map(int initialSize) {
if (_mapType != null) {
try {
@SuppressWarnings("unchecked")
Map m = (Map)
_mapType.getDeclaredConstructor().newInstance((Object[]) null);
return m;
} catch (Exception e) {
Throwable t = e;
while (t.getCause() != null) {
t = t.getCause();
}
throw new IllegalArgumentException("Failed to create an instance of "
+_mapType.getName()+" ("+t.getClass().getName()+"): "+t.getMessage());
}
}
final boolean sort = isEnabled(Feature.PRESERVE_FIELD_ORDERING);
if (isEnabled(Feature.USE_DEFERRED_MAPS)) {
return new DeferredMap(sort, initialSize);
}
return sort ? new LinkedHashMap(initialSize)
: new HashMap(initialSize);
}
}
}