diff --git a/pom.xml b/pom.xml index b7a5ba5..1dca9bc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 de.arraying Kotys - 0.5.2 + 0.6.0 diff --git a/src/main/java/de/arraying/kotys/JSON.java b/src/main/java/de/arraying/kotys/JSON.java index 21da5b3..ce7794a 100644 --- a/src/main/java/de/arraying/kotys/JSON.java +++ b/src/main/java/de/arraying/kotys/JSON.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * Copyright 2017 Arraying @@ -23,10 +24,10 @@ @SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"}) public class JSON { - private final Map rawContent = new LinkedHashMap<>(); private static final JSONUtil util = new JSONUtil(); - - private JSONMarshalFormat format = null; + private final Object writeLock = new Object(); + private Map rawContent = new HashMap<>(); + private JSONFormatter formatter = new JSONFormatter.DefaultImplementation(); /** * Creates an empty JSON object. @@ -118,6 +119,39 @@ public JSON(File file) this(util.getFileContent(file, true)); } + /** + * Specifies the map type to use for internal storage. + * This is so that when marshalling some specific ordering is retained. + * This map will get cleared. + * Note that a {@link java.util.concurrent.ConcurrentHashMap} is not allowed. + * @param map The map. + */ + public void use(Map map) { + if(map == null) { + throw new IllegalArgumentException("Provided map is null"); + } + if(map instanceof ConcurrentHashMap) { + throw new IllegalArgumentException("ConcurrentHashMap not allowed"); + } + Map cache = new HashMap<>(rawContent); + map.clear(); + synchronized(writeLock) { + rawContent = map; + rawContent.putAll(cache); + } + } + + /** + * Specifies the formatter to use when marshalling to a string. + * @param formatter A formatter instance. + */ + public void use(JSONFormatter formatter) { + if(formatter == null) { + throw new IllegalArgumentException("Formatter is null"); + } + this.formatter = formatter; + } + /** * Adds an entry to the current JSON object. * This entry can either be a raw JSON data type, or a custom object. @@ -132,7 +166,9 @@ public JSON put(String key, Object entry) if(key == null) { throw new IllegalArgumentException("Provided key is null"); } - rawContent.put(key, util.getFinalValue(entry)); + synchronized(writeLock) { + rawContent.put(key, util.getFinalValue(entry)); + } return this; } @@ -147,7 +183,9 @@ public JSON remove(String key) if(key == null) { throw new IllegalArgumentException("Provided key is null"); } - rawContent.remove(key); + synchronized(writeLock) { + rawContent.remove(key); + } return this; } @@ -286,27 +324,19 @@ public final int length() { * @return A string representation of the JSON object. */ public final String marshal() { - JSONFormatter formatter = new JSONFormatter() - .startObject(); + formatter.startObject(); Iterator> iterator = rawContent.entrySet().iterator(); while(iterator.hasNext()) { Map.Entry entry = iterator.next(); formatter.objectKey(entry.getKey()); Object valueRaw = entry.getValue(); - if(valueRaw instanceof JSON) { - formatter.object(((JSON) valueRaw).marshal()); - } else if(valueRaw instanceof JSONArray) { - formatter.array(((JSONArray) valueRaw).marshal()); - } else { - formatter.value(valueRaw); - } + util.format(formatter, valueRaw); if(iterator.hasNext()) { formatter.comma(); } } formatter.endObject(); - - return format != null ? format.format(formatter.result()) : formatter.result(); + return formatter.result(); } /** @@ -336,16 +366,6 @@ public final T marshal(Class clazz, String... ignoredKeys) return new JSONORM<>(clazz).mapTo(this, ignoredKeys); } - /** - * Set formatting to be applied for String marshal() - * @param format - Format to be applied to String marshal() - */ - - public JSON setFormat(JSONMarshalFormat format) { - this.format = format; - return this; - } - /** * Converts the JSON object to a string. * This method invokes the {@link #marshal()} method. diff --git a/src/main/java/de/arraying/kotys/JSONArray.java b/src/main/java/de/arraying/kotys/JSONArray.java index e8c3ea7..6bd0042 100644 --- a/src/main/java/de/arraying/kotys/JSONArray.java +++ b/src/main/java/de/arraying/kotys/JSONArray.java @@ -24,8 +24,10 @@ @SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"}) public class JSONArray implements Iterator { - private final List rawContent = new ArrayList<>(); private static final JSONUtil util = new JSONUtil(); + private final Object writeLock = new Object(); + private final List rawContent = new ArrayList<>(); + private JSONFormatter formatter = new JSONFormatter.DefaultImplementation(); /** * Creates an empty JSON array. @@ -88,6 +90,16 @@ public JSONArray(File file) throws IOException, IllegalStateException { this(util.getFileContent(file, false)); } + /** + * Specifies the formatter to use when marshalling to a string. + * @param formatter A formatter instance. + */ + public void use(JSONFormatter formatter) { + if(formatter == null) { + throw new IllegalArgumentException("Formatter is null"); + } + this.formatter = formatter; + } /** * Appends a value to the array. @@ -98,8 +110,10 @@ public JSONArray(File file) */ public JSONArray append(Object... values) throws IllegalArgumentException { - for(Object object : values) { - rawContent.add(util.getFinalValue(object)); + synchronized(writeLock) { + for(Object object : values) { + rawContent.add(util.getFinalValue(object)); + } } return this; } @@ -110,7 +124,9 @@ public JSONArray append(Object... values) * @return The object that was removed from the Array. */ public Object delete(int index) { - return rawContent.remove(index); + synchronized(writeLock) { + return rawContent.remove(index); + } } /** @@ -243,17 +259,10 @@ public final Object[] toArray() { * @return A string representation of the JSON array. */ public final String marshal() { - JSONFormatter formatter = new JSONFormatter() - .startArray(); + formatter.startArray(); for(int i = 0; i < length(); i++) { Object valueRaw = rawContent.get(i); - if(valueRaw instanceof JSON) { - formatter.object(((JSON) valueRaw).marshal()); - } else if(valueRaw instanceof JSONArray) { - formatter.array(((JSONArray) valueRaw).marshal()); - } else { - formatter.value(valueRaw); - } + util.format(formatter, valueRaw); if(i + 1 < length()) { formatter.comma(); } diff --git a/src/main/java/de/arraying/kotys/JSONDefaultMarshalFormat.java b/src/main/java/de/arraying/kotys/JSONDefaultMarshalFormat.java deleted file mode 100644 index 4aa6aa6..0000000 --- a/src/main/java/de/arraying/kotys/JSONDefaultMarshalFormat.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.arraying.kotys; - -/** - * Copyright 2019 ipr0james - *

- * 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. - */ -public class JSONDefaultMarshalFormat implements JSONMarshalFormat { - - @Override - public String format(String json) { - StringBuilder builder = new StringBuilder(); - int indentLevel = 0; - boolean inQuote = false; - - for(char type : json.toCharArray()) { - switch(type) { - case '"': - // switch the quoting status - inQuote = !inQuote; - builder.append(type); - break; - case ' ': - // For space: ignore the space if it is not being quoted. - if(inQuote) { - builder.append(type); - } - break; - case '{': - case '[': - // Starting a new block: increase the indent level - builder.append(type); - if(!inQuote) { - indentLevel++; - appendIndentedNewLine(indentLevel, builder); - } - break; - case '}': - case ']': - // Ending a new block; decrese the indent level - if(!inQuote) { - indentLevel--; - appendIndentedNewLine(indentLevel, builder); - } - builder.append(type); - break; - case ',': - // Ending a json item; create a new line after - builder.append(type); - if(!inQuote) { - appendIndentedNewLine(indentLevel, builder); - } - break; - case ':': - if(!inQuote) { - builder.append(": "); - } - break; - default: - builder.append(type); - } - } - return builder.toString(); - } - - /** - * Print a new line with indention at the beginning of the new line. - */ - private void appendIndentedNewLine(int indentLevel, StringBuilder builder) { - builder.append("\n"); - for(int i = 0; i < indentLevel; i++) { - builder.append(" "); - } - } -} diff --git a/src/main/java/de/arraying/kotys/JSONFormatter.java b/src/main/java/de/arraying/kotys/JSONFormatter.java index c9d7056..bfc10ea 100644 --- a/src/main/java/de/arraying/kotys/JSONFormatter.java +++ b/src/main/java/de/arraying/kotys/JSONFormatter.java @@ -16,110 +16,186 @@ * limitations under the License. */ @SuppressWarnings("UnusedReturnValue") -final class JSONFormatter { - - private final StringBuilder builder = new StringBuilder(); +public interface JSONFormatter { /** * Starts a new JSON object. * @return The formatter for chaining purposes. */ - JSONFormatter startObject() { - builder.append("{"); - return this; - } + JSONFormatter startObject(); /** * Ends the JSON object. * @return The formatter for chaining purposes. */ - JSONFormatter endObject() { - builder.append("}"); - return this; - } + JSONFormatter endObject(); /** * Starts a new JSON array. * @return The formatter for chaining purposes. */ - JSONFormatter startArray() { - builder.append("["); - return this; - } + JSONFormatter startArray(); /** * Ends the JSON array. * @return The formatter for chaining purposes. */ - JSONFormatter endArray() { - builder.append("]"); - return this; - } + JSONFormatter endArray(); /** * Appends a comma. * @return The formatter for chaining purposes. */ - JSONFormatter comma() { - builder.append(","); - return this; - } + JSONFormatter comma(); /** * Appends an object. * @param object The object. * @return The formatter for chaining purposes. */ - JSONFormatter object(String object) { - builder.append(object); - return this; - } + JSONFormatter object(String object); /** * Appends an array. * @param array The array. * @return The formatter for chaining purposes. */ - JSONFormatter array(String array) { - builder.append(array); - return this; - } + JSONFormatter array(String array); /** * Appends an object key. * @param key The key of the object. * @return The formatter for chaining purposes. */ - JSONFormatter objectKey(String key) { - builder.append("\"") - .append(key) - .append("\"") - .append(":"); - return this; - } + JSONFormatter objectKey(String key); /** * Appends the value. * @param value The value of the object. * @return The formatter for chaining purposes. */ - JSONFormatter value(Object value) { - if(value instanceof String) { - builder.append("\"") - .append(value) - .append("\""); - } else { - builder.append(value); - } - return this; - } + JSONFormatter value(Object value); /** * Gets the result of the formatting. * @return The resultant string. */ - String result() { - return builder.toString(); + String result(); + + /** + * Default implementation, minimal JSON formatter. + */ + final class DefaultImplementation implements JSONFormatter { + + private final StringBuilder builder = new StringBuilder(); + + /** + * Adds an opening brace. + * @return This. + */ + @Override + public JSONFormatter startObject() { + builder.append("{"); + return this; + } + + /** + * Adds a closing brace. + * @return This. + */ + @Override + public JSONFormatter endObject() { + builder.append("}"); + return this; + } + + /** + * Adds an opening bracket. + * @return This. + */ + @Override + public JSONFormatter startArray() { + builder.append("["); + return this; + } + + /** + * Adds a closing bracket. + * @return This. + */ + @Override + public JSONFormatter endArray() { + builder.append("]"); + return this; + } + + /** + * Adds a comma. + * @return This. + */ + @Override + public JSONFormatter comma() { + builder.append(","); + return this; + } + + /** + * Adds a JSON object. + * @return This. + */ + @Override + public JSONFormatter object(String object) { + builder.append(object); + return this; + } + + /** + * Adds a JSON array. + * @return This. + */ + @Override + public JSONFormatter array(String array) { + builder.append(array); + return this; + } + + /** + * Adds an object key, a string enclosed in quotation marks followed by a colon. + * @return This. + */ + @Override + public JSONFormatter objectKey(String key) { + builder.append("\"") + .append(key) + .append("\"") + .append(":"); + return this; + } + + /** + * Adds a numerical of textual value. + * @return This. + */ + @Override + public JSONFormatter value(Object value) { + if(value instanceof String) { + builder.append("\"") + .append(value) + .append("\""); + } else { + builder.append(value); + } + return this; + } + + /** + * @return The resultant string. + */ + @Override + public String result() { + return builder.toString(); + } + } } diff --git a/src/main/java/de/arraying/kotys/JSONMarshalFormat.java b/src/main/java/de/arraying/kotys/JSONMarshalFormat.java deleted file mode 100644 index 5cda93e..0000000 --- a/src/main/java/de/arraying/kotys/JSONMarshalFormat.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.arraying.kotys; - -/** - * Copyright 2019 ipr0james - *

- * 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. - */ -public interface JSONMarshalFormat { - - String format(String json); -} diff --git a/src/main/java/de/arraying/kotys/JSONUtil.java b/src/main/java/de/arraying/kotys/JSONUtil.java index e1b7ec8..ae7deb3 100644 --- a/src/main/java/de/arraying/kotys/JSONUtil.java +++ b/src/main/java/de/arraying/kotys/JSONUtil.java @@ -62,6 +62,22 @@ String getFileContent(File file, boolean object) return new String(Files.readAllBytes(file.toPath())); } + /** + * Applies a raw JSON object to the formatter. + * This method exists such that there's no duplicate method. + * @param formatter The formatter instance. + * @param valueRaw The raw value. + */ + void format(JSONFormatter formatter, Object valueRaw) { + if(valueRaw instanceof JSON) { + formatter.object(((JSON) valueRaw).marshal()); + } else if(valueRaw instanceof JSONArray) { + formatter.array(((JSONArray) valueRaw).marshal()); + } else { + formatter.value(valueRaw); + } + } + /** * Checks if the specified object is a valid JSON data type. * This presumes that all arrays have already been parsed.