Search in sources :

Example 1 with InvertibleModificationOperation

use of org.structr.web.diff.InvertibleModificationOperation in project structr by structr.

the class FtpFilePageWrapper method createOutputStream.

@Override
public OutputStream createOutputStream(long offset) throws IOException {
    final Page origPage = page;
    OutputStream out = new ByteArrayOutputStream() {

        @Override
        public void flush() throws IOException {
            final String source = toString();
            final App app = StructrApp.getInstance();
            try (Tx tx = app.tx()) {
                // parse page from modified source
                Page modifiedPage = Importer.parsePageFromSource(page.getSecurityContext(), source, "__FTP_Temporary_Page__");
                final List<InvertibleModificationOperation> changeSet = Importer.diffNodes(origPage, modifiedPage);
                for (final InvertibleModificationOperation op : changeSet) {
                    // execute operation
                    op.apply(app, origPage, modifiedPage);
                }
                app.delete(modifiedPage);
                tx.success();
            } catch (FrameworkException fex) {
                logger.warn("", fex);
            }
            super.flush();
        }
    };
    return out;
}
Also used : StructrApp(org.structr.core.app.StructrApp) App(org.structr.core.app.App) InvertibleModificationOperation(org.structr.web.diff.InvertibleModificationOperation) Tx(org.structr.core.graph.Tx) FrameworkException(org.structr.common.error.FrameworkException) OutputStream(java.io.OutputStream) ByteArrayOutputStream(java.io.ByteArrayOutputStream) Page(org.structr.web.entity.dom.Page) ByteArrayOutputStream(java.io.ByteArrayOutputStream)

Example 2 with InvertibleModificationOperation

use of org.structr.web.diff.InvertibleModificationOperation in project structr by structr.

the class DiffTest method testDiff.

private String testDiff(final String source, final Function<String, String> modifier) {
    Settings.JsonIndentation.setValue(true);
    Settings.HtmlIndentation.setValue(true);
    final StringBuilder buf = new StringBuilder();
    String sourceHtml = null;
    try {
        // create page from source
        final Page sourcePage = Importer.parsePageFromSource(securityContext, source, "test");
        // render page into HTML string
        try (final Tx tx = app.tx()) {
            sourceHtml = sourcePage.getContent(RenderContext.EditMode.RAW);
            tx.success();
        }
        // modify HTML string with transformation function
        final String modifiedHtml = modifier.apply(sourceHtml);
        // parse page from modified source
        final Page modifiedPage = Importer.parsePageFromSource(securityContext, modifiedHtml, "Test");
        // create and apply diff operations
        try (final Tx tx = app.tx()) {
            final List<InvertibleModificationOperation> changeSet = Importer.diffNodes(sourcePage, modifiedPage);
            for (final InvertibleModificationOperation op : changeSet) {
                System.out.println(op);
                // execute operation
                op.apply(app, sourcePage, modifiedPage);
                System.out.println("############################################################################################");
            // System.out.println(sourcePage.getContent(RenderContext.EditMode.NONE));
            }
            tx.success();
        }
        // render modified page into buffer
        try (final Tx tx = app.tx()) {
            buf.append(sourcePage.getContent(RenderContext.EditMode.NONE));
            tx.success();
        }
    } catch (Throwable t) {
        logger.warn("", t);
    }
    return buf.toString();
}
Also used : InvertibleModificationOperation(org.structr.web.diff.InvertibleModificationOperation) Tx(org.structr.core.graph.Tx) Page(org.structr.web.entity.dom.Page)

Example 3 with InvertibleModificationOperation

use of org.structr.web.diff.InvertibleModificationOperation in project structr by structr.

the class SaveNodeCommand method processMessage.

@Override
public void processMessage(final WebSocketMessage webSocketData) {
    final String nodeId = webSocketData.getId();
    final Map<String, Object> nodeData = webSocketData.getNodeData();
    final String modifiedHtml = (String) nodeData.get("source");
    final SecurityContext securityContext = getWebSocket().getSecurityContext();
    final App app = StructrApp.getInstance(securityContext);
    Page modifiedNode = null;
    DOMNode sourceNode = (DOMNode) getNode(nodeId);
    if (sourceNode != null) {
        TransactionCommand.registerNodeCallback(sourceNode, callback);
        try {
            // parse page from modified source
            modifiedNode = Importer.parsePageFromSource(securityContext, modifiedHtml, "__SaveNodeCommand_Temporary_Page__");
            DOMNode targetNode = modifiedNode;
            if (!(sourceNode instanceof Page)) {
                targetNode = (DOMNode) modifiedNode.getFirstChild().getNextSibling().getFirstChild().getNextSibling().getFirstChild();
            }
            final List<InvertibleModificationOperation> changeSet = Importer.diffNodes(sourceNode, targetNode);
            for (final InvertibleModificationOperation op : changeSet) {
                // execute operation
                op.apply(app, sourceNode.getClosestPage(), modifiedNode);
            }
        } catch (Throwable t) {
            logger.warn("", t);
            // send exception
            getWebSocket().send(MessageBuilder.status().code(422).message(t.toString()).build(), true);
        }
        try {
            app.delete(modifiedNode);
        } catch (FrameworkException ex) {
            logger.warn("", ex);
        }
    } else {
        // send exception
        getWebSocket().send(MessageBuilder.status().code(422).message("Cannot save page").build(), true);
    }
}
Also used : StructrApp(org.structr.core.app.StructrApp) App(org.structr.core.app.App) InvertibleModificationOperation(org.structr.web.diff.InvertibleModificationOperation) FrameworkException(org.structr.common.error.FrameworkException) SecurityContext(org.structr.common.SecurityContext) Page(org.structr.web.entity.dom.Page) DOMNode(org.structr.web.entity.dom.DOMNode)

Example 4 with InvertibleModificationOperation

use of org.structr.web.diff.InvertibleModificationOperation in project structr by structr.

the class Importer method diffNodes.

public static List<InvertibleModificationOperation> diffNodes(final DOMNode sourceNode, final DOMNode modifiedNode) {
    if (sourceNode == null) {
        logger.warn("Source node was null, returning empty change set.");
        return Collections.EMPTY_LIST;
    }
    if (modifiedNode == null) {
        logger.warn("Modified node was null, returning empty change set.");
        return Collections.EMPTY_LIST;
    }
    final List<InvertibleModificationOperation> changeSet = new LinkedList<>();
    final Map<String, DOMNode> indexMappedExistingNodes = new LinkedHashMap<>();
    final Map<String, DOMNode> hashMappedExistingNodes = new LinkedHashMap<>();
    final Map<DOMNode, Integer> depthMappedExistingNodes = new LinkedHashMap<>();
    final Map<String, DOMNode> indexMappedNewNodes = new LinkedHashMap<>();
    final Map<String, DOMNode> hashMappedNewNodes = new LinkedHashMap<>();
    final Map<DOMNode, Integer> depthMappedNewNodes = new LinkedHashMap<>();
    InvertibleModificationOperation.collectNodes(sourceNode, indexMappedExistingNodes, hashMappedExistingNodes, depthMappedExistingNodes);
    InvertibleModificationOperation.collectNodes(modifiedNode, indexMappedNewNodes, hashMappedNewNodes, depthMappedNewNodes);
    // iterate over existing nodes and try to find deleted ones
    for (final Iterator<Map.Entry<String, DOMNode>> it = hashMappedExistingNodes.entrySet().iterator(); it.hasNext(); ) {
        final Map.Entry<String, DOMNode> existingNodeEntry = it.next();
        final DOMNode existingNode = existingNodeEntry.getValue();
        final String existingHash = existingNode.getIdHash();
        // check for deleted nodes ignoring Page nodes
        if (!hashMappedNewNodes.containsKey(existingHash) && !(existingNode instanceof Page)) {
            changeSet.add(new DeleteOperation(hashMappedExistingNodes, existingNode));
        }
    }
    // iterate over new nodes and try to find new ones
    for (final Iterator<Map.Entry<String, DOMNode>> it = indexMappedNewNodes.entrySet().iterator(); it.hasNext(); ) {
        final Map.Entry<String, DOMNode> newNodeEntry = it.next();
        final DOMNode newNode = newNodeEntry.getValue();
        // if newNode is a content element, do not rely on local hash property
        String newHash = newNode.getDataHash();
        if (newHash == null) {
            newHash = newNode.getIdHash();
        }
        // check for deleted nodes ignoring Page nodes
        if (!hashMappedExistingNodes.containsKey(newHash) && !(newNode instanceof Page)) {
            final DOMNode newParent = newNode.getParent();
            changeSet.add(new CreateOperation(hashMappedExistingNodes, getHashOrNull(newParent), getSiblingHashes(newNode), newNode, depthMappedNewNodes.get(newNode)));
        }
    }
    // compare all new nodes with all existing nodes
    for (final Map.Entry<String, DOMNode> newNodeEntry : indexMappedNewNodes.entrySet()) {
        final String newTreeIndex = newNodeEntry.getKey();
        final DOMNode newNode = newNodeEntry.getValue();
        for (final Map.Entry<String, DOMNode> existingNodeEntry : indexMappedExistingNodes.entrySet()) {
            final String existingTreeIndex = existingNodeEntry.getKey();
            final DOMNode existingNode = existingNodeEntry.getValue();
            DOMNode newParent = null;
            int equalityBitmask = 0;
            if (newTreeIndex.equals(existingTreeIndex)) {
                equalityBitmask |= 1;
            }
            if (newNode.getIdHashOrProperty().equals(existingNode.getIdHash())) {
                equalityBitmask |= 2;
            }
            if (newNode.contentEquals(existingNode)) {
                equalityBitmask |= 4;
            }
            switch(equalityBitmask) {
                case // same tree index (1), same node (2), same content (4) => node is completely unmodified
                7:
                    break;
                case // same content (2), same node (4), NOT same tree index => node has moved
                6:
                    newParent = newNode.getParent();
                    changeSet.add(new MoveOperation(hashMappedExistingNodes, getHashOrNull(newParent), getSiblingHashes(newNode), newNode, existingNode));
                    break;
                case // same tree index (1), NOT same node, same content (5) => node was deleted and restored, maybe the identification information was lost
                5:
                    break;
                case // NOT same tree index, NOT same node, same content (4) => different node, content is equal by chance?
                4:
                    break;
                case // same tree index, same node, NOT same content => node was modified but not moved
                3:
                    changeSet.add(new UpdateOperation(hashMappedExistingNodes, existingNode, newNode));
                    break;
                case // NOT same tree index, same node (2), NOT same content => node was moved and changed
                2:
                    newParent = newNode.getParent();
                    changeSet.add(new UpdateOperation(hashMappedExistingNodes, existingNode, newNode));
                    changeSet.add(new MoveOperation(hashMappedExistingNodes, getHashOrNull(newParent), getSiblingHashes(newNode), newNode, existingNode));
                    break;
                case // same tree index (1), NOT same node, NOT same content => ignore
                1:
                    break;
                case // NOT same tree index, NOT same node, NOT same content => ignore
                0:
                    break;
            }
        }
    }
    return changeSet;
}
Also used : InvertibleModificationOperation(org.structr.web.diff.InvertibleModificationOperation) Page(org.structr.web.entity.dom.Page) LinkedList(java.util.LinkedList) LinkedHashMap(java.util.LinkedHashMap) DeleteOperation(org.structr.web.diff.DeleteOperation) CreateOperation(org.structr.web.diff.CreateOperation) MoveOperation(org.structr.web.diff.MoveOperation) UpdateOperation(org.structr.web.diff.UpdateOperation) DOMNode(org.structr.web.entity.dom.DOMNode) Map(java.util.Map) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) PropertyMap(org.structr.core.property.PropertyMap)

Aggregations

InvertibleModificationOperation (org.structr.web.diff.InvertibleModificationOperation)4 Page (org.structr.web.entity.dom.Page)4 FrameworkException (org.structr.common.error.FrameworkException)2 App (org.structr.core.app.App)2 StructrApp (org.structr.core.app.StructrApp)2 Tx (org.structr.core.graph.Tx)2 DOMNode (org.structr.web.entity.dom.DOMNode)2 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 OutputStream (java.io.OutputStream)1 HashMap (java.util.HashMap)1 LinkedHashMap (java.util.LinkedHashMap)1 LinkedList (java.util.LinkedList)1 Map (java.util.Map)1 SecurityContext (org.structr.common.SecurityContext)1 PropertyMap (org.structr.core.property.PropertyMap)1 CreateOperation (org.structr.web.diff.CreateOperation)1 DeleteOperation (org.structr.web.diff.DeleteOperation)1 MoveOperation (org.structr.web.diff.MoveOperation)1 UpdateOperation (org.structr.web.diff.UpdateOperation)1