Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[api] Define standard property for handling duplicated keys #34

Merged
merged 6 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
94 changes: 84 additions & 10 deletions impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@

package org.eclipse.parsson;

import org.eclipse.parsson.api.BufferPool;

import jakarta.json.*;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.eclipse.parsson.api.BufferPool;

import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonConfig.KeyStrategy;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;
import jakarta.json.JsonWriter;

/**
* JsonObjectBuilder implementation
Expand All @@ -35,43 +49,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(jakarta.json.JsonConfig.KEY_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(jakarta.json.JsonConfig.KEY_STRATEGY));
}

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

JsonObjectBuilderImpl(Map<String, ?> map, BufferPool bufferPool, boolean rejectDuplicateKeys) {
JsonObjectBuilderImpl(Map<String, ?> 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(jakarta.json.JsonConfig.KEY_STRATEGY));
}

@Override
Expand Down Expand Up @@ -206,9 +227,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 +389,52 @@ public boolean containsKey(Object key) {
}
}

private static enum DuplicateStrategy {

NONE(KeyStrategy.NONE) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
if (previous != null) {
throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name));
} else {
return value;
}
}
},
FIRST(KeyStrategy.FIRST) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
if (previous != null) {
return previous;
} else {
return value;
}
}
},
LAST(KeyStrategy.LAST) {
@Override
protected JsonValue getValue(String name, JsonValue value, JsonValue previous) {
return value;
}
};

private final KeyStrategy property;

private DuplicateStrategy(KeyStrategy 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;
lukasj marked this conversation as resolved.
Show resolved Hide resolved
}

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,6 +56,7 @@ 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;

Expand All @@ -63,33 +65,36 @@ public class JsonParserImpl implements JsonParser {
private boolean closed = false;

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 @@ -162,7 +167,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 @@ -171,7 +176,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, jakarta.json.JsonConfig.KEY_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.
* jakarta.json.JsonConfig#NONE is preferred over {@link #REJECT_DUPLICATE_KEYS} for reading.
*/
String REJECT_DUPLICATE_KEYS = "org.eclipse.parsson.rejectDuplicateKeys";
}
Loading