/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/macro/MacroImpl.java,v 1.36.2.1 2004/02/05 16:05:08 mholz Exp $
* $Revision: 1.36.2.1 $
* $Date: 2004/02/05 16:05:08 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* 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 org.apache.slide.macro;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.slide.common.Domain;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.SlideTokenWrapper;
import org.apache.slide.content.Content;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.lock.Lock;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.lock.ObjectLockedException;
import org.apache.slide.macro.CopyRouteRedirector.CopyRoute;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.security.NodePermission;
import org.apache.slide.security.Security;
import org.apache.slide.store.Store;
import org.apache.slide.structure.LinkedObjectNotFoundException;
import org.apache.slide.structure.ObjectHasChildrenException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.Structure;
import org.apache.slide.util.Configuration;
/**
* Macro helper class.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @version $Revision: 1.36.2.1 $
*/
public final class MacroImpl implements Macro {
private Namespace namespace;
private NamespaceConfig namespaceConfig;
private Content contentHelper;
private Structure structureHelper;
private Security securityHelper;
private Lock lockHelper;
/**
* Constructor.
*
* @param namespace Namespace
* @param namespaceConfig Namespace configuration
* @param contentHelper Access to content
* @param structureHelper Access to structure
*/
public MacroImpl(Namespace namespace, NamespaceConfig namespaceConfig,
Security securityHelper, Content contentHelper,
Structure structureHelper,
Lock lockHelper) {
this.namespace = namespace;
this.namespaceConfig = namespaceConfig;
this.securityHelper = securityHelper;
this.contentHelper = contentHelper;
this.structureHelper = structureHelper;
this.lockHelper = lockHelper;
}
/**
* Recursive copy with overwrite macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @exception CopyMacroException Generic Slide exception
*/
public void copy(SlideToken token, String sourceUri,
String destinationUri)
throws CopyMacroException, DeleteMacroException {
copy(token, sourceUri, destinationUri,
(CopyRouteRedirector)null, (CopyListener)null,
(DeleteTargetRedirector)null, (DeleteListener)null);
}
/**
* Recursive copy with overwrite macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param copyRedirector the CopyRoutRedirector may be used to redirect
* the source and/or destination URI of the
* <code>copy</code> operation.
* (May be <code>null</code>.)
* @param copyListener the CopyListener that will be notified
* before and after copying a resource.
* (May be <code>null</code>)
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a destination
* that will be overwritten by the copy.
* (May be <code>null</code>)
* @exception CopyMacroException Generic Slide exception
*/
public void copy(SlideToken token, String sourceUri,
String destinationUri, CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws CopyMacroException, DeleteMacroException {
this.copy(token, sourceUri, destinationUri,
RECURSIVE_OVERWRITE_PARAMETERS, copyRedirector, copyListener,
deleteRedirector, deleteListener);
}
/**
* Copy macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param parameters Macro parameters
* @exception CopyMacroException Generic Slide exception
*/
public void copy(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters)
throws CopyMacroException, DeleteMacroException {
copy(token, sourceUri, destinationUri, parameters, null, null, null, null);
}
/**
* Copy macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param parameters Macro parameters
* @param copyRedirector the CopyRoutRedirector may be used to redirect
* the source and/or destination URI of the
* <code>copy</code> operation.
* (May be <code>null</code>.)
* @param copyListener the CopyListener that will be notified
* before and after copying a resource.
* (May be <code>null</code>)
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a destination
* that will be overwritten by the copy.
* (May be <code>null</code>)
* @exception CopyMacroException Generic Slide exception
*/
public void copy(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters,
CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws CopyMacroException, DeleteMacroException {
Domain.debug("Copy " + sourceUri + " to " + destinationUri);
// Now performing the actual copy
CopyMacroException e = new CopyMacroException("Copy failed");
//----------------
// handle the case that source and destination are identical
if (sourceUri.equals(destinationUri)) {
e.addException(new ForbiddenException(sourceUri));
throw e;
}
// try to writeLock the complete destination tree
try {
writeLock(token, destinationUri, true);
}
catch( ServiceAccessException x ) {
e.addException(x);
throw e;
}
catch (SlideException x) {}; // ignore silently
Map alreadyCopied = new HashMap(); // maps source-UURI -> destination-URI
parameters.setParameter( ALREADY_COPIED, alreadyCopied );
copyObject(token, sourceUri, destinationUri, parameters, true, e,
copyRedirector, copyListener, deleteRedirector, deleteListener);
// If there were errors, we throw the nested exception
if (!e.isEmpty()) {
throw e;
}
}
/**
* WriteLock the specified URI
*
* @param token a SlideToken
* @param uri a String
* @param recursive a boolean
* @throws SlideException
*/
private void writeLock(SlideToken token, String uri, boolean recursive) throws SlideException {
if (!token.isForceStoreEnlistment()) {
token = new SlideTokenWrapper(token, true);
}
ObjectNode onode = structureHelper.retrieve(token, uri);
if (onode != null && recursive) {
Iterator i = onode.getChildren().iterator();
while (i.hasNext()) {
writeLock( token, (String)i.next(), true );
}
}
}
// TODO: copyRedirector not used
public void rebind(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters,
CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws CopyMacroException, DeleteMacroException {
CopyMacroException e = new CopyMacroException("Rebind failed");
//----------------
// handle the case that source and destination are identical
if (sourceUri.equals(destinationUri)) {
e.addException(new ForbiddenException(sourceUri));
throw e;
}
// try to writeLock the complete destination tree
try {
writeLock(token, destinationUri, true);
}
catch( ServiceAccessException x ) {
e.addException(x);
throw e;
}
catch (SlideException x) {}; // ignore silently
if (parameters.isDeleteCreate()) {
try {
// If the object we want to overwrite exists, we delete is first
structureHelper.retrieve(token, destinationUri);
delete(token, destinationUri, deleteRedirector, deleteListener);
} catch(ObjectNotFoundException onf) {
// Silent catch, the target doesn't exist
} catch(DeleteMacroException s) {
throw s;
} catch(SlideException s) {
e.addException(s);
throw e;
}
}
//----------------
UriHandler destinationUh = new UriHandler(destinationUri);
UriHandler destinationParentUh = destinationUh.getParent();
ObjectNode destinationParentNode = null;
String destinationSegment = destinationUh.getLastSegment();
UriHandler sourceUh = new UriHandler(sourceUri);
UriHandler sourceParentUh = sourceUh.getParent();
ObjectNode sourceParentNode = null;
String sourceSegment = sourceUh.getLastSegment();
ObjectNode sourceNode = null;
try {
try {
destinationParentNode =
structureHelper.retrieve(token, destinationParentUh.toString(), false );
}
catch (ObjectNotFoundException ex) {
throw new ConflictException(destinationParentUh.toString());
}
sourceParentNode =
structureHelper.retrieve(token, sourceParentUh.toString(), false );
sourceNode =
structureHelper.retrieve(token, sourceUri, false );
}
catch (SlideException x) {
e.addException(x);
throw e;
}
try {
// notify Listeners
if (copyListener != null) {
copyListener.beforeCopy(sourceUri, destinationUri, true);
}
structureHelper.addBinding( token, destinationParentNode, destinationSegment, sourceNode );
structureHelper.removeBinding( token, sourceParentNode, sourceSegment );
}
catch (SlideException x) {
e.addException(x);
throw e;
}
}
/**
* Recursive move with overwrite macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @exception CopyMacroException Exception occured during copy
* @exception DeleteMacroException Exception occured during deletion
*/
public void move(SlideToken token, String sourceUri,
String destinationUri)
throws CopyMacroException, DeleteMacroException {
move(token, sourceUri, destinationUri,
(CopyRouteRedirector)null, (CopyListener)null,
(DeleteTargetRedirector)null, (DeleteListener)null);
}
/**
* Recursive move with overwrite macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param copyRedirector the CopyRoutRedirector may be used to redirect
* the source and/or destination URI of the
* <code>copy</code> operation.
* (May be <code>null</code>.)
* @param copyListener the CopyListener that will be notified
* before and after copying a resource.
* (May be <code>null</code>)
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a resource.
* (May be <code>null</code>)
* @exception CopyMacroException Exception occured during copy
* @exception DeleteMacroException Exception occured during deletion
*/
public void move(SlideToken token, String sourceUri,
String destinationUri, CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws CopyMacroException, DeleteMacroException {
move(token, sourceUri, destinationUri,
RECURSIVE_OVERWRITE_PARAMETERS,
copyRedirector, copyListener, deleteRedirector, deleteListener);
}
/**
* Move macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param parameters Macro parameters
* @exception CopyMacroException Exception occured during copy
* @exception DeleteMacroException Exception occured during deletion
*/
public void move(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters)
throws CopyMacroException, DeleteMacroException {
move(token, sourceUri, destinationUri, parameters, null, null, null, null);
}
/**
* Move macro.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param parameters Macro parameters
* @param copyRedirector the CopyRoutRedirector may be used to redirect
* the source and/or destination URI of the
* <code>copy</code> operation.
* (May be <code>null</code>.)
* @param copyListener the CopyListener that will be notified
* before and after copying a resource.
* (May be <code>null</code>)
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a resource.
* (May be <code>null</code>)
* @exception CopyMacroException Exception occured during copy
* @exception DeleteMacroException Exception occured during deletion
*/
public void move(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters,
CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws CopyMacroException, DeleteMacroException {
if (Configuration.useBinding(namespace.getUri(token, sourceUri).getStore()) &&
Configuration.useBinding(namespace.getUri(token, destinationUri).getStore()) &&
sameStore(token, sourceUri, destinationUri)) {
rebind(token, sourceUri, destinationUri, parameters,
copyRedirector, copyListener, deleteRedirector, deleteListener);
}
else {
copy(token, sourceUri, destinationUri, parameters,
copyRedirector, copyListener, deleteRedirector, deleteListener);
delete(token, sourceUri, parameters, deleteRedirector, deleteListener);
}
}
/**
* Recursive delete.
*
* @param token Credentials token
* @param targetUri Uri of the object to delete
* @exception DeleteMacroException Generic Slide exception
*/
public void delete(SlideToken token, String targetUri)
throws DeleteMacroException {
delete(token, targetUri, (DeleteTargetRedirector)null, (DeleteListener)null);
}
/**
* Recursive delete.
*
* @param token Credentials token
* @param targetUri Uri of the object to delete
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a resource.
* (May be <code>null</code>)
* @exception DeleteMacroException Generic Slide exception
*/
public void delete(SlideToken token, String targetUri,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws DeleteMacroException {
delete(token, targetUri, RECURSIVE_OVERWRITE_PARAMETERS, deleteRedirector, deleteListener);
}
/**
* Delete macro.
*
* @param token Credentials token
* @param targetUri Uri of the source
* @param parameters Macro parameters, not used right now,
* so it can be null
* @exception DeleteMacroException Generic Slide exception
*/
public void delete(SlideToken token, String targetUri,
MacroParameters parameters)
throws DeleteMacroException {
delete(token, targetUri, parameters, null, null);
}
/**
* Delete macro.
*
* @param token Credentials token
* @param targetUri Uri of the source
* @param parameters Macro parameters, not used right now,
* so it can be null
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a resource.
* (May be <code>null</code>)
* @exception DeleteMacroException Generic Slide exception
*/
public void delete(SlideToken token, String targetUri, MacroParameters parameters,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener)
throws DeleteMacroException {
Domain.debug("Delete " + targetUri);
DeleteMacroException e = new DeleteMacroException("Delete failed");
deleteObject(token, targetUri, e, deleteRedirector, deleteListener);
// If there were errors, we throw the nested exception
if (!e.isEmpty()) {
throw e;
}
}
// -------------------------------------------------------- Private Methods
/**
* Copy object.
*
* @param token Credentials token
* @param sourceUri Uri of the source
* @param destinationUri Uri of the destination
* @param parameters Macro parameters
* @param copyRedirector the CopyRoutRedirector may be used to redirect
* the source and/or destination URI of the
* <code>copy</code> operation.
* (May be <code>null</code>.)
* @param copyListener the CopyListener that will be notified
* before and after copying a resource.
* (May be <code>null</code>)
* @param CopyMacroException Exception occured during copy
*/
private void copyObject(SlideToken token, String sourceUri,
String destinationUri, MacroParameters parameters, boolean isRootOfCopy,
CopyMacroException e, CopyRouteRedirector copyRedirector, CopyListener copyListener,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener
) {
Domain.debug("Copy object : from " + sourceUri + " to "
+ destinationUri);
try {
// check for lock-null
if (isLockNull(token, sourceUri)) {
// skip
return;
}
// Remember children of orginal source
ObjectNode sourceNode =
structureHelper.retrieve(token, sourceUri, false);
Enumeration sourceNodeChildren = sourceNode.enumerateChildren();
ObjectNode destinationNode = null;
// now let the client redirect
if (copyRedirector != null) { // TODO: dump re-directing
CopyRoute copyRoute = new CopyRoute(sourceUri, destinationUri);
copyRoute = copyRedirector.getRedirectedCopyRoute(copyRoute);
sourceUri = copyRoute.getSourceUri();
destinationUri = copyRoute.getDestinationUri();
sourceNode = structureHelper.retrieve(token, sourceUri, false);
// note that childrenList is *not* re-assigned. This might be a bug ...
}
// notify CopyListener
if (copyListener != null) {
copyListener.beforeCopy(sourceUri, destinationUri, isRootOfCopy );
}
// delete target if it is the root of the copied tree
if (isRootOfCopy && parameters.isDeleteCreate()) {
try {
// We make sure the object we want to overwrite exists
structureHelper.retrieve(token, destinationUri);
if (parameters.getParameter(PARENT_BINDINGS) != null) {
Map parentBindings = (Map)parameters.getParameter(PARENT_BINDINGS);
Iterator i = parentBindings.entrySet().iterator();
while (i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
String uriToDelete = (String)me.getKey()+"/"+(String)me.getValue();
delete(token, uriToDelete, deleteRedirector, deleteListener);
}
}
else {
delete(token, destinationUri, deleteRedirector, deleteListener);
}
} catch(ObjectNotFoundException onf) {
// Silent catch, the target doesn't exist
} catch(DeleteMacroException s) {
Enumeration en = s.enumerateExceptions();
if (en.hasMoreElements()) {
throw (SlideException)en.nextElement();
}
}
}
boolean destinationExists = destinationExists(token, destinationUri);
// Creating the copy
if (parameters.isDeleteCreate() || !destinationExists) {
try {
Map alreadyCopied = (Map)parameters.getParameter( ALREADY_COPIED );
if (alreadyCopied.containsKey(sourceNode.getUuri())) {
// If a COPY request would cause a new resource to be created
// as a copy of an existing resource, and that COPY request
// has already created a copy of that existing resource,
// the COPY request instead creates another binding to the
// previous copy, instead of creating a new resource.
UriHandler destinationUh = new UriHandler(destinationUri);
UriHandler destinationParentUh = destinationUh.getParent();
String segment = destinationUh.getLastSegment();
destinationNode = structureHelper.retrieve( token, (String)alreadyCopied.get(sourceNode.getUuri()) );
ObjectNode destinationParentNode = structureHelper.retrieve( token, destinationParentUh.toString() );
structureHelper.addBinding( token, destinationParentNode, segment, destinationNode );
}
else {
structureHelper.create(token, sourceNode.copyObject(),
destinationUri);
destinationNode = structureHelper.retrieve( token, destinationUri );
alreadyCopied.put( sourceNode.getUuri(), destinationNode.getUri() );
}
} catch (ObjectNotFoundException s){
throw new ConflictException(s.getObjectUri());
}
}
// Trying to recreate permissions
try {
Enumeration sourcePermissions = securityHelper
.enumeratePermissions(token, sourceNode);
while (sourcePermissions.hasMoreElements()) {
NodePermission permission =
(NodePermission) sourcePermissions.nextElement();
NodePermission newPermission =
new NodePermission(destinationUri,
permission.getSubjectUri(),
permission.getActionUri(),
permission.isInheritable(),
permission.isNegative());
securityHelper.grantPermission(token, newPermission);
}
} catch (AccessDeniedException ex) {
// Means that we don't have modifyPermissions rights
// (root access) on the target.
// The copy should definitely succeed anyway,
// so we silently catch the exception.
}
// Now copying revision descriptors and content
NodeRevisionDescriptors sourceNrds =
contentHelper.retrieve(token, sourceNode.getUri());
if (sourceNrds.hasRevisions()) {
NodeRevisionDescriptor sourceNrd =
contentHelper.retrieve(token, sourceNrds);
NodeRevisionContent sourceNrc = null;
if (sourceNrd.getContentLength() > 0) {
sourceNrc =
contentHelper.retrieve(token, sourceNrds, sourceNrd);
}
if (parameters.isDeleteCreate() || !destinationExists) {
contentHelper.create(token, destinationUri, sourceNrd, sourceNrc);
}
else {
NodeRevisionDescriptor destinationNrd =
contentHelper.retrieve(token,
contentHelper.retrieve(token, destinationUri));
Enumeration sourceProps = sourceNrd.enumerateProperties();
while (sourceProps.hasMoreElements()) {
destinationNrd.setProperty( (NodeProperty)sourceProps.nextElement() );
}
contentHelper.store(token, destinationUri, destinationNrd, sourceNrc);
}
}
// notify CopyListener
if (copyListener != null) {
copyListener.afterCopy(sourceUri, destinationUri, isRootOfCopy);
}
// We copy each of this object's children
while(sourceNodeChildren.hasMoreElements()) {
String childUri = (String) sourceNodeChildren.nextElement();
String childDestinationUri = destinationUri + childUri
.substring(sourceNode.getUri().length());
copyObject(token, childUri, childDestinationUri,
parameters, false, e, copyRedirector, copyListener,
deleteRedirector, deleteListener
);
}
} catch(SlideException ex) {
// ex.printStackTrace(); //the exception is packed and
// (hopefully) reported in the response
e.addException(ex);
}
}
private boolean destinationExists(SlideToken token, String destinationUri) throws ServiceAccessException, LinkedObjectNotFoundException, RevisionDescriptorNotFoundException, ObjectLockedException, AccessDeniedException {
boolean destinationExists = true;
NodeRevisionDescriptor destinationNrd = null;
try {
destinationNrd =
contentHelper.retrieve(token,
contentHelper.retrieve(token, destinationUri));
}
catch (ObjectNotFoundException x) {
destinationExists = false;
}
return destinationExists;
}
/**
* Delete object function. Recursive for now.
*
* @param token Credentials token
* @param targetUri Uri of the source
* @param parameters Macro parameters, not used right now, so it can
* be null
* @param e Nested exception
* @param deleteRedirector the DeleteTargetRedirector may be used to redirect
* the <code>delete</code> operation to a different
* target. (May be <code>null</code>.)
* @param deleteListener the DeleteListener that will be notified
* before and after deleting a resource.
* (May be <code>null</code>)
*/
private void deleteObject(SlideToken token, String targetUri, MacroException e,
DeleteTargetRedirector deleteRedirector, DeleteListener deleteListener) {
Domain.debug("Delete object : " + targetUri);
try {
ObjectNode currentObject =
structureHelper.retrieve(token, targetUri, false);
if (!Configuration.useBinding(namespace.getUri(token, targetUri).getStore()) || currentObject.numberOfParentBindings() < 2) {
// Removing children objects
if (currentObject.hasChildren()) {
Enumeration children = currentObject.enumerateChildren();
while (children.hasMoreElements()) {
String childUri = (String)children.nextElement();
deleteObject(token, childUri, e, deleteRedirector, deleteListener);
}
}
// Removing links objects
if (currentObject.hasLinks()) {
Enumeration links = currentObject.enumerateLinks();
while (links.hasMoreElements()) {
String linkUri = (String)links.nextElement();
deleteObject(token, linkUri, e, deleteRedirector, deleteListener);
}
}
// now let the client redirect
if (deleteRedirector != null) {
targetUri = deleteRedirector.getRedirectedTargetUri(targetUri);
currentObject = structureHelper.retrieve(token, targetUri, false);
}
// notify DeleteListener
if (deleteListener != null) {
deleteListener.beforeDelete(targetUri);
}
NodeRevisionDescriptors revisionDescriptors =
contentHelper.retrieve(token, currentObject.getUri());
// remove the associated locks
Enumeration locks = lockHelper.enumerateLocks
(token, currentObject.getUri(), false);
while (locks.hasMoreElements()) {
lockHelper.unlock(token, (NodeLock) locks.nextElement());
}
// remove the associated security
Enumeration permissions = securityHelper.enumeratePermissions
(token, currentObject);
while (permissions.hasMoreElements()) {
NodePermission permission =
(NodePermission) permissions.nextElement();
securityHelper.revokePermission(token, permission);
}
// remove all revisions
if( !revisionDescriptors.isRedirected() ) {
Enumeration revisionNumberEnum =
revisionDescriptors.enumerateRevisionNumbers();
if (revisionNumberEnum != null) {
while (revisionNumberEnum.hasMoreElements()) {
contentHelper.remove
(token, currentObject.getUri(),
(NodeRevisionNumber)revisionNumberEnum.nextElement());
}
}
removeHiddenRevisions( token, targetUri );
// remove the NodeRevisionDescriptors object
contentHelper.remove(token, revisionDescriptors);
}
}
// Removing object.
structureHelper.remove(token, currentObject);
// notify DeleteListener
if (deleteListener != null) {
deleteListener.afterDelete(targetUri);
}
} catch (ObjectHasChildrenException ex) {
// ignore, the object has children because something else failed
// (one of the children is locked, for example), and the
// corresponding exception has already been added the the
// MacroException
} catch (SlideException ex) {
e.addException(ex);
}
}
/**
* Removes all <i>hidden</i> revisions for the specified URI. Currently there are hidden
* revisions only at 0.0.
*/
private void removeHiddenRevisions( SlideToken token, String targetUri ) throws ServiceAccessException, ObjectNotFoundException, LinkedObjectNotFoundException, AccessDeniedException, ObjectLockedException {
// Can this piece of code be moved to MoveMethod.afterDelete() and DeleteMethod.afterDelete?
// It would look more consistent (the copying of hidden revisions is there!!)
// BUT: I dont think it's a good idea because the deleteObject() method would delete
// the NRDS while hidden revisions remain.
try {
contentHelper.remove(token, targetUri, NodeRevisionNumber.HIDDEN_0_0 );
}
catch (RevisionDescriptorNotFoundException ne) {
// ignore
}
}
/**
** Uri handler.
**
** @author peter.nevermann@softwareag.com
**/
private static class UriHandler {
/** The path tokens */
String[] tokens = null;
final String uriDelimiter = "/";
/**
** Default constructor.
**/
UriHandler( String uri ) {
StringTokenizer ut = new StringTokenizer( uri, uriDelimiter );
int ntok = ut.countTokens();
this.tokens = new String[ntok];
for( int i = 0; i < ntok; i++ )
tokens[i] = ut.nextToken();
}
/**
** Default constructor.
**/
UriHandler( String[] toks ) {
this.tokens = new String[toks.length];
for( int i = 0; i < toks.length; i++ )
tokens[i] = toks[i];
}
/**
** Default constructor.
** @pre number <= toks.length
**/
UriHandler( String[] toks, int number ) {
this.tokens = new String[number];
for( int i = 0; i < number; i++ )
tokens[i] = toks[i];
}
/**
** Check whether this is the root.
**/
boolean isRoot() {
return (tokens.length == 0);
}
/**
* Return the parent.
* Example: for /a/b/c returns: /a/b
*/
UriHandler getParent() {
if( isRoot() )
return null;
return new UriHandler( tokens, tokens.length - 1 );
}
/**
* Checks this URI is parent of the specified URI
*/
boolean isParentOf( UriHandler uh ) {
return equals( uh.getParent() );
}
/**
* Returns the node name (last token), e.g. "c" for /a/b/c
*/
String getLastSegment() {
return tokens[tokens.length - 1];
}
/**
*
*/
public boolean equals( Object o ) {
if( o instanceof UriHandler ) {
UriHandler ouh = (UriHandler)o;
return Arrays.equals( tokens, ouh.tokens );
}
return false;
}
/**
*
*/
public int hashCode() {
return tokens.length;
}
/**
* Return string representation.
*/
public String toString() {
StringBuffer b = new StringBuffer();
if( tokens.length == 0 )
b.append( uriDelimiter );
for( int i = 0; i < tokens.length; i++ )
b.append( uriDelimiter ).append( tokens[i] );
return b.toString();
}
}
private boolean sameStore(SlideToken token, String leftUri, String rightUri) {
Store leftStore = namespace.getUri(token, leftUri).getStore();
Store rightStore = namespace.getUri(token, rightUri).getStore();
if (leftStore == null || rightStore == null) {
throw new IllegalStateException("Got null store: leftStore="+leftStore+", rightStore="+rightStore);
}
return leftStore == rightStore;
}
private boolean isLockNull( SlideToken slideToken, String uriStr ) throws ServiceAccessException {
boolean isLockNull = false;
try {
NodeRevisionDescriptor nrd =
contentHelper.retrieve(slideToken, contentHelper.retrieve(slideToken, uriStr));
isLockNull = isLockNull( nrd );
}
catch (ServiceAccessException x) {
throw x;
}
catch (SlideException x) {
// ignore silently
}
return isLockNull;
}
private boolean isLockNull( NodeRevisionDescriptor nrd ) {
return nrd.propertyValueContains("resourcetype", "lock-null");
}
}