From 24093491a8d9c1dfa8e062df9ae0c1dde56bba5a Mon Sep 17 00:00:00 2001 From: Tamas Perger Date: Tue, 21 Feb 2023 19:13:07 +0000 Subject: [PATCH] refactor: introduce ParserConfiguration class hierarchy --- .../org/json/JSONMLParserConfiguration.java | 83 ++----------- .../java/org/json/ParserConfiguration.java | 112 ++++++++++++++++++ .../java/org/json/XMLParserConfiguration.java | 64 ++-------- src/test/java/org/json/junit/JSONMLTest.java | 29 +++++ 4 files changed, 162 insertions(+), 126 deletions(-) create mode 100644 src/main/java/org/json/ParserConfiguration.java diff --git a/src/main/java/org/json/JSONMLParserConfiguration.java b/src/main/java/org/json/JSONMLParserConfiguration.java index b7162bf2f..b2514ab6e 100644 --- a/src/main/java/org/json/JSONMLParserConfiguration.java +++ b/src/main/java/org/json/JSONMLParserConfiguration.java @@ -7,17 +7,12 @@ * Configuration object for the XML to JSONML parser. The configuration is immutable. */ @SuppressWarnings({""}) -public class JSONMLParserConfiguration { - /** - * Used to indicate there's no defined limit to the maximum nesting depth when parsing a XML - * document to JSONML. - */ - public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1; +public class JSONMLParserConfiguration extends ParserConfiguration { /** - * The default maximum nesting depth when parsing a XML document to JSONML. + * We can override the default maximum nesting depth if needed. */ - public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; + public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH; /** Original Configuration of the XML to JSONML Parser. */ public static final JSONMLParserConfiguration ORIGINAL @@ -26,22 +21,12 @@ public class JSONMLParserConfiguration { public static final JSONMLParserConfiguration KEEP_STRINGS = new JSONMLParserConfiguration().withKeepStrings(true); - /** - * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if - * they should try to be guessed into JSON values (numeric, boolean, string) - */ - private boolean keepStrings; - - /** - * The maximum nesting depth when parsing a XML document to JSONML. - */ - private int maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH; - /** * Default parser configuration. Does not keep strings (tries to implicitly convert values). */ public JSONMLParserConfiguration() { - this.keepStrings = false; + super(); + this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH; } /** @@ -50,9 +35,8 @@ public JSONMLParserConfiguration() { * false to try and convert XML string values into a JSON value. * @param maxNestingDepth int to limit the nesting depth */ - private JSONMLParserConfiguration(final boolean keepStrings, final int maxNestingDepth) { - this.keepStrings = keepStrings; - this.maxNestingDepth = maxNestingDepth; + protected JSONMLParserConfiguration(final boolean keepStrings, final int maxNestingDepth) { + super(keepStrings, maxNestingDepth); } /** @@ -71,58 +55,13 @@ protected JSONMLParserConfiguration clone() { ); } - /** - * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if - * they should try to be guessed into JSON values (numeric, boolean, string) - * - * @return The keepStrings configuration value. - */ - public boolean isKeepStrings() { - return this.keepStrings; - } - - /** - * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if - * they should try to be guessed into JSON values (numeric, boolean, string) - * - * @param newVal - * new value to use for the keepStrings configuration option. - * - * @return The existing configuration will not be modified. A new configuration is returned. - */ + @Override public JSONMLParserConfiguration withKeepStrings(final boolean newVal) { - JSONMLParserConfiguration newConfig = this.clone(); - newConfig.keepStrings = newVal; - return newConfig; - } - - /** - * The maximum nesting depth that the parser will descend before throwing an exception - * when parsing the XML into JSONML. - * @return the maximum nesting depth set for this configuration - */ - public int getMaxNestingDepth() { - return maxNestingDepth; + return super.withKeepStrings(newVal); } - /** - * Defines the maximum nesting depth that the parser will descend before throwing an exception - * when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser - * will throw a JsonException if the maximum depth is reached. - * Using any negative value as a parameter is equivalent to setting no limit to the nesting depth, - * which means the parses will go as deep as the maximum call stack size allows. - * @param maxNestingDepth the maximum nesting depth allowed to the XML parser - * @return The existing configuration will not be modified. A new configuration is returned. - */ + @Override public JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) { - JSONMLParserConfiguration newConfig = this.clone(); - - if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) { - newConfig.maxNestingDepth = maxNestingDepth; - } else { - newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH; - } - - return newConfig; + return super.withMaxNestingDepth(maxNestingDepth); } } diff --git a/src/main/java/org/json/ParserConfiguration.java b/src/main/java/org/json/ParserConfiguration.java new file mode 100644 index 000000000..519e2099d --- /dev/null +++ b/src/main/java/org/json/ParserConfiguration.java @@ -0,0 +1,112 @@ +package org.json; +/* +Public Domain. +*/ + +/** + * Configuration base object for parsers. The configuration is immutable. + */ +@SuppressWarnings({""}) +public class ParserConfiguration { + /** + * Used to indicate there's no defined limit to the maximum nesting depth when parsing a document. + */ + public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1; + + /** + * The default maximum nesting depth when parsing a document. + */ + public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; + + /** + * Specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + */ + protected boolean keepStrings; + + /** + * The maximum nesting depth when parsing a document. + */ + protected int maxNestingDepth; + + public ParserConfiguration() { + this.keepStrings = false; + this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH; + } + + protected ParserConfiguration(final boolean keepStrings, final int maxNestingDepth) { + this.keepStrings = keepStrings; + this.maxNestingDepth = maxNestingDepth; + } + + /** + * Provides a new instance of the same configuration. + */ + @Override + protected ParserConfiguration clone() { + // future modifications to this method should always ensure a "deep" + // clone in the case of collections. i.e. if a Map is added as a configuration + // item, a new map instance should be created and if possible each value in the + // map should be cloned as well. If the values of the map are known to also + // be immutable, then a shallow clone of the map is acceptable. + return new ParserConfiguration( + this.keepStrings, + this.maxNestingDepth + ); + } + + /** + * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + * + * @return The keepStrings configuration value. + */ + public boolean isKeepStrings() { + return this.keepStrings; + } + + /** + * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + * + * @param newVal + * new value to use for the keepStrings configuration option. + * + * @return The existing configuration will not be modified. A new configuration is returned. + */ + public T withKeepStrings(final boolean newVal) { + T newConfig = (T)this.clone(); + newConfig.keepStrings = newVal; + return newConfig; + } + + /** + * The maximum nesting depth that the parser will descend before throwing an exception + * when parsing the XML into JSONML. + * @return the maximum nesting depth set for this configuration + */ + public int getMaxNestingDepth() { + return maxNestingDepth; + } + + /** + * Defines the maximum nesting depth that the parser will descend before throwing an exception + * when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser + * will throw a JsonException if the maximum depth is reached. + * Using any negative value as a parameter is equivalent to setting no limit to the nesting depth, + * which means the parses will go as deep as the maximum call stack size allows. + * @param maxNestingDepth the maximum nesting depth allowed to the XML parser + * @return The existing configuration will not be modified. A new configuration is returned. + */ + public T withMaxNestingDepth(int maxNestingDepth) { + T newConfig = (T)this.clone(); + + if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) { + newConfig.maxNestingDepth = maxNestingDepth; + } else { + newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH; + } + + return newConfig; + } +} diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index 103023ed8..566146d6d 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -15,17 +15,12 @@ * @author AylwardJ */ @SuppressWarnings({""}) -public class XMLParserConfiguration { - /** - * Used to indicate there's no defined limit to the maximum nesting depth when parsing a XML - * document to JSON. - */ - public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1; +public class XMLParserConfiguration extends ParserConfiguration { /** * The default maximum nesting depth when parsing a XML document to JSON. */ - public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; +// public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; // We could override /** Original Configuration of the XML Parser. */ public static final XMLParserConfiguration ORIGINAL @@ -34,12 +29,6 @@ public class XMLParserConfiguration { public static final XMLParserConfiguration KEEP_STRINGS = new XMLParserConfiguration().withKeepStrings(true); - /** - * When parsing the XML into JSON, specifies if values should be kept as strings (true), or if - * they should try to be guessed into JSON values (numeric, boolean, string) - */ - private boolean keepStrings; - /** * The name of the key in a JSON Object that indicates a CDATA section. Historically this has * been the value "content" but can be changed. Use null to indicate no CDATA @@ -65,17 +54,12 @@ public class XMLParserConfiguration { */ private Set forceList; - /** - * The maximum nesting depth when parsing a XML document to JSON. - */ - private int maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH; - /** * Default parser configuration. Does not keep strings (tries to implicitly convert * values), and the CDATA Tag Name is "content". */ public XMLParserConfiguration () { - this.keepStrings = false; + super(); this.cDataTagName = "content"; this.convertNilAttributeToNull = false; this.xsiTypeMap = Collections.emptyMap(); @@ -122,7 +106,7 @@ public XMLParserConfiguration (final String cDataTagName) { */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) { - this.keepStrings = keepStrings; + super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH); this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = false; } @@ -141,7 +125,7 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) { - this.keepStrings = keepStrings; + super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH); this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; } @@ -162,12 +146,11 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull, final Map> xsiTypeMap, final Set forceList, final int maxNestingDepth) { - this.keepStrings = keepStrings; + super(keepStrings, maxNestingDepth); this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); this.forceList = Collections.unmodifiableSet(forceList); - this.maxNestingDepth = maxNestingDepth; } /** @@ -190,16 +173,6 @@ protected XMLParserConfiguration clone() { ); } - /** - * When parsing the XML into JSON, specifies if values should be kept as strings (true), or if - * they should try to be guessed into JSON values (numeric, boolean, string) - * - * @return The keepStrings configuration value. - */ - public boolean isKeepStrings() { - return this.keepStrings; - } - /** * When parsing the XML into JSON, specifies if values should be kept as strings (true), or if * they should try to be guessed into JSON values (numeric, boolean, string) @@ -209,10 +182,9 @@ public boolean isKeepStrings() { * * @return The existing configuration will not be modified. A new configuration is returned. */ + @Override public XMLParserConfiguration withKeepStrings(final boolean newVal) { - XMLParserConfiguration newConfig = this.clone(); - newConfig.keepStrings = newVal; - return newConfig; + return super.withKeepStrings(newVal); } /** @@ -318,15 +290,6 @@ public XMLParserConfiguration withForceList(final Set forceList) { return newConfig; } - /** - * The maximum nesting depth that the parser will descend before throwing an exception - * when parsing the XML into JSON. - * @return the maximum nesting depth set for this configuration - */ - public int getMaxNestingDepth() { - return maxNestingDepth; - } - /** * Defines the maximum nesting depth that the parser will descend before throwing an exception * when parsing the XML into JSON. The default max nesting depth is 512, which means the parser @@ -336,15 +299,8 @@ public int getMaxNestingDepth() { * @param maxNestingDepth the maximum nesting depth allowed to the XML parser * @return The existing configuration will not be modified. A new configuration is returned. */ + @Override public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) { - XMLParserConfiguration newConfig = this.clone(); - - if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) { - newConfig.maxNestingDepth = maxNestingDepth; - } else { - newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH; - } - - return newConfig; + return super.withMaxNestingDepth(maxNestingDepth); } } diff --git a/src/test/java/org/json/junit/JSONMLTest.java b/src/test/java/org/json/junit/JSONMLTest.java index 1514ddda6..35c0af2c4 100644 --- a/src/test/java/org/json/junit/JSONMLTest.java +++ b/src/test/java/org/json/junit/JSONMLTest.java @@ -895,7 +895,36 @@ public void testToJSONArrayMaxNestingDepthWithValidFittingXML() { } + @Test + public void testToJSONObjectMaxDefaultNestingDepthIsRespected() { + final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", ""); + try { + JSONML.toJSONObject(wayTooLongMalformedXML, JSONMLParserConfiguration.ORIGINAL); + + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">", + e.getMessage().startsWith("Maximum nesting depth of " + JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH)); + } + } + + @Test + public void testToJSONObjectUnlimitedNestingDepthIsPossible() { + int actualDepth = JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH +10; + final String deeperThanDefaultMax = new String(new char[actualDepth]).replace("\0", "") + + "value" + + new String(new char[actualDepth]).replace("\0", ""); + + try { + JSONML.toJSONObject(deeperThanDefaultMax, JSONMLParserConfiguration.ORIGINAL + .withMaxNestingDepth(JSONMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH)); + } catch (JSONException e) { + e.printStackTrace(); + fail("XML document should be parsed beyond the default maximum depth if maxNestingDepth " + + "parameter is set to -1 in JSONMLParserConfiguration"); + } + } @Test