/*
* 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.impl.jcr;
import com.twcable.jackalope.impl.common.Paths;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import javax.annotation.Nonnull;
import javax.jcr.Credentials;
import javax.jcr.InvalidSerializedDataException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.LoginException;
import javax.jcr.NamespaceException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFactory;
import javax.jcr.Workspace;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.retention.RetentionManager;
import javax.jcr.security.AccessControlManager;
import javax.jcr.version.VersionException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Simple implementation of an {@link Session}
*/
@SuppressWarnings("DuplicateThrows")
public class SessionImpl implements Session {
private final Repository repository;
private final Map<String, ItemImpl> itemStore = new LinkedHashMap<>();
private boolean isLive = true;
private Set<String> addedItems = new LinkedHashSet<>();
private Set<String> changedItems = new LinkedHashSet<>();
private Workspace workspace = null;
@Override
public Repository getRepository() {
return repository;
}
@Override
public String getUserID() {
return null;
}
@Override
public String[] getAttributeNames() {
return new String[0];
}
@Override
public Object getAttribute(String name) {
return null;
}
@Override
public Workspace getWorkspace() {
if (workspace == null) {
WorkspaceImpl workspaceImpl = new WorkspaceImpl();
workspaceImpl.setSession(this);
workspace = workspaceImpl;
}
return workspace;
}
@Override
public Node getRootNode() {
return (Node)itemStore.get("/"); // Added in ctor
}
@Override
public Session impersonate(Credentials credentials) throws LoginException, RepositoryException {
return this;
}
@Override
public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException {
return null; //TODO: Implement IDs
}
@Override
public Node getNodeByIdentifier(String id) throws ItemNotFoundException, RepositoryException {
return null; //TODO: Implement IDs
}
@Override
public Item getItem(String absPath) throws PathNotFoundException {
if (!itemExists(absPath)) throw new PathNotFoundException();
return getItemImpl(absPath);
}
@Override
public Node getNode(String absPath) throws PathNotFoundException {
return (Node)getItem(absPath);
}
@Override
public Property getProperty(String absPath) throws PathNotFoundException {
return (Property)getItem(absPath);
}
@Override
public boolean itemExists(String absPath) {
return itemStore.containsKey(absPath);
}
@Override
public boolean nodeExists(String absPath) {
return itemExists(absPath) && itemStore.get(absPath).isNode();
}
@Override
public boolean propertyExists(String absPath) {
return itemExists(absPath) && !itemStore.get(absPath).isNode();
}
@Override
public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
if (!nodeExists(Paths.parent(srcAbsPath))) throw new PathNotFoundException();
if (!nodeExists(Paths.parent(destAbsPath))) throw new PathNotFoundException();
if (itemExists(destAbsPath)) throw new ItemExistsException();
// Can't modify a map while iterating over it. So, put the keys that need to me moved into a list while
// iterating over the map and then iterate over the list
List<String> keys = new ArrayList<>();
for (String key : itemStore.keySet())
if (key.startsWith(srcAbsPath))
keys.add(key);
for (String key : keys)
moveItem(key, key.replaceFirst("^" + srcAbsPath, destAbsPath));
}
@Override
public void removeItem(String absPath) {
if (itemExists(absPath))
removeItem(getItemImpl(absPath));
}
@Override
public void save() {
addedItems.clear();
changedItems.clear();
}
@Override
public void refresh(boolean keepChanges) throws RepositoryException {
}
@Override
public boolean hasPendingChanges() {
return !addedItems.isEmpty() || !changedItems.isEmpty();
}
@Override
public ValueFactory getValueFactory() throws UnsupportedRepositoryOperationException, RepositoryException {
return new ValueFactoryImpl();
}
@Override
public boolean hasPermission(String absPath, String actions) throws RepositoryException {
return false;
}
@Override
public void checkPermission(String absPath, String actions) throws AccessControlException, RepositoryException {
}
@Override
public boolean hasCapability(String methodName, Object target, Object[] arguments) throws RepositoryException {
return false;
}
@Override
public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, RepositoryException {
return null;
}
@Override
public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException, VersionException, InvalidSerializedDataException, LockException, RepositoryException {
}
@Override
public void exportSystemView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
}
@Override
public void exportSystemView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
}
@Override
public void exportDocumentView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
}
@Override
public void exportDocumentView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
}
@Override
public void setNamespacePrefix(String prefix, String uri) throws NamespaceException, RepositoryException {
}
@Override
public String[] getNamespacePrefixes() throws RepositoryException {
return new String[0];
}
@Override
public String getNamespaceURI(String prefix) throws NamespaceException, RepositoryException {
return null;
}
@Override
public String getNamespacePrefix(String uri) throws NamespaceException, RepositoryException {
return null;
}
@Override
public void logout() {
isLive = false;
}
@Override
public boolean isLive() {
return isLive;
}
@Override
public void addLockToken(String lt) {
}
@Override
public String[] getLockTokens() {
return new String[0];
}
@Override
public void removeLockToken(String lt) {
}
@Override
public AccessControlManager getAccessControlManager() throws UnsupportedRepositoryOperationException, RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
@Override
public RetentionManager getRetentionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
/* The following methods are package private and implement the functionality used to implement the various
* features of the jackrabbit project.
*/
Item addItem(@Nonnull ItemImpl item) throws ItemNotFoundException, ItemExistsException {
if (itemStore.containsKey(item.getPath())) throw new ItemExistsException();
addedItems.add(item.getPath());
return storeItem(item);
}
Item changeItem(@Nonnull ItemImpl item) throws ItemNotFoundException {
if (!addedItems.contains(item.getPath()))
changedItems.add(item.getPath());
return storeItem(item);
}
Item removeItem(@Nonnull ItemImpl item) {
changedItems.add(item.getParentImpl().getPath());
for (ItemImpl descendant : getDescendants(item))
itemStore.remove(descendant.getPath());
itemStore.remove(item.getPath());
return item;
}
private ItemImpl getItemImpl(String absPath) {
return itemStore.get(absPath);
}
private Item storeItem(@Nonnull ItemImpl item) throws ItemNotFoundException {
itemStore.put(item.getPath(), item);
return item;
}
private void moveItem(String src, String dest) {
ItemImpl item = itemStore.get(src);
item.setPath(dest);
itemStore.put(dest, item);
itemStore.remove(src);
}
private List<ItemImpl> getDescendants(ItemImpl item) {
List<ItemImpl> descendants = new ArrayList<>();
for (String key : itemStore.keySet())
if (Paths.ancestorOf(item.getPath(), key))
descendants.add(itemStore.get(key));
return descendants;
}
List<Item> getChildren(Item parent) {
List<Item> children = new ArrayList<>();
for (ItemImpl item : itemStore.values())
if (item.getParentImpl() == parent)
children.add(item);
return children;
}
public SessionImpl() {
this(null);
}
public SessionImpl(Repository repository) {
this.repository = repository;
try {
addItem(new NodeImpl(this, "/"));
}
catch (RepositoryException re) { /* can't happen */ }
save();
}
void save(ItemImpl item) {
String path = item.getPath();
for (Iterator<String> i = addedItems.iterator(); i.hasNext(); )
if (Paths.selfOrAncestorOf(path, i.next()))
i.remove();
for (Iterator<String> i = changedItems.iterator(); i.hasNext(); )
if (Paths.selfOrAncestorOf(path, i.next()))
i.remove();
}
boolean isNew(ItemImpl item) {
return addedItems.contains(item.getPath());
}
boolean isModified(ItemImpl item) {
return changedItems.contains(item.getPath());
}
}