/*
* Copyright 2015 Time Warner Cable, Inc.
*
* Licensed 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 com.twcable.jackalope;
import com.google.common.base.Strings;
import com.twcable.jackalope.impl.common.Values;
import com.twcable.jackalope.impl.jcr.NodeImpl;
import com.twcable.jackalope.impl.jcr.SessionImpl;
import com.twcable.jackalope.impl.sling.NodeResourceImpl;
import com.twcable.jackalope.impl.sling.ResourceResolverImpl;
import com.twcable.jackalope.impl.sling.SimpleResourceResolverFactory;
import com.twcable.jackalope.impl.sling.SlingRepositoryException;
import com.twcable.jackalope.impl.sling.SlingRepositoryImpl;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.jcr.api.SlingRepository;
import javax.annotation.Nonnull;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import java.io.File;
/**
* Utilities for building an in-memory jcr implementation for use in unit tests.
*/
public class JCRBuilder {
private JCRBuilder() {
}
@Nonnull
public static RepositoryBuilder repository(NodeBuilder... nodeBuilders) {
return new RepositoryBuilderImpl(nodeBuilders);
}
/**
* Creates a ResourceBuilder with the given node.
* <p/>
* The ResourceBuilder will create a resource from the node built by nodeBuilder.
*
* @param nodeBuilder The node builder that will build the node that the resource will use
* @return the ResourceBuilder
*/
@Nonnull
public static ResourceBuilder resource(NodeBuilder nodeBuilder) {
return new ResourceBuilderImpl(nodeBuilder);
}
/**
* Creates a NodeBuilder with the given contents.
* <p/>
* The NodeBuilder will build an nt:unstructured node. Its name is derived from the path by default. If you pass
* in a NodeBuilder, the Node it creates will be added as a child of the Node. If you pass in a PropertyBuilderImpl,
* it will set a property of the Node.
* <p/>
*
* @param path the path of the node
* @param values builders for the Node's child items (properties, child nodes)
* @return the NodeBuilder
*/
@Nonnull
public static NodeBuilder node(String path, ItemBuilder... values) {
return node(path, JcrConstants.NT_UNSTRUCTURED, values);
}
/**
* Creates a NodeBuilder with the given contents.
* <p/>
* The NodeBuilder will build a node of the specified type. Its name is derived from the path by default. If you pass
* in a NodeBuilder, the Node it creates will be added as a child of the Node. If you pass in a PropertyBuilderImpl,
* it will set a property of the Node.
*
* @param path the path of the node
* @param nodeTypeName the node type to use for this node
* @param values builders for the Node's child items (properties, child nodes)
* @return the NodeBuilder
*/
@Nonnull
public static NodeBuilder node(String path, String nodeTypeName, ItemBuilder... values) {
return new NodeBuilderImpl(path, nodeTypeName, values);
}
/**
* Creates a PropertyBuilderImpl with the given name and value.
* <p/>
*
* @param name the name of the property
* @param value the value of the property
* @return the PropertyBuilderImpl
*/
@Nonnull
public static PropertyBuilder property(String name, Object value) {
return new PropertyBuilderImpl(name, value);
}
/**
* Creates a PropertyBuilderImpl that will build a multi-value property with the given name and values.
* <p/>
*
* @param name the name of the property
* @param values the values of the property
* @return the PropertyBuilderImpl
*/
@Nonnull
public static PropertyBuilder property(String name, Object[] values) {
return new PropertyBuilderImpl(name, values);
}
// **********************************************************************
//
// INNER CLASSES
//
// **********************************************************************
static class RepositoryBuilderImpl implements RepositoryBuilder {
NodeBuilder[] nodeBuilders;
RepositoryBuilderImpl(NodeBuilder... nodeBuilders) {
this.nodeBuilders = nodeBuilders;
}
public SlingRepository build() {
SlingRepositoryImpl repository = new SlingRepositoryImpl();
try {
for (NodeBuilder nodeBuilder : nodeBuilders)
((NodeBuilderImpl)nodeBuilder).build(repository.login().getRootNode());
}
catch (RepositoryException re) {
throw new SlingRepositoryException(re);
}
return repository;
}
}
static class NodeBuilderImpl implements NodeBuilder {
private final String path;
private final String nodeTypeName;
private final ItemBuilder[] childBuilders;
NodeBuilderImpl(String path, String nodeTypeName, ItemBuilder... childBuilders) {
this.path = path;
this.nodeTypeName = nodeTypeName;
this.childBuilders = childBuilders;
}
/**
* Builds the requested node.
*
* @return the Node
*/
public Node build() {
return build((NodeImpl)null);
}
/**
* Builds the requested node.
*
* @param parent The parent node of the node to be built. If the node does not need a parent, null can be used.
* @return the Node
*/
public NodeImpl build(Node parent) {
try {
NodeImpl node = (parent != null) ? (NodeImpl)parent.addNode(getName(), nodeTypeName) : new NodeImpl(new SessionImpl(), getName());
if (!Strings.isNullOrEmpty(nodeTypeName))
node.setPrimaryType(nodeTypeName);
for (ItemBuilder builder : childBuilders)
builder.build(node);
node.getSession().save();
return node;
}
catch (RepositoryException re) {
throw new SlingRepositoryException(re);
}
}
/**
* Builds the requested node.
*
* @param session Session to be associated with the new node
* @return the Node
*/
protected NodeImpl build(Session session) {
try {
NodeImpl node = new NodeImpl((SessionImpl)session, getName());
if (!Strings.isNullOrEmpty(nodeTypeName))
node.setPrimaryType(nodeTypeName);
for (ItemBuilder builder : childBuilders)
builder.build(node);
node.getSession().save();
return node;
}
catch (RepositoryException re) {
throw new SlingRepositoryException(re);
}
}
private String getName() {
return new File(path).getName();
}
}
static class ResourceBuilderImpl implements ResourceBuilder {
private final NodeBuilder builder;
ResourceBuilderImpl(NodeBuilder builder) {
this.builder = builder;
}
/**
* Builds the requested resource.
*
* @return the Resource
*/
public Resource build() {
try {
ResourceResolverImpl resolver = (ResourceResolverImpl)new SimpleResourceResolverFactory(new SlingRepositoryImpl()).getAdministrativeResourceResolver(null);
return new NodeResourceImpl(resolver, ((NodeBuilderImpl)builder).build(resolver.adaptTo(Session.class)));
}
catch (LoginException le) {
throw new SlingRepositoryException(le);
}
}
}
static class PropertyBuilderImpl implements PropertyBuilder {
private final String name;
private final Value[] values;
private final boolean hasMultiple;
PropertyBuilderImpl(String name, Object value) {
this.name = name;
this.values = Values.convertObjectsToValues(value);
this.hasMultiple = false;
}
PropertyBuilderImpl(String name, Object[] values) {
this.name = name;
this.values = Values.convertObjectsToValues(values);
this.hasMultiple = true;
}
public Property build(Node parent) {
if (parent == null) return null; // properties only exist in nodes so there is nothing to build.
try {
if (hasMultiple)
parent.setProperty(name, values);
else
parent.setProperty(name, values[0]);
return parent.getProperty(name);
}
catch (RepositoryException re) {
throw new SlingRepositoryException(re);
}
}
}
}