You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/07/22 09:08:38 UTC

[sling-org-apache-sling-contentparser-api] 01/11: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-api.git

commit 0f5852061580d94f2482d40cdee7b1f0caa1f2f1
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Wed Jul 10 19:19:43 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * extracted API bundle
    * first attempt at implementing a separate JSON content parser
---
 pom.xml                                            |  61 ++++++++
 .../sling/contentparser/api/ContentHandler.java    |  41 +++++
 .../sling/contentparser/api/ContentParser.java     |  79 ++++++++++
 .../sling/contentparser/api/JsonParserFeature.java |  36 +++++
 .../sling/contentparser/api/ParseException.java    |  49 ++++++
 .../sling/contentparser/api/ParserHelper.java      | 104 +++++++++++++
 .../sling/contentparser/api/ParserOptions.java     | 171 +++++++++++++++++++++
 .../sling/contentparser/api/package-info.java      |  22 +++
 .../sling/contentparser/api/ParserHelperTest.java  |  57 +++++++
 9 files changed, 620 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..799b201
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.contentparser.api</artifactId>
+    <version>0.9.0-SNAPSHOT</version>
+
+    <name>Apache Sling Content Parser API</name>
+    <description>
+        Parser API Apache Sling Resource trees stored in files (e.g. JSON, FileVault XML, etc.).
+    </description>
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-api.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-api.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-contentparser-api.git</url>
+      <tag>HEAD</tag>
+  </scm>
+
+    <build>
+        <plugins>
+            
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/contentparser/api/ContentHandler.java b/src/main/java/org/apache/sling/contentparser/api/ContentHandler.java
new file mode 100644
index 0000000..34103d1
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/ContentHandler.java
@@ -0,0 +1,41 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import java.util.Map;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A {@code ContentHandler} gets notified while parsing content with a {@link ContentParser}. The resources are always reported in the
+ * order in which they are found in the parsed files. Parents are always reported before their children.
+ */
+@ConsumerType
+public interface ContentHandler {
+
+    /**
+     * Resource found in parsed content.
+     *
+     * @param path       the path of the found resource inside the content fragment; the root resource from the content fragment will
+     *                   have the {@code "/"} path
+     * @param properties the resource's properties
+     */
+    void resource(String path, Map<String, Object> properties);
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/ContentParser.java b/src/main/java/org/apache/sling/contentparser/api/ContentParser.java
new file mode 100644
index 0000000..1a684ca
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/ContentParser.java
@@ -0,0 +1,79 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * A {@code ContentParser} parses Sling resource trees from a file. Implementations have to be thread-safe. A consumer requiring a {@code
+ * ContentParser} reference should filter based on the {@link #SERVICE_PROPERTY_CONTENT_TYPE} in order to get a content type specific
+ * parser.
+ */
+@ProviderType
+public interface ContentParser {
+
+    /**
+     * JSON content descriptor file.
+     *
+     * @see <a href="https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#json-descriptor-files">JCR
+     * ContentLoader JSON descriptor files</a>
+     */
+    String JSON_CONTENT_TYPE = "json";
+
+    /**
+     * XML content descriptor file.
+     *
+     * @see <a href="https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#xml-descriptor-files">JCR
+     * ContentLoader XML descriptor files</a>
+     */
+    String XML_CONTENT_TYPE = "xml";
+
+    /**
+     * JCR XML content (FileVault XML),aAlso known as extended document view XML. Extends the regular document view as specified by JCR 2.0
+     * with specifics like multi-value and type information.
+     *
+     * @see <a href="https://docs.adobe.com/content/docs/en/spec/jcr/2.0/7_Export.html#7.3%20Document%20View">JCR 2.0, 7.3 Document View</a>
+     * @see <a href="http://jackrabbit.apache.org/filevault/">Jackrabbit FileVault</a>
+     */
+    String JCR_XML_CONTENT_TYPE = "jcr.xml";
+
+    /**
+     * OSGi service registration property indicating the content type this {@code ContentParser} supports.
+     *
+     * @see #JSON_CONTENT_TYPE
+     * @see #XML_CONTENT_TYPE
+     * @see #JCR_XML_CONTENT_TYPE
+     */
+    String SERVICE_PROPERTY_CONTENT_TYPE = "org.apache.sling.contentparser.content_type";
+
+    /**
+     * Parse content in a "stream-based" way. Each resource that is found in the content is reported to the {@link ContentHandler}.
+     *
+     * @param contentHandler content handler that accepts the parsed content
+     * @param inputStream    stream with serialized content
+     * @param parserOptions  parser options, providing different settings for handling the serialized content
+     * @throws IOException    when an I/O error occurs
+     * @throws ParseException when a parsing error occurs.
+     */
+    void parse(ContentHandler contentHandler, InputStream inputStream, ParserOptions parserOptions) throws IOException, ParseException;
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/JsonParserFeature.java b/src/main/java/org/apache/sling/contentparser/api/JsonParserFeature.java
new file mode 100644
index 0000000..58c3e2b
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/JsonParserFeature.java
@@ -0,0 +1,36 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+/**
+ * Feature flags for parsing JSON files.
+ */
+public enum JsonParserFeature {
+
+    /**
+     * Support comments (&#47;* ... *&#47;) in JSON files.
+     */
+    COMMENTS,
+
+    /**
+     * Support ticks (') additional to double quotes (") as quoting symbol for JSON names and strings.
+     */
+    QUOTE_TICK
+    
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/ParseException.java b/src/main/java/org/apache/sling/contentparser/api/ParseException.java
new file mode 100644
index 0000000..4a19711
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/ParseException.java
@@ -0,0 +1,49 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Defines a parsing exception encountered by a {@link ContentParser}.
+ */
+@ProviderType
+public final class ParseException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Builds a {@code ParseException}, providing a message.
+     *
+     * @param message the message
+     */
+    public ParseException(String message) {
+        super(message);
+    }
+
+    /**
+     * Builds a {@code ParseException}, providing a message and a cause.
+     *
+     * @param message the message
+     * @param cause   the cause
+     */
+    public ParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/ParserHelper.java b/src/main/java/org/apache/sling/contentparser/api/ParserHelper.java
new file mode 100644
index 0000000..7cb1196
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/ParserHelper.java
@@ -0,0 +1,104 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import java.lang.reflect.Array;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+@ConsumerType
+public final class ParserHelper {
+
+    public static final String ECMA_DATE_FORMAT = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z";
+    public static final String ISO_8601_MILLISECONDS_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSVV";
+    public static final Locale DATE_FORMAT_LOCALE = Locale.US;
+    public static final DateTimeFormatter ECMA_DATE_FORMATTER = DateTimeFormatter.ofPattern(ECMA_DATE_FORMAT, DATE_FORMAT_LOCALE);
+    public static final DateTimeFormatter ISO_8601_MILLISECONDS_DATE_FORMATTER =
+            DateTimeFormatter.ofPattern(ISO_8601_MILLISECONDS_DATE_FORMAT,
+                    DATE_FORMAT_LOCALE);
+
+    /**
+     * Attempts to parse a {@code string} using first the {@link #ISO_8601_MILLISECONDS_DATE_FORMAT} format and then the {@link
+     * #ECMA_DATE_FORMAT}.
+     *
+     * @param string the string to parse
+     * @return a {@link Calendar} containing the parsed date or {@code null}, if the parsing failed
+     */
+    public static Calendar parseDate(String string) {
+        Calendar calendar = Calendar.getInstance();
+        try {
+            final OffsetDateTime offsetDateTime = OffsetDateTime.parse(string, ISO_8601_MILLISECONDS_DATE_FORMATTER);
+            final Instant instant = offsetDateTime.toInstant();
+            calendar.setTime(Date.from(instant));
+            calendar.setTimeZone(TimeZone.getTimeZone(offsetDateTime.getOffset()));
+        } catch (DateTimeParseException e) {
+            try {
+                final OffsetDateTime offsetDateTime = OffsetDateTime.parse(string, ECMA_DATE_FORMATTER);
+                final Instant instant = offsetDateTime.toInstant();
+                calendar.setTime(Date.from(instant));
+                calendar.setTimeZone(TimeZone.getTimeZone(offsetDateTime.getOffset()));
+            } catch (DateTimeParseException ee) {
+                calendar = null;
+            }
+        }
+        return calendar;
+    }
+
+    /**
+     * Converts a multi-value property to a single object.
+     *
+     * @param values the multi-value property's values
+     * @return an object representation of the multi-value property
+     */
+    public static Object convertSingleTypeArray(Object[] values) {
+        if (values.length == 0) {
+            return values;
+        }
+        Class<?> itemType = null;
+        for (Object value : values) {
+            if (value == null) {
+                throw new ParseException("Multi-value array must not contain null values.");
+            }
+            if (value instanceof Map) {
+                throw new ParseException("Multi-value array must not contain maps/objects.");
+            }
+            if (itemType == null) {
+                itemType = value.getClass();
+            } else if (itemType != value.getClass()) {
+                throw new ParseException("Multivalue array must not contain values with different types "
+                        + "(" + itemType.getName() + ", " + value.getClass().getName() + ").");
+            }
+        }
+        Object convertedArray = Array.newInstance(itemType, values.length);
+        for (int i = 0; i < values.length; i++) {
+            Array.set(convertedArray, i, values[i]);
+        }
+        return convertedArray;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/ParserOptions.java b/src/main/java/org/apache/sling/contentparser/api/ParserOptions.java
new file mode 100644
index 0000000..ebf80f0
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/ParserOptions.java
@@ -0,0 +1,171 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Options for content parsers.
+ */
+@ProviderType
+public final class ParserOptions {
+
+    /**
+     * Default primary type.
+     */
+    public static final String DEFAULT_PRIMARY_TYPE = "nt:unstructured";
+
+    /**
+     * Default list of prefixes to remove from property names.
+     */
+    public static final Set<String> DEFAULT_REMOVE_PROPERTY_NAME_PREFIXES
+            = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            "jcr:reference:",
+            "jcr:path:",
+            "jcr:name:",
+            "jcr:uri:"
+    )));
+
+    /**
+     * Default list of resource names that should be ignored.
+     */
+    public static final Set<String> DEFAULT_IGNORE_RESOURCE_NAMES
+            = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            "security:acl",
+            "security:principals"
+    )));
+
+    /**
+     * List of JSON parser features activated by default.
+     */
+    public static final EnumSet<JsonParserFeature> DEFAULT_JSON_PARSER_FEATURES
+            = EnumSet.of(JsonParserFeature.COMMENTS);
+
+    private String defaultPrimaryType = DEFAULT_PRIMARY_TYPE;
+    private boolean detectCalendarValues;
+    private Set<String> ignorePropertyNames = Collections.emptySet();
+    private Set<String> ignoreResourceNames = DEFAULT_IGNORE_RESOURCE_NAMES;
+    private Set<String> removePropertyNamePrefixes = DEFAULT_REMOVE_PROPERTY_NAME_PREFIXES;
+    private EnumSet<JsonParserFeature> jsonParserFeatures = DEFAULT_JSON_PARSER_FEATURES;
+
+    /**
+     * Default "jcr:primaryType" property for resources that have no explicit value for this value.
+     * If set to null, not default type is applied.
+     *
+     * @param value Default primary type.
+     * @return this
+     */
+    public ParserOptions defaultPrimaryType(String value) {
+        this.defaultPrimaryType = value;
+        return this;
+    }
+
+    public String getDefaultPrimaryType() {
+        return defaultPrimaryType;
+    }
+
+    /**
+     * 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
+     * @return this
+     */
+    public ParserOptions detectCalendarValues(boolean value) {
+        this.detectCalendarValues = value;
+        return this;
+    }
+
+    public boolean isDetectCalendarValues() {
+        return detectCalendarValues;
+    }
+
+    /**
+     * Set a list of property names that should be ignored when parsing the content file.
+     *
+     * @param value List of property names
+     * @return this
+     */
+    public ParserOptions ignorePropertyNames(Set<String> value) {
+        this.ignorePropertyNames = value;
+        return this;
+    }
+
+    public Set<String> getIgnorePropertyNames() {
+        return ignorePropertyNames;
+    }
+
+    /**
+     * Set a list of resource/node names that should be ignored when parsing the content file.
+     *
+     * @param value List of resource/node names
+     * @return this
+     */
+    public ParserOptions ignoreResourceNames(Set<String> value) {
+        this.ignoreResourceNames = value;
+        return this;
+    }
+
+    public Set<String> getIgnoreResourceNames() {
+        return ignoreResourceNames;
+    }
+
+    /**
+     * Set a list of property name prefixes that should be removed automatically from the property name.
+     *
+     * @param value List of property name prefixes
+     * @return this
+     */
+    public ParserOptions removePropertyNamePrefixes(Set<String> value) {
+        this.removePropertyNamePrefixes = value;
+        return this;
+    }
+
+    public Set<String> getRemovePropertyNamePrefixes() {
+        return removePropertyNamePrefixes;
+    }
+
+    /**
+     * Set set of features the JSON parser should apply when parsing files.
+     *
+     * @param value JSON parser features
+     * @return this
+     */
+    public ParserOptions jsonParserFeatures(EnumSet<JsonParserFeature> value) {
+        this.jsonParserFeatures = value;
+        return this;
+    }
+
+    public ParserOptions jsonParserFeatures(JsonParserFeature... value) {
+        this.jsonParserFeatures = EnumSet.copyOf(Arrays.asList(value));
+        return this;
+    }
+
+    public EnumSet<JsonParserFeature> getJsonParserFeatures() {
+        return jsonParserFeatures;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/api/package-info.java b/src/main/java/org/apache/sling/contentparser/api/package-info.java
new file mode 100644
index 0000000..8997911
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/api/package-info.java
@@ -0,0 +1,22 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+@Version("1.0.0")
+package org.apache.sling.contentparser.api;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/contentparser/api/ParserHelperTest.java b/src/test/java/org/apache/sling/contentparser/api/ParserHelperTest.java
new file mode 100644
index 0000000..2b2da48
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/api/ParserHelperTest.java
@@ -0,0 +1,57 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.contentparser.api;
+
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNull;
+
+public class ParserHelperTest {
+
+    @Test
+    public void parseDate() {
+        Map<String, int[]> formats = new HashMap<>();
+        formats.put("Sun Oct 31 2010 21:48:04 GMT+0100", new int[]{31, 10, 2010, 21, 48, 4, 0, 1});
+        formats.put("Sun Oct 31 2010 21:48", null);
+        formats.put("2014-09-19T21:20:26.812+02:00", new int[]{19, 9, 2014, 21, 20, 26, 812, 2});
+        formats.put("2014-09-19T21:20:26.812", null);
+        for (Map.Entry<String, int[]> entry : formats.entrySet()) {
+            int[] dateAsInts = entry.getValue();
+            Calendar calendar = ParserHelper.parseDate(entry.getKey());
+            if (dateAsInts == null) {
+                assertNull("Expected a null return value for string " + entry.getKey(), calendar);
+            } else {
+                assertEquals(dateAsInts[0], calendar.get(Calendar.DAY_OF_MONTH));
+                assertEquals(dateAsInts[1], calendar.get(Calendar.MONTH) + 1);
+                assertEquals(dateAsInts[2], calendar.get(Calendar.YEAR));
+                assertEquals(dateAsInts[3], calendar.get(Calendar.HOUR_OF_DAY));
+                assertEquals(dateAsInts[4], calendar.get(Calendar.MINUTE));
+                assertEquals(dateAsInts[5], calendar.get(Calendar.SECOND));
+                assertEquals(dateAsInts[6], calendar.get(Calendar.MILLISECOND));
+                assertEquals(dateAsInts[7], calendar.getTimeZone().getRawOffset() / 3600 / 1000);
+
+            }
+        }
+    }
+}