DefaultDiffService.java 14.5 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.day.util.diff.ChangeListener
 *  com.day.util.diff.Document
 *  com.day.util.diff.Document$Element
 *  com.day.util.diff.DocumentDiff
 *  com.day.util.diff.DocumentSource
 *  com.day.util.diff.ElementsFactory
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.osgi.service.component.ComponentContext
 */
package com.day.cq.commons.impl;

import com.day.cq.commons.DiffService;
import com.day.cq.commons.impl.WordsElementsFactory;
import com.day.util.diff.ChangeListener;
import com.day.util.diff.Document;
import com.day.util.diff.DocumentDiff;
import com.day.util.diff.DocumentSource;
import com.day.util.diff.ElementsFactory;
import java.util.Dictionary;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.osgi.service.component.ComponentContext;

@Component(metatype=0)
@Service(value={DiffService.class})
public class DefaultDiffService
implements DiffService {
    private static final String DEFAULT_ADDED_MARKER_CSS_CLASS = "textAdded";
    private static final String DEFAULT_REMOVED_MARKER_CSS_CLASS = "textRemoved";
    private static final String DEFAULT_ADDED_IMAGE_MARKER_CSS_CLASS = "imageAdded";
    private static final String DEFAULT_REMOVED_IMAGE_MARKER_CSS_CLASS = "imageRemoved";
    private static final boolean DEFAULT_INCLUDE_IMAGE_HTML_TAG = true;
    @Property(value={"textAdded"})
    private static final String ADDED_MARKER_CSS_CLASS = "addMarkerCssClass";
    @Property(value={"textRemoved"})
    private static final String REMOVED_MARKER_CSS_CLASS = "removedMarkerCssClass";
    @Property(value={"imageAdded"})
    private static final String ADDED_IMAGE_MARKER_CSS_CLASS = "addImageMarkerCssClass";
    @Property(value={"imageRemoved"})
    private static final String REMOVED_IMAGE_MARKER_CSS_CLASS = "removedImageMarkerCssClass";
    @Property(boolValue={1})
    private static final String INCLUDE_IMAGE_HTML_TAG = "includeImageHtmlTag";
    private static final DocumentSource DOCUMENT_SOURCE = new SimpleDocumentSource("DUMMY", "DUMMY");
    private final Configuration configuration = new Configuration();

    protected void activate(ComponentContext context) {
        String removeImageMarker;
        String addImageMarker;
        String removeMarker;
        String addMarker = (String)context.getProperties().get("addMarkerCssClass");
        if (addMarker == null) {
            addMarker = "textAdded";
        }
        if ((removeMarker = (String)context.getProperties().get("removedMarkerCssClass")) == null) {
            removeMarker = "textRemoved";
        }
        if ((addImageMarker = (String)context.getProperties().get("addImageMarkerCssClass")) == null) {
            addImageMarker = "imageAdded";
        }
        if ((removeImageMarker = (String)context.getProperties().get("removedImageMarkerCssClass")) == null) {
            removeImageMarker = "imageRemoved";
        }
        this.configuration.inPostfix = "</ins>";
        this.configuration.inPrefix = addMarker.length() == 0 ? "<ins>" : "<ins class='" + addMarker + "'>";
        this.configuration.outPostfix = "</del>";
        this.configuration.outPrefix = removeMarker.length() == 0 ? "<del>" : "<del class='" + removeMarker + "'>";
        this.configuration.includeImageHtmlTag = OsgiUtil.toBoolean(context.getProperties().get("includeImageHtmlTag"), (boolean)true);
        this.configuration.imgHtmlTagPrefix = "img";
        this.configuration.imageAddedClassAttribute = " class=\"" + addImageMarker + "\"";
        this.configuration.imageRemovedClassAttribute = " class=\"" + removeImageMarker + "\"";
    }

    @Override
    public String diff(String origText, String diffText, boolean isRichText) {
        ElementsFactory origEF;
        ElementsFactory otherEF;
        String validDiffText;
        if (!this.configuration.includeImageHtmlTag) {
            if (origText == null || origText.trim().length() == 0) {
                if (diffText == null || diffText.trim().length() == 0) {
                    return "";
                }
                return this.wrapText(diffText, this.configuration.outPrefix, this.configuration.outPostfix);
            }
            if (diffText == null || diffText.trim().length() == 0) {
                return this.wrapText(origText, this.configuration.inPrefix, this.configuration.inPostfix);
            }
        }
        String validOrigText = origText != null ? origText : "";
        String string = validDiffText = diffText != null ? diffText : "";
        if (this.configuration.includeImageHtmlTag) {
            String[] htmlTagsToInclude = new String[]{this.configuration.imgHtmlTagPrefix};
            origEF = WordsElementsFactory.create(validOrigText, htmlTagsToInclude);
            otherEF = WordsElementsFactory.create(validDiffText, htmlTagsToInclude);
        } else {
            origEF = WordsElementsFactory.create(validOrigText);
            otherEF = WordsElementsFactory.create(validDiffText);
        }
        DocumentDiff diff = new DocumentDiff(new Document(DOCUMENT_SOURCE, origEF), new Document(DOCUMENT_SOURCE, otherEF));
        DiffChangeListener changeListener = new DiffChangeListener(this.configuration, origEF.getElements(), otherEF.getElements(), validOrigText, validDiffText);
        diff.showChanges((ChangeListener)changeListener, 0);
        return changeListener.getText();
    }

    private String wrapText(String text, String pre, String post) {
        if (!text.startsWith("<p>")) {
            return pre + text + post;
        }
        String replaced = text.replace("<p>", "<p>" + pre);
        replaced = replaced.replace("</p>", post + "</p>");
        return replaced;
    }

    private static final class Configuration {
        public String inPrefix = "<ins>";
        public String inPostfix = "</ins>";
        public String outPrefix = "<del>";
        public String outPostfix = "</del>";
        public boolean includeImageHtmlTag = true;
        public String imgHtmlTagPrefix = "img";
        public String imageAddedClassAttribute = " class=\"imageAdded\"";
        public String imageRemovedClassAttribute = " class=\"imageRemoved\"";

        private Configuration() {
        }
    }

    private static final class SimpleDocumentSource
    implements DocumentSource {
        private final String label;
        private final String location;

        public SimpleDocumentSource(String label, String location) {
            this.label = label;
            this.location = location;
        }

        public String getLabel() {
            return this.label;
        }

        public String getLocation() {
            return this.location;
        }
    }

    private static final class DiffChangeListener
    implements ChangeListener {
        private final Document.Element[] origElements;
        private final Document.Element[] diffElements;
        private final Configuration configuration;
        private final String diffText;
        private StringBuilder text;
        private int lastOffset;
        private boolean addedTagAtEnd = false;

        public DiffChangeListener(Configuration configuration, Document.Element[] origElements, Document.Element[] diffElements, String origText, String diffText) {
            this.origElements = origElements;
            this.diffElements = diffElements;
            this.text = new StringBuilder(origText);
            this.diffText = diffText;
            this.configuration = configuration;
            this.lastOffset = origElements.length > 0 ? origElements[origElements.length - 1].getString().length() : 0;
        }

        public String getText() {
            return this.text.toString();
        }

        private void compact(String first, String second, boolean removeIndex) {
            int pos1;
            int start = 0;
            while ((pos1 = this.text.indexOf(first, start)) != -1) {
                int end;
                int begin = pos1 + first.length();
                int wordIndex = -1;
                if (removeIndex) {
                    int endIndex = this.text.indexOf(":", begin);
                    wordIndex = Integer.valueOf(this.text.substring(begin, endIndex));
                    this.text.delete(begin, endIndex + 1);
                }
                if ((end = this.text.indexOf(second, begin)) != -1) {
                    boolean found = true;
                    for (int i = begin; i < end; ++i) {
                        char c = this.text.charAt(i);
                        if (c != '<' && c != '>' && !Character.isLetterOrDigit(c)) continue;
                        found = false;
                        break;
                    }
                    int startIndex = -1;
                    int endIndex = -1;
                    if (wordIndex != -1) {
                        WordsElementsFactory.WordElement previous = (WordsElementsFactory.WordElement)this.diffElements[wordIndex];
                        WordsElementsFactory.WordElement next = (WordsElementsFactory.WordElement)this.diffElements[wordIndex + 1];
                        startIndex = previous.getIndex() + previous.getString().length();
                        endIndex = next.getIndex();
                        for (int i2 = startIndex; i2 < endIndex; ++i2) {
                            char c = this.diffText.charAt(i2);
                            if (c != '<' && c != '>' && !Character.isLetterOrDigit(c)) continue;
                            found = false;
                            break;
                        }
                    }
                    if (found) {
                        this.text.delete(end, end + second.length());
                        this.text.delete(pos1, begin);
                        if (wordIndex == -1 || startIndex >= endIndex) continue;
                        this.text.insert(pos1, this.diffText.substring(startIndex, endIndex));
                        continue;
                    }
                    start = begin;
                    continue;
                }
                start = this.text.length();
            }
        }

        public void onDeleted(int leftIdx, int rightIdx, Document.Element elem) {
            int startPos = ((WordsElementsFactory.WordElement)this.origElements[leftIdx]).getIndex();
            String elemText = elem.getString();
            int len = elemText.length();
            int oldLen = this.text.length();
            if (this.shouldHandleImageTag(elemText)) {
                int imgTagLength = this.configuration.imgHtmlTagPrefix.length();
                this.text.insert(startPos + imgTagLength + 1, this.configuration.imageAddedClassAttribute);
            } else {
                this.text.insert(startPos + len, this.configuration.inPostfix);
                this.text.insert(startPos, this.configuration.inPrefix);
            }
            int diffLen = this.text.length() - oldLen;
            ((WordsElementsFactory.WordElement)this.origElements[leftIdx]).incrementIndex(diffLen + len);
            for (int i = leftIdx + 1; i < this.origElements.length; ++i) {
                ((WordsElementsFactory.WordElement)this.origElements[i]).incrementIndex(diffLen);
            }
        }

        private boolean shouldHandleImageTag(String elemText) {
            if (this.configuration.includeImageHtmlTag && elemText.startsWith("<" + this.configuration.imgHtmlTagPrefix) && elemText.charAt(elemText.length() - 1) == '>' && elemText.lastIndexOf(60) == 0) {
                return true;
            }
            return false;
        }

        public void onInserted(int leftIdx, int rightIdx, Document.Element elem) {
            String elemText = elem.getString();
            if (leftIdx >= this.origElements.length) {
                int pos = (this.origElements.length > 0 ? ((WordsElementsFactory.WordElement)this.origElements[this.origElements.length - 1]).getIndex() : 0) + this.lastOffset;
                int oldLen = this.text.length();
                if (this.shouldHandleImageTag(elemText)) {
                    this.text.insert(pos, elemText);
                    int imgTagLength = this.configuration.imgHtmlTagPrefix.length();
                    this.text.insert(pos + imgTagLength + 1, this.configuration.imageRemovedClassAttribute);
                } else {
                    this.text.insert(pos, ':');
                    this.text.insert(pos, rightIdx);
                    this.text.insert(pos, this.configuration.outPostfix);
                    this.text.insert(pos, elem.getString());
                    this.text.insert(pos, this.configuration.outPrefix);
                }
                if (!this.addedTagAtEnd) {
                    this.text.insert(pos, ' ');
                    this.addedTagAtEnd = true;
                }
                this.lastOffset += this.text.length() - oldLen;
                return;
            }
            int startPos = ((WordsElementsFactory.WordElement)this.origElements[leftIdx]).getIndex();
            int oldLen = this.text.length();
            if (this.shouldHandleImageTag(elemText)) {
                this.text.insert(startPos, elemText);
                int imgTagLength = this.configuration.imgHtmlTagPrefix.length();
                this.text.insert(startPos + imgTagLength + 1, this.configuration.imageRemovedClassAttribute);
            } else {
                this.text.insert(startPos, ':');
                this.text.insert(startPos, rightIdx);
                this.text.insert(startPos, this.configuration.outPostfix);
                this.text.insert(startPos, elem.getString());
                this.text.insert(startPos, this.configuration.outPrefix);
            }
            int diffLen = this.text.length() - oldLen;
            for (int i = leftIdx; i < this.origElements.length; ++i) {
                ((WordsElementsFactory.WordElement)this.origElements[i]).incrementIndex(diffLen);
            }
        }

        public void onDocumentsEnd(Document left, Document right) {
            this.compact(this.configuration.inPostfix, this.configuration.inPrefix, false);
            this.compact(this.configuration.outPostfix, this.configuration.outPrefix, true);
        }

        public void onDocumentsStart(Document left, Document right) {
        }

        public void onChangeEnd() {
        }

        public void onChangeStart(int leftElem, int leftLen, int rightElem, int rightLen) {
        }

        public void onUnmodified(int leftIdx, int rightIdx, Document.Element elem) {
        }
    }

}