Skip to content

Commit

Permalink
[api] Define standard property for handling duplicated keys
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos committed Sep 29, 2021
1 parent 1820e42 commit 19d291d
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ class JsonBuilderFactoryImpl implements JsonBuilderFactory {

@Override
public JsonObjectBuilder createObjectBuilder() {
return new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys);
return new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys, Collections.emptyMap());
}

@Override
public JsonObjectBuilder createObjectBuilder(JsonObject object) {
return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys);
return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys, Collections.emptyMap());
}

@Override
public JsonObjectBuilder createObjectBuilder(Map<String, Object> object) {
return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys);
return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys, Collections.emptyMap());
}

@Override
Expand Down
72 changes: 66 additions & 6 deletions impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,43 +35,50 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder {
protected Map<String, JsonValue> valueMap;
private final BufferPool bufferPool;
private final boolean rejectDuplicateKeys;
private final DuplicateStrategy duplicateStrategy;

JsonObjectBuilderImpl(BufferPool bufferPool) {
this.bufferPool = bufferPool;
rejectDuplicateKeys = false;
this.duplicateStrategy = null;
}

JsonObjectBuilderImpl(BufferPool bufferPool, boolean rejectDuplicateKeys) {
JsonObjectBuilderImpl(BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.duplicateStrategy = DuplicateStrategy.strategyFromProperty(config.get(JsonReader.DUPLICATE_KEYS_STRATEGY));
}

JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool) {
this.bufferPool = bufferPool;
valueMap = new LinkedHashMap<>();
valueMap.putAll(object);
rejectDuplicateKeys = false;
this.duplicateStrategy = null;
}

JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool, boolean rejectDuplicateKeys) {
JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
valueMap = new LinkedHashMap<>();
valueMap.putAll(object);
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.duplicateStrategy = DuplicateStrategy.strategyFromProperty(config.get(JsonReader.DUPLICATE_KEYS_STRATEGY));
}

JsonObjectBuilderImpl(Map<String, Object> map, BufferPool bufferPool) {
this.bufferPool = bufferPool;
valueMap = new LinkedHashMap<>();
populate(map);
rejectDuplicateKeys = false;
this.duplicateStrategy = null;
}

JsonObjectBuilderImpl(Map<String, Object> map, BufferPool bufferPool, boolean rejectDuplicateKeys) {
JsonObjectBuilderImpl(Map<String, Object> map, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
valueMap = new LinkedHashMap<>();
populate(map);
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.duplicateStrategy = DuplicateStrategy.strategyFromProperty(config.get(JsonReader.DUPLICATE_KEYS_STRATEGY));
}

@Override
Expand Down Expand Up @@ -206,9 +213,14 @@ private void putValueMap(String name, JsonValue value) {
if (valueMap == null) {
this.valueMap = new LinkedHashMap<>();
}
JsonValue previousValue = valueMap.put(name, value);
if (rejectDuplicateKeys && previousValue != null) {
throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name));
if (duplicateStrategy == null) {
JsonValue previousValue = valueMap.put(name, value);
if (rejectDuplicateKeys && previousValue != null) {
throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name));
}
} else {
JsonValue previousValue = valueMap.get(name);
valueMap.put(name, duplicateStrategy.getValue(name, value, previousValue));
}
}

Expand Down Expand Up @@ -363,4 +375,52 @@ public boolean containsKey(Object key) {
}
}

private static enum DuplicateStrategy {

STRICT(JsonReader.STRICT) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
if (previous != null) {
throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name));
} else {
return value;
}
}
},
USE_FIRST(JsonReader.USE_FIRST) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
if (previous != null) {
return previous;
} else {
return value;
}
}
},
USE_LAST(JsonReader.USE_LAST) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
return value;
}
};

private final String property;

private DuplicateStrategy(String property) {
this.property = property;
}

private static DuplicateStrategy strategyFromProperty(Object value) {
if (value != null) {
for (DuplicateStrategy strategy : DuplicateStrategy.values()) {
if (strategy.property.equals(value)) {
return strategy;
}
}
}
return null;
}

protected abstract JsonValue getValue(String name, JsonValue value, JsonValue previous);
}
}
21 changes: 13 additions & 8 deletions impl/src/main/java/org/eclipse/parsson/JsonParserImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Spliterator;
Expand Down Expand Up @@ -55,40 +56,44 @@ public class JsonParserImpl implements JsonParser {

private final BufferPool bufferPool;
private final boolean rejectDuplicateKeys;
private final Map<String, ?> config;
private Context currentContext = new NoneContext();
private Event currentEvent;

private final Stack stack = new Stack();
private final JsonTokenizer tokenizer;

public JsonParserImpl(Reader reader, BufferPool bufferPool) {
this(reader, bufferPool, false);
this(reader, bufferPool, false, Collections.emptyMap());
}

public JsonParserImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) {
public JsonParserImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.config = config;
tokenizer = new JsonTokenizer(reader, bufferPool);
}

public JsonParserImpl(InputStream in, BufferPool bufferPool) {
this(in, bufferPool, false);
this(in, bufferPool, false, Collections.emptyMap());
}

public JsonParserImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) {
public JsonParserImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.config = config;
UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in);
tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool);
}

public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) {
this(in, encoding, bufferPool, false);
this(in, encoding, bufferPool, false, Collections.emptyMap());
}

public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool, boolean rejectDuplicateKeys) {
public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
this.bufferPool = bufferPool;
this.rejectDuplicateKeys = rejectDuplicateKeys;
this.config = config;
tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool);
}

Expand Down Expand Up @@ -161,7 +166,7 @@ public JsonObject getObject() {
throw new IllegalStateException(
JsonMessages.PARSER_GETOBJECT_ERR(currentEvent));
}
return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys));
return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys, config));
}

@Override
Expand All @@ -170,7 +175,7 @@ public JsonValue getValue() {
case START_ARRAY:
return getArray(new JsonArrayBuilderImpl(bufferPool));
case START_OBJECT:
return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys));
return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys, config));
case KEY_NAME:
case VALUE_STRING:
return new JsonStringImpl(getCharSequence());
Expand Down
7 changes: 7 additions & 0 deletions impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
pool = bufferPool;
} else {
providerConfig = new HashMap<>();
addKnowProperty(providerConfig, config, JsonReader.DUPLICATE_KEYS_STRATEGY);
if (rejectDuplicateKeys = JsonProviderImpl.isRejectDuplicateKeysEnabled(config)) {
providerConfig.put(JsonConfig.REJECT_DUPLICATE_KEYS, true);
}
Expand Down Expand Up @@ -280,6 +281,12 @@ public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
return new JsonBuilderFactoryImpl(pool, rejectDuplicateKeys);
}

private void addKnowProperty(Map<String, Object> providerConfig, Map<String, ?> config, String property) {
if (config.containsKey(property)) {
providerConfig.put(property, config.get(property));
}
}

static boolean isPrettyPrintingEnabled(Map<String, ?> config) {
return config.containsKey(JsonGenerator.PRETTY_PRINTING);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ class JsonReaderFactoryImpl implements JsonReaderFactory {

@Override
public JsonReader createReader(Reader reader) {
return new JsonReaderImpl(reader, bufferPool, rejectDuplicateKeys);
return new JsonReaderImpl(reader, bufferPool, rejectDuplicateKeys, config);
}

@Override
public JsonReader createReader(InputStream in) {
return new JsonReaderImpl(in, bufferPool, rejectDuplicateKeys);
return new JsonReaderImpl(in, bufferPool, rejectDuplicateKeys, config);
}

@Override
public JsonReader createReader(InputStream in, Charset charset) {
return new JsonReaderImpl(in, charset, bufferPool, rejectDuplicateKeys);
return new JsonReaderImpl(in, charset, bufferPool, rejectDuplicateKeys, config);
}

@Override
Expand Down
21 changes: 12 additions & 9 deletions impl/src/main/java/org/eclipse/parsson/JsonReaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;

import jakarta.json.JsonArray;
import jakarta.json.JsonException;
import jakarta.json.JsonObject;
Expand All @@ -41,29 +44,29 @@ class JsonReaderImpl implements JsonReader {
private final BufferPool bufferPool;

JsonReaderImpl(Reader reader, BufferPool bufferPool) {
this(reader, bufferPool, false);
this(reader, bufferPool, false, Collections.emptyMap());
}

JsonReaderImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) {
parser = new JsonParserImpl(reader, bufferPool, rejectDuplicateKeys);
JsonReaderImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
parser = new JsonParserImpl(reader, bufferPool, rejectDuplicateKeys, config);
this.bufferPool = bufferPool;
}

JsonReaderImpl(InputStream in, BufferPool bufferPool) {
this(in, bufferPool, false);
this(in, bufferPool, false, Collections.emptyMap());
}

JsonReaderImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) {
parser = new JsonParserImpl(in, bufferPool, rejectDuplicateKeys);
JsonReaderImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
parser = new JsonParserImpl(in, bufferPool, rejectDuplicateKeys, config);
this.bufferPool = bufferPool;
}

JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) {
this(in, charset, bufferPool, false);
this(in, charset, bufferPool, false, Collections.emptyMap());
}

JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool, boolean rejectDuplicateKeys) {
parser = new JsonParserImpl(in, charset, bufferPool, rejectDuplicateKeys);
JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool, boolean rejectDuplicateKeys, Map<String, ?> config) {
parser = new JsonParserImpl(in, charset, bufferPool, rejectDuplicateKeys, config);
this.bufferPool = bufferPool;
}

Expand Down
1 change: 1 addition & 0 deletions impl/src/main/java/org/eclipse/parsson/api/JsonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public interface JsonConfig {
/**
* Configuration property to reject duplicate keys. The value of the property could be
* be anything.
* {@link JsonReader#STRICT} is preferred over {@link #REJECT_DUPLICATE_KEYS} for reading.
*/
String REJECT_DUPLICATE_KEYS = "org.eclipse.parsson.rejectDuplicateKeys";
}
Loading

0 comments on commit 19d291d

Please sign in to comment.