org.jboss.aerogear.sync.diffmatchpatch.server.DiffMatchPatchServerSynchronizer.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.aerogear.sync.diffmatchpatch.server.DiffMatchPatchServerSynchronizer.java

Source

/**
 * JBoss, Home of Professional Open Source
 * Copyright Red Hat, Inc., and individual contributors.
 *
 * 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.jboss.aerogear.sync.diffmatchpatch.server;

import com.fasterxml.jackson.databind.JsonNode;
import org.jboss.aerogear.sync.*;
import org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatch;
import org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatchDiff;
import org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatchEdit;
import org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatchMessage;
import org.jboss.aerogear.sync.diffmatchpatch.JsonMapper;
import org.jboss.aerogear.sync.server.ServerSynchronizer;

import java.util.LinkedList;
import java.util.Queue;

import static org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatch.Patch;
import static org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatch.builder;
import static org.jboss.aerogear.sync.diffmatchpatch.DiffMatchPatch.checksum;

/**
 * A {@link ServerSynchronizer} implementation that can handle text documents.
 */
public class DiffMatchPatchServerSynchronizer implements ServerSynchronizer<String, DiffMatchPatchEdit> {

    private final DiffMatchPatch diffMatchPatch;

    public DiffMatchPatchServerSynchronizer() {
        this(builder().build());
    }

    public DiffMatchPatchServerSynchronizer(final DiffMatchPatch diffMatchPatch) {
        this.diffMatchPatch = diffMatchPatch;
    }

    @Override
    public DiffMatchPatchEdit clientDiff(final Document<String> document,
            final ShadowDocument<String> shadowDocument) {
        final String shadowText = shadowDocument.document().content();
        final LinkedList<DiffMatchPatch.Diff> diffs = diffMatchPatch.diffMain(document.content(), shadowText);
        return DiffMatchPatchEdit.withChecksum(checksum(shadowText)).diffs(asAeroGearDiffs(diffs)).build();
    }

    @Override
    public DiffMatchPatchEdit serverDiff(final Document<String> document,
            final ShadowDocument<String> shadowDocument) {
        final String shadowText = shadowDocument.document().content();
        final LinkedList<DiffMatchPatch.Diff> diffs = diffMatchPatch.diffMain(shadowText, document.content());
        return DiffMatchPatchEdit.withChecksum(checksum(shadowText)).serverVersion(shadowDocument.serverVersion())
                .clientVersion(shadowDocument.clientVersion()).diffs(asAeroGearDiffs(diffs)).build();
    }

    @Override
    public ShadowDocument<String> patchShadow(final DiffMatchPatchEdit edit,
            final ShadowDocument<String> shadowDocument) {
        final LinkedList<Patch> patches = patchesFrom(edit);
        final ClientDocument<String> doc = shadowDocument.document();
        //TODO: results also contains a boolean array. Not sure what we should do with it.
        final Object[] results = diffMatchPatch.patchApply(patches, doc.content());
        final ClientDocument<String> patchedDocument = new DefaultClientDocument<String>(doc.id(), doc.clientId(),
                (String) results[0]);
        return new DefaultShadowDocument<String>(shadowDocument.serverVersion(), edit.clientVersion(),
                patchedDocument);
    }

    @Override
    public Document<String> patchDocument(final DiffMatchPatchEdit edit, final Document<String> document) {
        final LinkedList<Patch> patches = patchesFrom(edit);
        final Object[] results = diffMatchPatch.patchApply(patches, document.content());
        //TODO: results also contains a boolean array. Not sure what we should do with it.
        return new DefaultDocument<String>(document.id(), (String) results[0]);
    }

    @Override
    public PatchMessage<DiffMatchPatchEdit> createPatchMessage(final String documentId, final String clientId,
            final Queue<DiffMatchPatchEdit> edits) {
        return new DiffMatchPatchMessage(documentId, clientId, edits);
    }

    @Override
    public PatchMessage<DiffMatchPatchEdit> patchMessageFromJson(String json) {
        return JsonMapper.fromJson(json, DiffMatchPatchMessage.class);
    }

    @Override
    public Document<String> documentFromJson(JsonNode json) {
        final JsonNode contentNode = json.get("content");
        String content = null;
        if (contentNode != null && !contentNode.isNull()) {
            if (contentNode.isArray() || contentNode.isObject()) {
                content = JsonMapper.toString(contentNode);
            } else {
                content = contentNode.asText();
            }
        }
        return new DefaultDocument<String>(json.get("id").asText(), content);
    }

    private LinkedList<Patch> patchesFrom(final DiffMatchPatchEdit edit) {
        return diffMatchPatch.patchMake(asDiffUtilDiffs(edit.diff().diffs()));
    }

    private static LinkedList<DiffMatchPatch.Diff> asDiffUtilDiffs(final LinkedList<DiffMatchPatchDiff> diffs) {
        final LinkedList<DiffMatchPatch.Diff> dsf = new LinkedList<DiffMatchPatch.Diff>();
        for (DiffMatchPatchDiff d : diffs) {
            dsf.add(DiffMatchPatch.diff(diffutilOp(d.operation()), d.text()));
        }
        return dsf;
    }

    private static LinkedList<DiffMatchPatchDiff> asAeroGearDiffs(final LinkedList<DiffMatchPatch.Diff> diffs) {
        final LinkedList<DiffMatchPatchDiff> syncDiffs = new LinkedList<DiffMatchPatchDiff>();
        for (DiffMatchPatch.Diff diff : diffs) {
            syncDiffs.add(new DiffMatchPatchDiff(aerogearOp(diff.operation), diff.text));
        }
        return syncDiffs;
    }

    private static DiffMatchPatch.Operation diffutilOp(final DiffMatchPatchDiff.Operation operation) {
        switch (operation) {
        case DELETE:
            return DiffMatchPatch.Operation.DELETE;
        case ADD:
            return DiffMatchPatch.Operation.INSERT;
        case UNCHANGED:
            return DiffMatchPatch.Operation.EQUAL;
        default:
            throw new RuntimeException("Unsupported Operation: " + operation);
        }
    }

    private static DiffMatchPatchDiff.Operation aerogearOp(final DiffMatchPatch.Operation operation) {
        switch (operation) {
        case DELETE:
            return DiffMatchPatchDiff.Operation.DELETE;
        case INSERT:
            return DiffMatchPatchDiff.Operation.ADD;
        case EQUAL:
            return DiffMatchPatchDiff.Operation.UNCHANGED;
        default:
            throw new RuntimeException("Unsupported Operation: " + operation);
        }
    }

}