/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.core.graph;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.error.ErrorBuffer;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.AbstractRelationship;
import org.structr.core.entity.Relation;
//~--- classes ----------------------------------------------------------------
/**
* Deletes a node.
*
*
*/
public class DeleteNodeCommand extends NodeServiceCommand {
private static final Logger logger = LoggerFactory.getLogger(DeleteNodeCommand.class.getName());
private final Set<NodeInterface> deletedNodes = new HashSet<>();
//~--- methods --------------------------------------------------------
public void execute(NodeInterface node) {
doDeleteNode(node);
deletedNodes.clear();
}
private AbstractNode doDeleteNode(final NodeInterface node) {
try {
if (!deletedNodes.contains(node) && node.getUuid() == null) {
logger.warn("Will not delete node which has no UUID, dumping stack.");
Thread.dumpStack();
return null;
}
} catch (java.lang.IllegalStateException ise) {
logger.warn("Trying to delete a node which is already deleted", ise.getMessage());
return null;
} catch (org.structr.api.NotFoundException nfex) {
// exception can be ignored, node is already deleted
}
deletedNodes.add(node);
App app = StructrApp.getInstance(securityContext);
try {
List<NodeInterface> nodesToCheckAfterDeletion = new LinkedList<>();
// Delete all end nodes of outgoing relationships which are connected
// by relationships which are marked with DELETE_OUTGOING
for (AbstractRelationship rel : node.getOutgoingRelationships()) {
// deleted rels can be null..
if (rel != null) {
int cascadeDelete = rel.cascadeDelete();
NodeInterface endNode = rel.getTargetNode();
if ((cascadeDelete & Relation.CONSTRAINT_BASED) == Relation.CONSTRAINT_BASED) {
nodesToCheckAfterDeletion.add(endNode);
}
if (!deletedNodes.contains(endNode) && ((cascadeDelete & Relation.SOURCE_TO_TARGET) == Relation.SOURCE_TO_TARGET)) {
// remove end node from index
endNode.removeFromIndex();
doDeleteNode(endNode);
}
}
}
// Delete all start nodes of incoming relationships which are connected
// by relationships which are marked with DELETE_INCOMING
for (AbstractRelationship rel : node.getIncomingRelationships()) {
// deleted rels can be null
if (rel != null) {
int cascadeDelete = rel.cascadeDelete();
NodeInterface startNode = rel.getSourceNode();
if ((cascadeDelete & Relation.CONSTRAINT_BASED) == Relation.CONSTRAINT_BASED) {
nodesToCheckAfterDeletion.add(startNode);
}
if (!deletedNodes.contains(startNode) && ((cascadeDelete & Relation.TARGET_TO_SOURCE) == Relation.TARGET_TO_SOURCE)) {
// remove start node from index
startNode.removeFromIndex();
doDeleteNode(startNode);
}
}
}
// deletion callback, must not prevent node deletion!
node.onNodeDeletion();
// Delete any relationship (this is PASSIVE DELETION)
for (AbstractRelationship r : node.getRelationships()) {
if (r != null) {
app.delete(r);
}
}
// remove node from index
node.removeFromIndex();
// mark node as deleted in transaction
TransactionCommand.nodeDeleted(securityContext.getCachedUser(), node);
// delete node in database
node.getNode().delete();
// now check again the deletion cascade for violated constraints
// Check all end nodes of outgoing relationships which are connected if they are
// still valid after node deletion
for (NodeInterface nodeToCheck : nodesToCheckAfterDeletion) {
ErrorBuffer errorBuffer = new ErrorBuffer();
if (!deletedNodes.contains(nodeToCheck) && !nodeToCheck.isValid(errorBuffer)) {
// remove end node from index
nodeToCheck.removeFromIndex();
doDeleteNode(nodeToCheck);
}
}
} catch (Throwable t) {
logger.debug("Exception while deleting node: {}", node);
}
return null;
}
}