aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Grebac2014-05-02 09:51:37 (EDT)
committerLukas Jungmann2014-07-15 11:14:06 (EDT)
commit4303983bf8b2f5e86358726b49859b3807f578e5 (patch)
tree5748835e5154498492040de4f3c73adede5e31b7
parent279a774d39ecee7bb287240d6aeac9c0a0b5e7f6 (diff)
downloadeclipselink.runtime-4303983bf8b2f5e86358726b49859b3807f578e5.zip
eclipselink.runtime-4303983bf8b2f5e86358726b49859b3807f578e5.tar.gz
eclipselink.runtime-4303983bf8b2f5e86358726b49859b3807f578e5.tar.bz2
Fix for 428560 - Poor performance of JSONReader.string(String)
Signed-off-by: Martin Grebac <martin.grebac@oracle.com>
-rw-r--r--foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/AllTests.java24
-rw-r--r--foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/JsonTestSuite.java78
-rw-r--r--foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/record/json/JSONReader.java190
-rw-r--r--moxy/eclipselink.moxy.test/resource/org/eclipse/persistence/testing/jaxb/json/multiline/json.json1
-rw-r--r--moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/JSONTestSuite.java4
-rw-r--r--moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiBean.java36
-rw-r--r--moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiLineStringTestCases.java33
7 files changed, 281 insertions, 85 deletions
diff --git a/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/AllTests.java b/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/AllTests.java
new file mode 100644
index 0000000..2f36ba8
--- /dev/null
+++ b/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/AllTests.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Martin Grebac - 2.6.0 July 2014
+ ******************************************************************************/
+package org.eclipse.persistence.internal.oxm.record.json;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({
+ org.eclipse.persistence.internal.oxm.record.json.JsonTestSuite.class
+ }
+)
+public class AllTests {} \ No newline at end of file
diff --git a/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/JsonTestSuite.java b/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/JsonTestSuite.java
new file mode 100644
index 0000000..59deaf4
--- /dev/null
+++ b/foundation/eclipselink.core.test/src/org/eclipse/persistence/internal/oxm/record/json/JsonTestSuite.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Martin Grebac - 2.6.0 July 2014
+ ******************************************************************************/
+package org.eclipse.persistence.internal.oxm.record.json;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import org.eclipse.persistence.testing.framework.TestCase;
+
+/**
+ * Test Json support in core.
+ */
+public class JsonTestSuite extends TestCase {
+
+ private static final Map<String, String> testMap = new TreeMap<String,String>();
+
+ public JsonTestSuite() {
+ super();
+ setName(getName());
+ setDescription("Test Json implementation methods");
+ }
+
+ public void setup() throws Exception {
+ testMap.put("\"abc\"", "abc");
+ testMap.put("\"a\\bbc\"", "a\bbc");
+ testMap.put("\"\\n\"", "\n");
+ testMap.put("\"\\r\\n\"", "\r\n");
+ testMap.put("\"a\\r\\n\"", "a\r\n");
+ testMap.put("\"\r\n\"", "\n");
+ testMap.put("\"\\u041e\\u041a\"", "ОК");
+ testMap.put("\"abc\n\"", "abc\n");
+ testMap.put("\"abc\r\n\"", "abc\n");
+ testMap.put("\"abc\\r\\n\"", "abc\r\n");
+ testMap.put("\"abcrr\\n\\nd\"", "abcrr\n\nd");
+ testMap.put("\"abcrr\\n\\ndd\"", "abcrr\n\ndd");
+
+ // create some large string to deal with
+ StringBuilder input = new StringBuilder();
+ input.append("\"");
+ for (int i=1;i<10000;i++) {
+ input.append("aaaaaaaaaaaaaaaaaa\baaaaaaaaaaaaaaaaaaa\\n\n");
+ }
+ input.append("\"");
+
+ StringBuilder output = new StringBuilder();
+ for (int i=1;i<10000;i++) {
+ output.append("aaaaaaaaaaaaaaaaaa\baaaaaaaaaaaaaaaaaaa\n\n");
+ }
+ testMap.put(input.toString(), output.toString());
+ }
+
+ // Execute all tests in suite.
+ public void test() {
+ jsonStringEscapingTest();
+ }
+
+ public void jsonStringEscapingTest() {
+ Iterator<Map.Entry<String,String>> testEntryIterator = testMap.entrySet().iterator();
+ while (testEntryIterator.hasNext()) {
+ Map.Entry<String,String> entry = testEntryIterator.next();
+ String input = entry.getKey();
+ String output = JSONReader.string(input);
+ String expectedOutput = entry.getValue();
+ assertEquals("Difference found in json string escaping for string " + input, expectedOutput, output);
+ }
+ }
+
+}
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/record/json/JSONReader.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/record/json/JSONReader.java
index fcaad59..26a7963 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/record/json/JSONReader.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/record/json/JSONReader.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -22,7 +22,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import javax.xml.namespace.QName;
@@ -64,7 +63,6 @@ public class JSONReader extends XMLReaderAdapter {
private static final String TRUE = "true";
private static final String FALSE = "false";
- private Properties properties;
private String attributePrefix = null;
private NamespaceResolver namespaces = null;
private boolean includeRoot;
@@ -76,9 +74,10 @@ public class JSONReader extends XMLReaderAdapter {
this(attrPrefix, nr, namespaceAware, includeRoot, namespaceSeparator, errorHandler, textWrapper, null);
}
+ @SuppressWarnings("StringEquality")
public JSONReader(String attrPrefix, NamespaceResolver nr, boolean namespaceAware, boolean includeRoot, Character namespaceSeparator, ErrorHandler errorHandler, String textWrapper, Class unmarshalClass){
this.attributePrefix = attrPrefix;
- if(attributePrefix == Constants.EMPTY_STRING){
+ if (attributePrefix == Constants.EMPTY_STRING) {
attributePrefix = null;
}
namespaces = nr;
@@ -94,9 +93,9 @@ public class JSONReader extends XMLReaderAdapter {
this.unmarshalClass = unmarshalClass;
}
- private JSONAttributes attributes = new JSONAttributes();
+ private final JSONAttributes attributes = new JSONAttributes();
- @Override
+ @Override
public void parse(InputSource input) throws IOException, SAXException {
try {
CharStream charStream;
@@ -295,7 +294,7 @@ public class JSONReader extends XMLReaderAdapter {
break;
}
}
- if(valueTree != null && valueTree.getType() == JSONLexer.NULL){
+ if (valueTree.getType() == JSONLexer.NULL) {
contentHandler.setNil(true);
}
@@ -438,7 +437,8 @@ public class JSONReader extends XMLReaderAdapter {
}
}
}
-
+
+ @Override
public boolean isNullRepresentedByXsiNil(AbstractNullPolicy nullPolicy){
return true;
}
@@ -452,6 +452,7 @@ public class JSONReader extends XMLReaderAdapter {
isInCollection = false;
}
+ @Override
public boolean isInCollection(){
return isInCollection;
}
@@ -463,85 +464,104 @@ public class JSONReader extends XMLReaderAdapter {
}
return((currentNode.getNonAttributeChildrenMap() == null
- || currentNode.getNonAttributeChildrenMap().size() ==0
+ || currentNode.getNonAttributeChildrenMap().isEmpty()
|| (currentNode.getNonAttributeChildrenMap().size() == 1 && currentNode.getTextNode() != null))
&& textWrapper != null && textWrapper.equals(localName));
}
- private static String string(String string) {
- string = string.substring(1, string.length()-1);
- string = string.replace("\r\n", "\n");
- String returnString = "";
+ /**
+ * Formats Java formatted string to a real String instance.
+ * @param Java escaped string with quotation marks
+ * @return string instance
+ */
+ static String string(String string) {
- int slashIndex = string.indexOf('\\');
-
- if(slashIndex == -1){
- return string;
- }
-
- int position = 0;
- while(slashIndex > -1){
- String subString = string.substring(position, slashIndex);
- returnString += subString;
- position = slashIndex;
-
- char nextChar = string.charAt(slashIndex + 1);
- switch (nextChar){
- case 'b':{
- position += 2;
- returnString += '\b';
- break;
- }
- case 'r':{
- position += 2;
- returnString += '\r';
- break;
- }
- case 'f':{
- position += 2;
- returnString += '\f';
- break;
- }
- case 'n':{
- position += 2;
- returnString += '\n';
- break;
- }
- case 't': {
- position += 2;
- returnString += '\t';
- break;
- }
- case '"': {
- position += 2;
- returnString += '"';
- break;
- }
- case '\\':{
- position += 2;
- returnString += '\\';
- break;
- }
- case '/':{
- position += 2;
- returnString += '/';
- break;
- }
- case 'u':{
- position += 6;
- String hexValue = string.substring(slashIndex+2, slashIndex+6);
- returnString += Character.toString((char)Integer.parseInt(hexValue, 16));
- break;
- }
- }
- slashIndex = string.indexOf('\\', position);
+ char[] inputStringChars = string.toCharArray();
+
+ int lengthWithoutQuotation = inputStringChars.length-2;
+ StringBuilder returnStringBuilder = new StringBuilder(lengthWithoutQuotation);
+
+ int begin = 1;
+ int end = lengthWithoutQuotation;
+
+ int position = begin;
+
+ while (position <= end) {
+ if (inputStringChars[position] == '\\') {
+ // append any regular string characters we passed by already
+ if (position > begin) {
+ returnStringBuilder.append(inputStringChars, begin, position-begin);
+ }
+ char nextChar = inputStringChars[position+1];
+ switch (nextChar) {
+ case 'b':{
+ returnStringBuilder.append("\b");
+ position += 2;
+ break;
+ }
+ case 'r':{
+ returnStringBuilder.append("\r");
+ position += 2;
+ break;
+ }
+ case 'f':{
+ returnStringBuilder.append("\f");
+ position += 2;
+ break;
+ }
+ case 'n':{
+ returnStringBuilder.append("\n");
+ position += 2;
+ break;
+ }
+ case 't': {
+ returnStringBuilder.append("\t");
+ position += 2;
+ break;
+ }
+ case '"': {
+ returnStringBuilder.append("\"");
+ position += 2;
+ break;
+ }
+ case '\\':{
+ returnStringBuilder.append("\\");
+ position += 2;
+ break;
+ }
+ case '/':{
+ returnStringBuilder.append("/");
+ position += 2;
+ break;
+ }
+ case 'u':{ //unicode encoded character
+ String hexValue = String.valueOf(inputStringChars, position+2, 4);
+ returnStringBuilder.append(Character.toString((char)Integer.parseInt(hexValue, 16)));
+ position += 6;
+ break;
+ }
+ //default - not needed, only characters above are allowed for escaping in Java strings
+ }
+ begin = position;
+ } else if (inputStringChars[position] == '\r' && inputStringChars[position+1] == '\n') {
+ // append any regular string characters we passed by already
+ if (position > begin) {
+ returnStringBuilder.append(inputStringChars, begin, position-begin);
+ }
+ returnStringBuilder.append("\n");
+ position += 2;
+ begin = position;
+ } else {
+ position++;
+ }
}
- //If there is content after the last '\' then append it.
- if(position < string.length()){
- String subString = string.substring(position, string.length());
- returnString += subString;
+
+ // add any remaining characters
+ if (position > begin) {
+ returnStringBuilder.append(inputStringChars, begin, position-begin);
}
- return returnString;
+
+ return returnStringBuilder.toString();
}
/**
@@ -612,7 +632,7 @@ public class JSONReader extends XMLReaderAdapter {
}
private void addSimpleAttribute(List attributes, String uri, String attributeLocalName,Tree childValueTree){
- switch(childValueTree.getType()) {
+ switch(childValueTree.getType()) {
case JSONLexer.STRING: {
String stringValue = JSONReader.string(childValueTree.getChild(0).getText());
attributes.add(new Attribute(uri, attributeLocalName, attributeLocalName, stringValue));
@@ -633,9 +653,10 @@ public class JSONReader extends XMLReaderAdapter {
case JSONLexer.NULL: {
break;
}
- }
+ }
}
+ @Override
public int getIndex(String uri, String localName) {
if(null == localName) {
return -1;
@@ -728,8 +749,8 @@ public class JSONReader extends XMLReaderAdapter {
*/
private static class ExtendedJSONParser extends JSONParser {
- private InputSource inputSource;
- private ErrorHandler errorHandler;
+ private final InputSource inputSource;
+ private final ErrorHandler errorHandler;
public ExtendedJSONParser(TokenStream input, InputSource inputSource, ErrorHandler errorHandler) {
super(input);
@@ -755,6 +776,7 @@ public class JSONReader extends XMLReaderAdapter {
super(e);
}
+ @Override
public SAXException getCause() {
return (SAXException)super.getCause();
}
diff --git a/moxy/eclipselink.moxy.test/resource/org/eclipse/persistence/testing/jaxb/json/multiline/json.json b/moxy/eclipselink.moxy.test/resource/org/eclipse/persistence/testing/jaxb/json/multiline/json.json
new file mode 100644
index 0000000..5f130eb
--- /dev/null
+++ b/moxy/eclipselink.moxy.test/resource/org/eclipse/persistence/testing/jaxb/json/multiline/json.json
@@ -0,0 +1 @@
+{"multiBean":{"name":"abcrr\n\nd"}} \ No newline at end of file
diff --git a/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/JSONTestSuite.java b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/JSONTestSuite.java
index 4cbc47d..6d860d2 100644
--- a/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/JSONTestSuite.java
+++ b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/JSONTestSuite.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -43,6 +43,7 @@ import org.eclipse.persistence.testing.jaxb.json.rootlevellist.RootLevelListTest
import org.eclipse.persistence.testing.jaxb.json.wrapper.AllWrapperTestCases;
import org.eclipse.persistence.testing.jaxb.json.xmlvalue.XMLValuePropDifferentTestCases;
import org.eclipse.persistence.testing.jaxb.json.xmlvalue.XMLValuePropTestCases;
+import org.eclipse.persistence.testing.jaxb.json.multiline.MultiLineStringTestCases;
import org.eclipse.persistence.testing.oxm.xmlconversionmanager.NumberTestCases;
import junit.framework.Test;
@@ -83,6 +84,7 @@ public class JSONTestSuite extends TestSuite {
suite.addTest(JSONWithPaddingTestCases.suite());
suite.addTest(AnyTestCases.suite());
suite.addTest(AllWrapperTestCases.suite());
+ suite.addTestSuite(MultiLineStringTestCases.class);
return suite;
}
diff --git a/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiBean.java b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiBean.java
new file mode 100644
index 0000000..8ebaeae
--- /dev/null
+++ b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiBean.java
@@ -0,0 +1,36 @@
+/**
+ * *****************************************************************************
+ * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
+ * program and the accompanying materials are made available under the terms of
+ * the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 which
+ * accompanies this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
+ * License is available at http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors: Martin Grebac - 2.6
+ * ****************************************************************************
+ */
+package org.eclipse.persistence.testing.jaxb.json.multiline;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@SuppressWarnings("EqualsAndHashcode")
+public class MultiBean {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return ((obj instanceof MultiBean) &&
+ (this.getName().equals(((MultiBean)obj).getName())));
+ }
+}
diff --git a/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiLineStringTestCases.java b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiLineStringTestCases.java
new file mode 100644
index 0000000..5891670
--- /dev/null
+++ b/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/json/multiline/MultiLineStringTestCases.java
@@ -0,0 +1,33 @@
+/**
+ * *****************************************************************************
+ * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
+ * program and the accompanying materials are made available under the terms of
+ * the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 which
+ * accompanies this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
+ * License is available at http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors: Martin Grebac - 2.6
+ * ****************************************************************************
+ */
+package org.eclipse.persistence.testing.jaxb.json.multiline;
+
+import org.eclipse.persistence.testing.jaxb.json.JSONMarshalUnmarshalTestCases;
+
+public class MultiLineStringTestCases extends JSONMarshalUnmarshalTestCases {
+
+ private final static String JSON_RESOURCE = "org/eclipse/persistence/testing/jaxb/json/multiline/json.json";
+
+ public MultiLineStringTestCases(String name) throws Exception {
+ super(name);
+ setControlJSON(JSON_RESOURCE);
+ setClasses(new Class[]{MultiBean.class});
+ }
+
+ protected Object getControlObject() {
+ MultiBean tb = new MultiBean();
+ tb.setName("abcrr\n" + "\n" + "d");
+ return tb;
+ }
+
+}