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

rombert pushed a commit to annotated tag org.apache.sling.resourcebuilder-1.0.0
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourcebuilder.git

commit ae44b20a796f96f0846e44042795495d31ae1d66
Author: Bertrand Delacretaz <bdelacre...@apache.org>
AuthorDate: Wed Dec 9 16:58:52 2015 +0000

    SLING-5356 - ResourceBuilder, work in progress
    
    git-svn-id: 
https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/resourcebuilder@1718908
 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  85 +++++++++
 .../sling/resourcebuilder/api/ResourceBuilder.java |  66 +++++++
 .../api/ResourceBuilderProvider.java               |  30 ++++
 .../resourcebuilder/impl/MapArgsConverter.java     |  43 +++++
 .../resourcebuilder/impl/ResourceBuilderImpl.java  | 143 +++++++++++++++
 .../impl/ResourceBuilderImplTest.java              | 199 +++++++++++++++++++++
 6 files changed, 566 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b51e328
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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</artifactId>
+        <version>25</version>
+        <relativePath/>
+    </parent>
+
+    <artifactId>org.apache.sling.commons.resourcebuilder</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Commons Content Builder</name>
+    <description>Utilities to create Sling content</description>
+
+    <scm>
+        
<connection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/resourcebuilder</connection>
+        
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/resourcebuilder</developerConnection>
+        
<url>https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/resourcebuilder</url>
+    </scm>
+    
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.3.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.sling-mock</artifactId>
+            <version>1.6.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git 
a/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java 
b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java
new file mode 100644
index 0000000..7d43ec3
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * 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.resourcebuilder.api;
+
+import org.apache.sling.api.resource.Resource;
+
+/** Builds Sling Resources using a simple fluent API */
+public interface ResourceBuilder {
+    
+    public static final String DEFAULT_PRIMARY_TYPE = "nt:unstructured";
+    
+    /** Create a Resource, which optionally becomes the current 
+     *  parent Resource. 
+     * @param relativePath The path of the Resource to create, relative to 
+     *          this builder's current parent Resource.
+     * @param properties optional name-value pairs 
+     * @return this builder
+     */
+    ResourceBuilder resource(String relativePath, Object ... properties);
+    
+    /** Commit created resources */
+    ResourceBuilder commit();
+    
+    /** Set the primary type for intermediate resources created
+     *  when the parent of resource being created does not exist.
+     * @param primaryType If null the DEFAULT_PRIMARY_TYPE is used.
+     * @return this builder
+     */
+    ResourceBuilder withIntermediatePrimaryType(String primaryType);
+    
+    /** Set siblings mode (as opposed to hierarchy mode) where creating a 
resource 
+     *  doesn't change the current parent. Used to create flat structures.
+     *  This is off by default.
+     * @return this builder
+     */
+    ResourceBuilder siblingsMode();
+    
+    /** Set hierarchy mode (as opposed to siblings mode) where creating a 
resource 
+     *  sets it as the current parent. Used to create tree structures.
+     *  This is on by default.
+     * @return this builder
+     */
+    ResourceBuilder hierarchyMode();
+    
+    /** Return the current parent resource */
+    Resource getCurrentParent();
+    
+    /** Reset the current parent Resource to the original one */ 
+    ResourceBuilder resetParent();
+}
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilderProvider.java
 
b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilderProvider.java
new file mode 100644
index 0000000..379547e
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilderProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.resourcebuilder.api;
+
+import org.apache.sling.api.resource.Resource;
+
+/** A service that provides ResourceBuilders */
+public interface ResourceBuilderProvider {
+    
+    /** Provides a ResourceBuilder to create resources 
+     *  under the supplied parent Resource. 
+     */
+    ResourceBuilder getResourceBuilder(Resource parent);
+}
diff --git 
a/src/main/java/org/apache/sling/resourcebuilder/impl/MapArgsConverter.java 
b/src/main/java/org/apache/sling/resourcebuilder/impl/MapArgsConverter.java
new file mode 100644
index 0000000..433dfb2
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcebuilder/impl/MapArgsConverter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.resourcebuilder.impl;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Convert arguments which are a list of Object to a Map, used
+ *  to simplify our builder's syntax.
+ */
+public class MapArgsConverter {
+    
+    /** Convert an args list to a Map */
+    public static Map<String, Object> toMap(Object ... args) {
+        if(args.length % 2 != 0) {
+            throw new IllegalArgumentException("args must be an even number of 
name/values:" + Arrays.asList(args));
+        }
+        final Map<String, Object> result = new HashMap<String, Object>();
+        for(int i=0 ; i < args.length; i+=2) {
+            result.put(args[i].toString(), args[i+1]);
+        }
+        return Collections.unmodifiableMap(result);
+    }
+
+}
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java 
b/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java
new file mode 100644
index 0000000..4e8899a
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.resourcebuilder.impl;
+
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.resourcebuilder.api.ResourceBuilder;
+
+/** ResourceBuilder implementation */
+public class ResourceBuilderImpl implements ResourceBuilder {
+    private final Resource originalParent;
+    private final ResourceResolver resourceResolver;
+    private Resource currentParent;
+    private String intermediatePrimaryType;
+    private boolean hierarchyMode;
+    
+    public static final String JCR_PRIMARYTYPE = "jcr:primaryType";
+    
+    public ResourceBuilderImpl(Resource parent) {
+        if(parent == null) {
+            throw new IllegalArgumentException("Parent resource is null");
+        }
+        originalParent = parent;
+        resourceResolver = originalParent.getResourceResolver();
+        withIntermediatePrimaryType(null);
+        resetParent();
+        hierarchyMode();
+    }
+
+    @Override
+    public Resource getCurrentParent() {
+        return currentParent;
+    }
+
+    @Override
+    public ResourceBuilder resetParent() {
+        currentParent = originalParent;
+        return this;
+    }
+
+    @Override
+    public ResourceBuilder resource(String relativePath, Object... properties) 
{
+        Resource created = null;
+        if(relativePath.startsWith("/")) {
+            throw new IllegalArgumentException("Path is not relative:" + 
relativePath);
+        }
+        
+        final String fullPath = currentParent.getPath() + "/" + relativePath;
+        final String parentPath = ResourceUtil.getParent(fullPath);
+        final Resource myParent = ensureResourceExists(parentPath);
+        
+        try {
+            created = currentParent.getResourceResolver().create(myParent, 
+                    ResourceUtil.getName(relativePath), 
MapArgsConverter.toMap(properties));
+        } catch(PersistenceException pex) {
+            throw new RuntimeException(
+                    "PersistenceException while creating Resource " + 
relativePath 
+                    + " under " + currentParent.getPath(), pex);
+        }
+        
+        if(created == null) {
+            throw new RuntimeException("Failed to created resource " + 
relativePath 
+                    + " under " + currentParent.getPath());
+        } else if(hierarchyMode) {
+            currentParent = created;
+        }
+        return this;
+    }
+    
+    /** Create a Resource at the specified path if none exists yet,
+     *  using the current intermediate primary type. "Stolen" from
+     *  the sling-mock module's ContentBuilder class.
+     *  @param path Resource path
+     *  @return Resource at path (existing or newly created)
+     */
+    protected final Resource ensureResourceExists(String path) {
+        if(path == null || path.length() == 0 || path.equals("/")) {
+            return resourceResolver.getResource("/");
+        }
+        Resource resource = resourceResolver.getResource(path);
+        if (resource != null) {
+            return resource;
+        }
+        String parentPath = ResourceUtil.getParent(path);
+        String name = ResourceUtil.getName(path);
+        Resource parentResource = ensureResourceExists(parentPath);
+        try {
+            resource = resourceResolver.create(
+                    parentResource, 
+                    name, 
+                    MapArgsConverter.toMap(JCR_PRIMARYTYPE, 
intermediatePrimaryType));
+            return resource;
+        } catch (PersistenceException ex) {
+            throw new RuntimeException("Unable to create intermediate resource 
at " + path, ex);
+        }
+    }
+
+    @Override
+    public ResourceBuilder withIntermediatePrimaryType(String primaryType) {
+        intermediatePrimaryType = primaryType == null ? DEFAULT_PRIMARY_TYPE : 
primaryType;
+        return this;
+    }
+
+    @Override
+    public ResourceBuilder siblingsMode() {
+        hierarchyMode = false;
+        return this;
+    }
+
+    @Override
+    public ResourceBuilder hierarchyMode() {
+        hierarchyMode = true;
+        return this;
+    }
+    
+    @Override
+    public ResourceBuilder commit() {
+        try {
+            resourceResolver.commit();
+        } catch (PersistenceException ex) {
+            throw new RuntimeException("Unable to commit", ex);
+        }
+        return this;
+    }
+}
\ No newline at end of file
diff --git 
a/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java
 
b/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java
new file mode 100644
index 0000000..a1f6c70
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.resourcebuilder.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ResourceBuilderImplTest {
+    
+    private String testRootPath;
+    private ResourceResolver resourceResolver;
+    
+    @Rule
+    public SlingContext context = new 
SlingContext(ResourceResolverType.RESOURCERESOLVER_MOCK);
+    
+    private ResourceBuilderImpl getBuilder(String path) throws 
PersistenceException {
+        final Resource root = context.resourceResolver().resolve("/");
+        assertNotNull("Expecting non-null root", root);
+        return new ResourceBuilderImpl(resourceResolver.create(root, 
ResourceUtil.getName(path), null));
+    }
+    
+    @Before
+    public void setup() {
+        testRootPath = "/" + UUID.randomUUID().toString();
+        resourceResolver = context.resourceResolver();
+    }
+    
+    private Resource assertResource(String path) {
+        final Resource result =  resourceResolver.resolve(fullPath(path));
+        assertNotNull("Expecting resource to exist:" + path, result);
+        return result;
+    }
+    
+    private String fullPath(String path) {
+        return path.startsWith("/") ? path : testRootPath + "/" + path;
+    }
+    
+    private void assertProperties(String path, Object ...props) {
+        final Map<String, Object> expected = MapArgsConverter.toMap(props);
+        final Resource r = assertResource(path);
+        final ValueMap vm = r.adaptTo(ValueMap.class);
+        for(Map.Entry<String, Object> e : expected.entrySet()) {
+            final Object value = vm.get(e.getKey());
+            assertNotNull("Expecting property " + e.getKey() + " for resource 
" + r.getPath());
+            assertEquals(
+                    "Expecting value " + e.getValue() 
+                    + " for property " + e.getKey() + " of resource " + 
r.getPath()
+                    , e.getValue(), value);
+        }
+    }
+    
+    @Test
+    public void basicResource() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("child", "title", "foo")
+            .commit();
+        
+        assertProperties("child", "title", "foo");
+        assertEquals(fullPath("child"), assertResource("child").getPath());
+    }
+    
+    @Test
+    public void ensureResourceExists() throws PersistenceException {
+        
+        class MyResourceBuilder extends ResourceBuilderImpl {
+            MyResourceBuilder() {
+                super(resourceResolver.getResource("/"));
+            }
+            
+            Resource r(String path) {
+                return ensureResourceExists(path);
+            }
+        };
+        final MyResourceBuilder b = new MyResourceBuilder();
+        
+        assertEquals("/", b.r(null).getPath());
+        assertEquals("/", b.r("").getPath());
+        assertEquals("/", b.r("/").getPath());
+    }
+    
+    @Test
+    public void deepResource() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("a/b/c", "title", "foo")
+            .commit();
+        
+        assertProperties("a/b/c", "title", "foo");
+        assertEquals(fullPath("a/b/c"), assertResource("a/b/c").getPath());
+        assertResource("a/b");
+        assertResource("a");
+    }
+    
+    @Test
+    public void intermediatePrimaryTypes() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("a/b/c")
+            .withIntermediatePrimaryType("foo")
+            .resource("d/e")
+            .withIntermediatePrimaryType(null)
+            .resource("f/g")
+            .commit();
+        
+        assertProperties("a/b", ResourceBuilderImpl.JCR_PRIMARYTYPE, 
"nt:unstructured");
+        assertProperties("a/b/c/d", ResourceBuilderImpl.JCR_PRIMARYTYPE, 
"foo");
+        assertProperties("a/b/c/d/e/f", ResourceBuilderImpl.JCR_PRIMARYTYPE, 
"nt:unstructured");
+    }
+    
+    @Test
+    public void resetParent() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("a/b/c")
+            .resetParent()
+            .resource("d/e")
+            .commit();
+        
+        assertResource("a/b/c");
+        assertResource("d/e");
+    }
+    
+    @Test
+    public void noResetParent() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("a/b/c")
+            .resource("d/e")
+            .commit();
+        
+        assertResource("a/b/c");
+        assertResource("a/b/c/d/e");
+    }
+    
+    @Test
+    public void getParent() throws PersistenceException {
+        final Resource parent = getBuilder(testRootPath).getCurrentParent();
+        assertNotNull(parent);
+        assertEquals(testRootPath, parent.getPath());
+    }
+    
+    @Test(expected=RuntimeException.class)
+    public void missingParentFails() throws PersistenceException {
+        new ResourceBuilderImpl(null).resource("foo");
+    }
+    
+    @Test(expected=IllegalArgumentException.class)
+    public void absolutePathFails() throws PersistenceException {
+        getBuilder(testRootPath).resource("/absolute");
+    }
+    
+    @Test
+    public void buildATree() throws PersistenceException {
+        getBuilder(testRootPath)
+            .resource("a/b/c", "title", "foo")
+            .siblingsMode()
+            .resource("1")
+            .resource("2")
+            .resource("3")
+            .hierarchyMode()
+            .resource("with")
+            .resource("more/here", "it", "worked")
+            .resource("deepest", "it", "worked")
+            .commit();
+        
+        assertProperties("a/b/c", "title", "foo");
+        assertProperties("a/b/c/with/more/here", "it", "worked");
+        assertResource("a/b/c/with/more/here/deepest");
+        assertResource("a/b/c/1");
+        assertResource("a/b/c/2");
+        assertResource("a/b/c/3");
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <commits@sling.apache.org>.

Reply via email to