You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/20 14:41:02 UTC
[sling-org-apache-sling-jcr-contentparser] 03/38: SLING-6592 switch
to Stream API for content parsing, remove content representation API
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-contentparser.git
commit 536aed4674ef83223fe8cbbb496b7498081ad75e
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Fri Mar 17 20:59:21 2017 +0000
SLING-6592 switch to Stream API for content parsing, remove content representation API
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1787499 13f79535-47bb-0310-9956-ffa450edef68
---
.../{ContentParser.java => ContentHandler.java} | 21 +++----
.../sling/jcr/contentparser/ContentParser.java | 9 ++-
.../jcr/contentparser/ContentParserFactory.java | 2 +-
.../sling/jcr/contentparser/ContentType.java | 2 +-
.../sling/jcr/contentparser/ParserOptions.java | 4 +-
.../contentparser/impl/JcrXmlContentParser.java | 67 ++++++++++++++-------
.../jcr/contentparser/impl/JsonContentParser.java | 44 ++++++++++----
.../sling/jcr/contentparser/impl/ParserHelper.java | 13 +++-
.../sling/jcr/contentparser/package-info.java | 2 +-
.../impl/JcrXmlContentParserTest.java | 55 ++++++++++-------
.../contentparser/impl/JsonContentParserTest.java | 61 ++++++++++++-------
.../sling/jcr/contentparser/impl/TestUtils.java | 27 ++-------
.../impl/mapsupport/ContentElement.java} | 44 +++++++-------
.../impl/mapsupport/ContentElementHandler.java | 69 ++++++++++++++++++++++
.../impl/mapsupport/ContentElementImpl.java | 68 +++++++++++++++++++++
15 files changed, 348 insertions(+), 140 deletions(-)
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java b/src/main/java/org/apache/sling/jcr/contentparser/ContentHandler.java
similarity index 62%
copy from src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java
copy to src/main/java/org/apache/sling/jcr/contentparser/ContentHandler.java
index 1d9595a..7582338 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/ContentHandler.java
@@ -18,23 +18,20 @@
*/
package org.apache.sling.jcr.contentparser;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.Map;
/**
- * Parses repository content from a file.
- * Implementations have to be thread-safe.
+ * Handler that gets notified while parsing content with {@link ContentParser}.
+ * The resources are always reported in order of their paths as found in the content fragment.
+ * Parents are always reported before their children.
*/
-public interface ContentParser {
+public interface ContentHandler {
/**
- * Parse content.
- * @param is Stream with serialized content
- * @return Content as Map
- * @throws IOException When I/O error occurs.
- * @throws ParseException When parsing error occurs.
+ * Resource found in parsed content.
+ * @param path Path of resource inside the content fragment. The root resource from the content fragment has a path "/".
+ * @param properties Resource properties
*/
- Map<String,Object> parse(InputStream is) throws IOException, ParseException;
-
+ void resource(String path, Map<String,Object> properties);
+
}
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java b/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java
index 1d9595a..fa6877d 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/ContentParser.java
@@ -20,7 +20,6 @@ package org.apache.sling.jcr.contentparser;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Map;
/**
* Parses repository content from a file.
@@ -29,12 +28,12 @@ import java.util.Map;
public interface ContentParser {
/**
- * Parse content.
- * @param is Stream with serialized content
- * @return Content as Map
+ * Parse content in a "stream-based" way. Each resource that is found in the content is reported to the contentHandler.
+ * @param contentHandler Content handler that accepts the parsed content.
+ * @param inputStream Stream with serialized content
* @throws IOException When I/O error occurs.
* @throws ParseException When parsing error occurs.
*/
- Map<String,Object> parse(InputStream is) throws IOException, ParseException;
+ void parse(ContentHandler contentHandler, InputStream inputStream) throws IOException, ParseException;
}
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ContentParserFactory.java b/src/main/java/org/apache/sling/jcr/contentparser/ContentParserFactory.java
index 7ae64e2..7357ec5 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ContentParserFactory.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/ContentParserFactory.java
@@ -22,7 +22,7 @@ import org.apache.sling.jcr.contentparser.impl.JcrXmlContentParser;
import org.apache.sling.jcr.contentparser.impl.JsonContentParser;
/**
- * Factory for content file parsers.
+ * Factory for content parsers.
*/
public final class ContentParserFactory {
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java b/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java
index 167722f..acba05d 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java
@@ -29,7 +29,7 @@ public enum ContentType {
JSON("json"),
/**
- * JCR XML content.
+ * JCR XML content (FileVault XML).
*/
JCR_XML("jcr.xml");
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ParserOptions.java b/src/main/java/org/apache/sling/jcr/contentparser/ParserOptions.java
index 0911dac..4dba088 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ParserOptions.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/ParserOptions.java
@@ -24,7 +24,7 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Options for content filer parser.
+ * Options for content parser.
*/
public final class ParserOptions {
@@ -60,7 +60,7 @@ public final class ParserOptions {
}
/**
- * Some content file formats like JSON do not contain information to identify date/time values.
+ * Some content formats like JSON do not contain information to identify date/time values.
* Instead they have to be detected by heuristics by trying to parse every string value.
* This mode is disabled by default.
* @param value Activate calendar value detection
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParser.java b/src/main/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParser.java
index 2af30e7..9b9486c 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParser.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParser.java
@@ -20,16 +20,20 @@ package org.apache.sling.jcr.contentparser.impl;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.HashSet;
import java.util.Map;
-import java.util.Stack;
+import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
+import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.util.ISO9075;
+import org.apache.sling.jcr.contentparser.ContentHandler;
import org.apache.sling.jcr.contentparser.ContentParser;
import org.apache.sling.jcr.contentparser.ParseException;
import org.apache.sling.jcr.contentparser.ParserOptions;
@@ -54,15 +58,14 @@ public final class JcrXmlContentParser implements ContentParser {
}
@Override
- public Map<String,Object> parse(InputStream is) throws IOException, ParseException {
+ public void parse(ContentHandler handler, InputStream is) throws IOException, ParseException {
try {
- XmlHandler xmlHandler = new XmlHandler();
+ XmlHandler xmlHandler = new XmlHandler(handler);
SAXParser parser = saxParserFactory.newSAXParser();
parser.parse(is, xmlHandler);
if (xmlHandler.hasError()) {
throw xmlHandler.getError();
}
- return xmlHandler.getContent();
}
catch (ParserConfigurationException | SAXException ex) {
throw new ParseException("Error parsing JCR XML content.", ex);
@@ -82,12 +85,13 @@ public final class JcrXmlContentParser implements ContentParser {
* Parses XML stream to Map.
*/
class XmlHandler extends DefaultHandler {
- private final Map<String,Object> content = new LinkedHashMap<>();
- private final Stack<Map<String,Object>> elements = new Stack<>();
+ private final ContentHandler contentHandler;
+ private final Deque<String> paths = new ArrayDeque<>();
+ private final Set<String> ignoredPaths = new HashSet<>();
private SAXParseException error;
- public Map<String,Object> getContent() {
- return content;
+ public XmlHandler(ContentHandler contentHandler) {
+ this.contentHandler = contentHandler;
}
public boolean hasError() {
@@ -102,36 +106,55 @@ public final class JcrXmlContentParser implements ContentParser {
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
- // prepare map for element
- Map<String,Object> element;
- if (elements.isEmpty()) {
- element = content;
+ String resourceName = decodeName(qName);
+
+ // generate path for element
+ String path;
+ if (paths.isEmpty()) {
+ path = "/";
}
else {
- element = new HashMap<>();
- String resourceName = decodeName(qName);
- if (!helper.ignoreResource(resourceName)) {
- elements.peek().put(resourceName, element);
+ path = helper.concatenatePath(paths.peek(), resourceName);
+ if (helper.ignoreResource(resourceName)) {
+ ignoredPaths.add(path);
}
}
- elements.push(element);
+ paths.push(path);
+
+ // skip further processing if this path or a parent path is ignored
+ if (isIgnoredPath(path)) {
+ return;
+ }
- // get attributes
+ // get properties
+ Map<String,Object> properties = new HashMap<>();
for (int i=0; i<attributes.getLength(); i++) {
String propertyName = helper.cleanupPropertyName(decodeName(attributes.getQName(i)));
if (!helper.ignoreProperty(propertyName)) {
Object value = JcrXmlValueConverter.parseValue(propertyName, attributes.getValue(i));
if (value != null) {
- element.put(propertyName, value);
+ properties.put(propertyName, value);
}
}
}
+ helper.ensureDefaultPrimaryType(properties);
+ contentHandler.resource(path, properties);
+ }
+
+ private boolean isIgnoredPath(String path) {
+ if (StringUtils.isEmpty(path)) {
+ return false;
+ }
+ if (ignoredPaths.contains(path)) {
+ return true;
+ }
+ String parentPath = StringUtils.substringBeforeLast(path, "/");
+ return isIgnoredPath(parentPath);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
- Map<String,Object> element = elements.pop();
- helper.ensureDefaultPrimaryType(element);
+ paths.pop();
}
@Override
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/impl/JsonContentParser.java b/src/main/java/org/apache/sling/jcr/contentparser/impl/JsonContentParser.java
index a17e91e..093fbec 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/impl/JsonContentParser.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/impl/JsonContentParser.java
@@ -35,6 +35,7 @@ import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.stream.JsonParsingException;
+import org.apache.sling.jcr.contentparser.ContentHandler;
import org.apache.sling.jcr.contentparser.ContentParser;
import org.apache.sling.jcr.contentparser.ParseException;
import org.apache.sling.jcr.contentparser.ParserOptions;
@@ -45,7 +46,12 @@ import org.apache.sling.jcr.contentparser.ParserOptions;
*/
public final class JsonContentParser implements ContentParser {
- private final ParserHelper helper;
+ private final ParserHelper helper;
+ /*
+ * Implementation note: This parser uses JsonReader instead of the (more memory-efficient)
+ * JsonParser Stream API because otherwise it would not be possible to report parent resources
+ * including all properties properly before their children.
+ */
private final JsonReaderFactory jsonReaderFactory;
public JsonContentParser(ParserOptions options) {
@@ -57,22 +63,25 @@ public final class JsonContentParser implements ContentParser {
}
@Override
- public Map<String,Object> parse(InputStream is) throws IOException, ParseException {
+ public void parse(ContentHandler handler, InputStream is) throws IOException, ParseException {
try (JsonReader reader = jsonReaderFactory.createReader(is)) {
- return toMap(reader.readObject());
+ parse(handler, reader.readObject(), "/");
}
catch (JsonParsingException ex) {
throw new ParseException("Error parsing JSON content.", ex);
}
}
-
- private Map<String,Object> toMap(JsonObject object) {
- Map<String,Object> map = new LinkedHashMap<>();
+
+ private void parse(ContentHandler handler, JsonObject object, String path) {
+ // parse JSON object
+ Map<String,Object> properties = new HashMap<>();
+ Map<String,JsonObject> children = new LinkedHashMap<>();
for (Map.Entry<String, JsonValue> entry : object.entrySet()) {
String childName = entry.getKey();
Object value = convertValue(entry.getValue());
+ boolean isResource = (value instanceof JsonObject);
boolean ignore = false;
- if (value instanceof Map) {
+ if (isResource) {
ignore = helper.ignoreResource(childName);
}
else {
@@ -80,11 +89,24 @@ public final class JsonContentParser implements ContentParser {
ignore = helper.ignoreProperty(childName);
}
if (!ignore) {
- map.put(childName, value);
+ if (isResource) {
+ children.put(childName, (JsonObject)value);
+ }
+ else {
+ properties.put(childName, value);
+ }
}
}
- helper.ensureDefaultPrimaryType(map);
- return map;
+ helper.ensureDefaultPrimaryType(properties);
+
+ // report current JSON object
+ handler.resource(path, properties);
+
+ // parse and report children
+ for (Map.Entry<String,JsonObject> entry : children.entrySet()) {
+ String childPath = helper.concatenatePath(path, entry.getKey());;
+ parse(handler, entry.getValue(), childPath);
+ }
}
private Object convertValue(JsonValue value) {
@@ -120,7 +142,7 @@ public final class JsonContentParser implements ContentParser {
}
return helper.convertSingleTypeArray(values);
case OBJECT:
- return toMap((JsonObject)value);
+ return (JsonObject)value;
default:
throw new ParseException("Unexpected JSON value type: " + value.getValueType());
}
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/impl/ParserHelper.java b/src/main/java/org/apache/sling/jcr/contentparser/impl/ParserHelper.java
index 160cd0e..69b0f48 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/impl/ParserHelper.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/impl/ParserHelper.java
@@ -27,6 +27,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import javax.json.JsonObject;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.jcr.contentparser.ParseException;
import org.apache.sling.jcr.contentparser.ParserOptions;
@@ -113,7 +115,7 @@ class ParserHelper {
if (value == null) {
throw new ParseException("Multivalue array must not contain null values.");
}
- if (value instanceof Map) {
+ if (value instanceof Map || value instanceof JsonObject) {
throw new ParseException("Multivalue array must not contain maps/objects.");
}
if (itemType == null) {
@@ -131,4 +133,13 @@ class ParserHelper {
return convertedArray;
}
+ public String concatenatePath(String parentPath, String name) {
+ if (StringUtils.endsWith(parentPath, "/")) {
+ return parentPath + name;
+ }
+ else {
+ return parentPath + "/" + name;
+ }
+ }
+
}
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/package-info.java b/src/main/java/org/apache/sling/jcr/contentparser/package-info.java
index 21b7f41..1b6b2a8 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/package-info.java
+++ b/src/main/java/org/apache/sling/jcr/contentparser/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
/**
- * Parser for repository content stored in files (e.g. JSON, JCR XML).
+ * Parser for repository content serialized e.g. as JSON or JCR XML.
*/
@org.osgi.annotation.versioning.Version("1.0.0")
package org.apache.sling.jcr.contentparser;
diff --git a/src/test/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParserTest.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParserTest.java
index 1faaf4e..99eca51 100644
--- a/src/test/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParserTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/JcrXmlContentParserTest.java
@@ -18,7 +18,6 @@
*/
package org.apache.sling.jcr.contentparser.impl;
-import static org.apache.sling.jcr.contentparser.impl.TestUtils.getDeep;
import static org.apache.sling.jcr.contentparser.impl.TestUtils.parse;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -37,8 +36,8 @@ import org.apache.sling.jcr.contentparser.ContentParserFactory;
import org.apache.sling.jcr.contentparser.ContentType;
import org.apache.sling.jcr.contentparser.ParseException;
import org.apache.sling.jcr.contentparser.ParserOptions;
+import org.apache.sling.jcr.contentparser.impl.mapsupport.ContentElement;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import com.google.common.collect.ImmutableSet;
@@ -52,14 +51,13 @@ public class JcrXmlContentParserTest {
file = new File("src/test/resources/content-test/content.jcr.xml");
}
- @SuppressWarnings("unchecked")
@Test
public void testParseJcrXml() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JCR_XML);
- Map<String,Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
assertNotNull(content);
- assertEquals("app:Page", content.get("jcr:primaryType"));
- assertEquals("app:PageContent", ((Map<String,Object>)content.get("jcr:content")).get("jcr:primaryType"));
+ assertEquals("app:Page", content.getProperties().get("jcr:primaryType"));
+ assertEquals("app:PageContent", content.getChild("jcr:content").getProperties().get("jcr:primaryType"));
}
@Test(expected=ParseException.class)
@@ -72,8 +70,8 @@ public class JcrXmlContentParserTest {
@Test
public void testDataTypes() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JCR_XML);
- Map<String,Object> content = parse(underTest, file);
- Map<String,Object> props = getDeep(content, "jcr:content");
+ ContentElement content = parse(underTest, file);
+ Map<String,Object> props = content.getChild("jcr:content").getProperties();
assertEquals("en", props.get("jcr:title"));
assertEquals(true, props.get("includeAside"));
@@ -105,26 +103,43 @@ public class JcrXmlContentParserTest {
ContentParser underTest = ContentParserFactory.create(ContentType.JCR_XML, new ParserOptions()
.ignoreResourceNames(ImmutableSet.of("teaserbar", "aside"))
.ignorePropertyNames(ImmutableSet.of("longProp", "jcr:title")));
- Map<String,Object> content = parse(underTest, file);
- Map<String,Object> props = getDeep(content, "jcr:content");
+ ContentElement content = parse(underTest, file);
+ ContentElement child = content.getChild("jcr:content");
- assertEquals("HOME", props.get("navTitle"));
- assertNull(props.get("jcr:title"));
- assertNull(props.get("longProp"));
+ assertEquals("HOME", child.getProperties().get("navTitle"));
+ assertNull(child.getProperties().get("jcr:title"));
+ assertNull(child.getProperties().get("longProp"));
- assertNull(props.get("teaserbar"));
- assertNull(props.get("aside"));
- assertNotNull(props.get("content"));
+ assertNull(child.getChildren().get("teaserbar"));
+ assertNull(child.getChildren().get("aside"));
+ assertNotNull(child.getChildren().get("content"));
+ }
+
+ @Test
+ public void testGetChild() throws Exception {
+ ContentParser underTest = ContentParserFactory.create(ContentType.JCR_XML);
+ ContentElement content = parse(underTest, file);
+ assertNull(content.getName());
+
+ ContentElement deepChild = content.getChild("jcr:content/teaserbar/teaserbaritem");
+ assertEquals("teaserbaritem", deepChild.getName());
+ assertEquals("samples/sample-app/components/content/teaserbar/teaserbarItem", deepChild.getProperties().get("sling:resourceType"));
+
+ ContentElement invalidChild = content.getChild("non/existing/path");
+ assertNull(invalidChild);
+
+ invalidChild = content.getChild("/jcr:content");
+ assertNull(invalidChild);
}
@Test
- @Ignore
public void testSameNamePropertyAndSubResource() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JCR_XML);
- Map<String,Object> content = parse(underTest, file);
- Map<String,Object> props = getDeep(content, "jcr:content/teaserbar");
+ ContentElement content = parse(underTest, file);
+ ContentElement child = content.getChild("jcr:content/teaserbar");
// teaserbaritem is a direct property as well as a sub resource
- assertEquals("test", props.get("teaserbaritem"));
+ assertEquals("test", child.getProperties().get("teaserbaritem"));
+ assertNotNull(child.getChildren().get("teaserbaritem"));
}
}
diff --git a/src/test/java/org/apache/sling/jcr/contentparser/impl/JsonContentParserTest.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/JsonContentParserTest.java
index 3aa9531..706cb6e 100644
--- a/src/test/java/org/apache/sling/jcr/contentparser/impl/JsonContentParserTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/JsonContentParserTest.java
@@ -18,7 +18,6 @@
*/
package org.apache.sling.jcr.contentparser.impl;
-import static org.apache.sling.jcr.contentparser.impl.TestUtils.getDeep;
import static org.apache.sling.jcr.contentparser.impl.TestUtils.parse;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -36,6 +35,7 @@ import org.apache.sling.jcr.contentparser.ContentParserFactory;
import org.apache.sling.jcr.contentparser.ContentType;
import org.apache.sling.jcr.contentparser.ParseException;
import org.apache.sling.jcr.contentparser.ParserOptions;
+import org.apache.sling.jcr.contentparser.impl.mapsupport.ContentElement;
import org.junit.Before;
import org.junit.Test;
@@ -53,17 +53,17 @@ public class JsonContentParserTest {
@Test
public void testPageJcrPrimaryType() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
- assertEquals("app:Page", content.get("jcr:primaryType"));
+ assertEquals("app:Page", content.getProperties().get("jcr:primaryType"));
}
@Test
public void testDataTypes() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
- Map<String, Object> props = getDeep(content, "toolbar/profiles/jcr:content");
+ Map<String, Object> props = content.getChild("toolbar/profiles/jcr:content").getProperties();
assertEquals(true, props.get("hideInNav"));
assertEquals(1234567890123L, props.get("longProp"));
@@ -78,9 +78,9 @@ public class JsonContentParserTest {
@Test
public void testContentProperties() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
- Map<String, Object> props = getDeep(content, "jcr:content/header");
+ Map<String, Object> props = content.getChild("jcr:content/header").getProperties();
assertEquals("/content/dam/sample/header.png", props.get("imageReference"));
}
@@ -88,9 +88,9 @@ public class JsonContentParserTest {
public void testCalendar() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON,
new ParserOptions().detectCalendarValues(true));
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
- Map<String, Object> props = getDeep(content, "jcr:content");
+ Map<String, Object> props = content.getChild("jcr:content").getProperties();
Calendar calendar = (Calendar) props.get("app:lastModified");
assertNotNull(calendar);
@@ -109,9 +109,9 @@ public class JsonContentParserTest {
@Test
public void testUTF8Chars() throws Exception {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
- Map<String, Object> props = getDeep(content, "jcr:content");
+ Map<String, Object> props = content.getChild("jcr:content").getProperties();
assertEquals("äöü߀", props.get("utf8Property"));
}
@@ -120,7 +120,7 @@ public class JsonContentParserTest {
public void testParseInvalidJson() throws Exception {
file = new File("src/test/resources/invalid-test/invalid.json");
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
assertNull(content);
}
@@ -128,7 +128,7 @@ public class JsonContentParserTest {
public void testParseInvalidJsonWithObjectList() throws Exception {
file = new File("src/test/resources/invalid-test/contentWithObjectList.json");
ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
- Map<String, Object> content = parse(underTest, file);
+ ContentElement content = parse(underTest, file);
assertNull(content);
}
@@ -137,18 +137,35 @@ public class JsonContentParserTest {
ContentParser underTest = ContentParserFactory.create(ContentType.JSON,
new ParserOptions().ignoreResourceNames(ImmutableSet.of("header", "newslist"))
.ignorePropertyNames(ImmutableSet.of("jcr:title")));
- Map<String, Object> content = parse(underTest, file);
- Map<String, Object> props = getDeep(content, "jcr:content");
+ ContentElement content = parse(underTest, file);
+ ContentElement child = content.getChild("jcr:content");
- assertEquals("Sample Homepage", props.get("pageTitle"));
- assertNull(props.get("jcr:title"));
+ assertEquals("Sample Homepage", child.getProperties().get("pageTitle"));
+ assertNull(child.getProperties().get("jcr:title"));
- assertNull(props.get("header"));
- assertNull(props.get("newslist"));
- assertNotNull(props.get("lead"));
+ assertNull(child.getChildren().get("header"));
+ assertNull(child.getChildren().get("newslist"));
+ assertNotNull(child.getChildren().get("lead"));
- assertEquals("abc", props.get("refpro1"));
- assertEquals("def", props.get("pathprop1"));
+ assertEquals("abc", child.getProperties().get("refpro1"));
+ assertEquals("def", child.getProperties().get("pathprop1"));
+ }
+
+ @Test
+ public void testGetChild() throws Exception {
+ ContentParser underTest = ContentParserFactory.create(ContentType.JSON);
+ ContentElement content = parse(underTest, file);
+ assertNull(content.getName());
+
+ ContentElement deepChild = content.getChild("jcr:content/par/image/file/jcr:content");
+ assertEquals("jcr:content", deepChild.getName());
+ assertEquals("nt:resource", deepChild.getProperties().get("jcr:primaryType"));
+
+ ContentElement invalidChild = content.getChild("non/existing/path");
+ assertNull(invalidChild);
+
+ invalidChild = content.getChild("/jcr:content");
+ assertNull(invalidChild);
}
}
diff --git a/src/test/java/org/apache/sling/jcr/contentparser/impl/TestUtils.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/TestUtils.java
index f91c5ee..be395b9 100644
--- a/src/test/java/org/apache/sling/jcr/contentparser/impl/TestUtils.java
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/TestUtils.java
@@ -22,10 +22,10 @@ import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
import org.apache.sling.jcr.contentparser.ContentParser;
+import org.apache.sling.jcr.contentparser.impl.mapsupport.ContentElement;
+import org.apache.sling.jcr.contentparser.impl.mapsupport.ContentElementHandler;
public final class TestUtils {
@@ -33,27 +33,12 @@ public final class TestUtils {
// static methods only
}
- @SuppressWarnings("unchecked")
- public static Map<String, Object> getDeep(Map<String, Object> map, String path) {
- String name = StringUtils.substringBefore(path, "/");
- Object object = map.get(name);
- if (object == null || !(object instanceof Map)) {
- return null;
- }
- String remainingPath = StringUtils.substringAfter(path, "/");
- Map<String, Object> childMap = (Map<String, Object>)object;
- if (StringUtils.isEmpty(remainingPath)) {
- return childMap;
- }
- else {
- return getDeep(childMap, remainingPath);
- }
- }
-
- public static Map<String,Object> parse(ContentParser contentParser, File file) throws IOException {
+ public static ContentElement parse(ContentParser contentParser, File file) throws IOException {
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis)) {
- return contentParser.parse(bis);
+ ContentElementHandler handler = new ContentElementHandler();
+ contentParser.parse(handler, bis);
+ return handler.getRoot();
}
}
diff --git a/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElement.java
similarity index 50%
copy from src/main/java/org/apache/sling/jcr/contentparser/ContentType.java
copy to src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElement.java
index 167722f..6584487 100644
--- a/src/main/java/org/apache/sling/jcr/contentparser/ContentType.java
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElement.java
@@ -16,35 +16,37 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sling.jcr.contentparser;
+package org.apache.sling.jcr.contentparser.impl.mapsupport;
+
+import java.util.Map;
/**
- * Content types.
+ * Represents a resource or node in the content hierarchy.
*/
-public enum ContentType {
+public interface ContentElement {
/**
- * JSON content.
+ * @return Resource name. The root resource has no name (null).
*/
- JSON("json"),
-
+ String getName();
+
/**
- * JCR XML content.
+ * Properties of this resource.
+ * @return Properties (keys, values)
*/
- JCR_XML("jcr.xml");
-
-
- private final String extension;
-
- private ContentType(String extension) {
- this.extension = extension;
- }
-
+ Map<String, Object> getProperties();
+
/**
- * @return Extension
+ * Get children of current resource. The Map preserves the ordering of children.
+ * @return Children (child names, child objects)
*/
- public String getExtension() {
- return extension;
- }
-
+ Map<String, ContentElement> getChildren();
+
+ /**
+ * Get child or descendant
+ * @param path Relative path to address child or one of it's descendants (use "/" as hierarchy separator).
+ * @return Child or null if no child found with this path
+ */
+ ContentElement getChild(String path);
+
}
diff --git a/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementHandler.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementHandler.java
new file mode 100644
index 0000000..190adad
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.sling.jcr.contentparser.impl.mapsupport;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.jcr.contentparser.ContentHandler;
+
+/**
+ * {@link ContentHandler} implementation that produces a tree of {@link ContentElement} items.
+ */
+public class ContentElementHandler implements ContentHandler {
+
+ private ContentElement root;
+ private Pattern PATH_PATTERN = Pattern.compile("^((/[^/]+)*)(/([^/]+))$");
+
+ @Override
+ public void resource(String path, Map<String, Object> properties) {
+ if (StringUtils.equals(path, "/")) {
+ root = new ContentElementImpl(null, properties);
+ }
+ else {
+ if (root == null) {
+ throw new RuntimeException("Root resource not set.");
+ }
+ Matcher matcher = PATH_PATTERN.matcher(path);
+ if (!matcher.matches()) {
+ throw new RuntimeException("Unexpected path:" + path);
+ }
+ String relativeParentPath = StringUtils.stripStart(matcher.group(1), "/");
+ String name = matcher.group(4);
+ ContentElement parent;
+ if (StringUtils.isEmpty(relativeParentPath)) {
+ parent = root;
+ }
+ else {
+ parent = root.getChild(relativeParentPath);
+ }
+ if (parent == null) {
+ throw new RuntimeException("Parent '" + relativeParentPath + "' does not exist.");
+ }
+ parent.getChildren().put(name, new ContentElementImpl(name, properties));
+ }
+ }
+
+ public ContentElement getRoot() {
+ return root;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementImpl.java b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementImpl.java
new file mode 100644
index 0000000..3956c98
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/contentparser/impl/mapsupport/ContentElementImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.sling.jcr.contentparser.impl.mapsupport;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+final class ContentElementImpl implements ContentElement {
+
+ private final String name;
+ private final Map<String, Object> properties;
+ private final Map<String, ContentElement> children = new LinkedHashMap<>();
+
+ public ContentElementImpl(String name, Map<String, Object> properties) {
+ this.name = name;
+ this.properties = properties;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public Map<String, ContentElement> getChildren() {
+ return children;
+ }
+
+ @Override
+ public ContentElement getChild(String path) {
+ String name = StringUtils.substringBefore(path, "/");
+ ContentElement child = children.get(name);
+ if (child == null) {
+ return null;
+ }
+ String remainingPath = StringUtils.substringAfter(path, "/");
+ if (StringUtils.isEmpty(remainingPath)) {
+ return child;
+ }
+ else {
+ return child.getChild(remainingPath);
+ }
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.