org.nuxeo.ecm.platform.annotations.gwt.client.view.AnnotatedDocument.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.platform.annotations.gwt.client.view.AnnotatedDocument.java

Source

/*
 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Alexandre Russel
 *
 * $Id$
 */

package org.nuxeo.ecm.platform.annotations.gwt.client.view;

import java.util.ArrayList;
import java.util.List;

import org.nuxeo.ecm.platform.annotations.gwt.client.AnnotationConstant;
import org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController;
import org.nuxeo.ecm.platform.annotations.gwt.client.model.Annotation;
import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationChangeListener;
import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationModel;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.CSSClassManager;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.ImageRangeXPointer;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.NullRangeXPointer;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.Point;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.StringRangeXPointer;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.Utils;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.Visitor;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.XPathUtil;
import org.nuxeo.ecm.platform.annotations.gwt.client.util.XPointer;
import org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator.DecoratorVisitor;
import org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator.DecoratorVisitorFactory;
import org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator.ImageDecorator;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.Window;

/**
 * @author <a href="mailto:arussel@nuxeo.com">Alexandre Russel</a>
 */
public class AnnotatedDocument implements AnnotationChangeListener {
    private List<Annotation> annotations = new ArrayList<Annotation>();

    private List<Annotation> decoratedAnnotations = new ArrayList<Annotation>();

    private static XPathUtil xPathUtil = new XPathUtil();

    private final ImageDecorator decorator;

    private AnnotationController controller;

    public AnnotatedDocument(AnnotationController controller) {
        this.controller = controller;
        decorator = new ImageDecorator(controller);
    }

    public void onChange(AnnotationModel model, ChangeEvent ce) {
        annotations = model.getAnnotations();
        Log.debug("On change: annotations.empty? " + annotations.isEmpty());
        if (annotations.isEmpty() || ce == ChangeEvent.annotation) {
            return;
        }

        update();
    }

    public void update() {
        update(false);
    }

    public void update(boolean forceDecorate) {
        Log.debug("Update annotations - forceDecorate: " + forceDecorate);
        if (annotations == null) {
            return;
        }

        if (forceDecorate) {
            decoratedAnnotations.clear();
            removeAllAnnotatedAreas();
        }

        for (Annotation annotation : annotations) {
            if (!decoratedAnnotations.contains(annotation)) {
                Log.debug("Decorate annotation");
                decorate(annotation);
                decoratedAnnotations.add(annotation);
            }
        }

        int selectedAnnotationIndex = getSelectedAnnotationIndex();
        if (selectedAnnotationIndex > -1) {
            updateSelectedAnnotation(selectedAnnotationIndex);
        }

        if (!isAnnotationsVisible()) {
            Log.debug("Hide annotations!");
            hideAnnotations();
            // disable popup listeners in case we just added a new annotation
            controller.disablePopupListeners();
        }
    }

    public void preDecorateDocument() {
        Document document = Document.get();
        Log.debug("preDecorateDocument -- isMultiImage? " + controller.isMultiImage());
        preDecorateDocument(document);
    }

    private static void preDecorateDocument(Document document) {
        Log.debug("Predecorate document !");
        NodeList<Element> elements = document.getElementsByTagName("img");
        for (int x = 0; x < elements.getLength(); x++) {
            Element element = elements.getItem(x);
            DivElement divElement = document.createDivElement();
            divElement.getStyle().setProperty("position", "relative");
            divElement.setClassName(AnnotationConstant.IGNORED_ELEMENT);
            String path = xPathUtil.getXPath(element);
            path = XPathUtil.toIdableName(path);
            divElement.setId(path);
            Element nextSibling = element.getNextSiblingElement();
            Element parent = element.getParentElement();
            if (nextSibling == null) {
                parent.appendChild(divElement);
            } else {
                parent.insertBefore(divElement, nextSibling);
            }
            divElement.appendChild(element);
        }
    }

    public void decorate(Annotation annotation) {
        XPointer xpointer = annotation.getXpointer();
        if (xpointer instanceof StringRangeXPointer) {
            decorateStringRange((StringRangeXPointer) xpointer, annotation);
        } else if (xpointer instanceof ImageRangeXPointer) {
            decorateImageRange((ImageRangeXPointer) xpointer, annotation);
        }
    }

    private void decorateImageRange(ImageRangeXPointer xpointer, Annotation annotation) {
        ImageElement img = xpointer.getImage(controller.isMultiImage());
        if (img == null) {
            return;
        }
        Point[] points = controller.filterAnnotation(xpointer.getTopLeft(), xpointer.getBottomRight());
        if (points == null) {
            return;
        }
        decorator.addAnnotatedArea(points[0].getX(), points[0].getY(), points[1].getX(), points[1].getY(), img,
                annotation, controller);
    }

    private void decorateStringRange(StringRangeXPointer xpointer, Annotation annotation) {
        DecoratorVisitor processor = DecoratorVisitorFactory.forAnnotation(annotation, controller);
        Visitor visitor = new Visitor(processor);
        visitor.process(xpointer.getOwnerDocument());
    }

    public void updateSelectedAnnotation(int index) {
        Annotation annotation = annotations.get(index);
        BodyElement bodyElement = Document.get().getBody();
        if (!(annotation.getXpointer() instanceof NullRangeXPointer)) {
            NodeList<Element> spans = bodyElement.getElementsByTagName("span");
            NodeList<Element> as = bodyElement.getElementsByTagName("div");
            int scrollTop = Integer.MAX_VALUE;
            int scrollLeft = Integer.MAX_VALUE;
            for (int x = 0; x < spans.getLength(); x++) {
                Element element = spans.getItem(x);
                if (processElement(annotation, element)) {
                    int[] absTopLeft = Utils.getAbsoluteTopLeft(element, Document.get());
                    if (absTopLeft[0] < scrollTop) {
                        scrollTop = absTopLeft[0];
                    }
                    if (absTopLeft[1] < scrollLeft) {
                        scrollLeft = absTopLeft[1];
                    }
                }
            }
            for (int x = 0; x < as.getLength(); x++) {
                Element element = as.getItem(x);
                if (processElement(annotation, element)) {
                    int[] absTopLeft = Utils.getAbsoluteTopLeft(element, Document.get());
                    if (absTopLeft[0] < scrollTop) {
                        scrollTop = absTopLeft[0];
                    }
                    if (absTopLeft[1] < scrollLeft) {
                        scrollLeft = absTopLeft[1];
                    }
                }
            }

            scrollLeft = scrollLeft == Integer.MAX_VALUE ? 0 : scrollLeft;
            scrollTop = scrollTop == Integer.MAX_VALUE ? 0 : scrollTop;
            Window.scrollTo(scrollLeft, scrollTop);
        }
    }

    private boolean processElement(Annotation annotation, Element element) {
        CSSClassManager manager = new CSSClassManager(element);
        // remove old
        manager.removeClass(AnnotationConstant.SELECTED_CLASS_NAME);
        // set new
        if (manager.isClassPresent(AnnotationConstant.DECORATE_CLASS_NAME + annotation.getId())) {
            manager.addClass(AnnotationConstant.SELECTED_CLASS_NAME);

            return true;
        }
        return false;
    }

    private native int getSelectedAnnotationIndex() /*-{
                                                    if (top && typeof top['selectedAnnotationIndex'] != "undefined") {
                                                    return top['selectedAnnotationIndex'];
                                                    } else {
                                                    return -1;
                                                    }
                                                    }-*/;

    public void hideAnnotations() {
        BodyElement bodyElement = Document.get().getBody();
        NodeList<Element> spans = bodyElement.getElementsByTagName("span");
        NodeList<Element> divs = bodyElement.getElementsByTagName("div");

        for (int x = 0; x < spans.getLength(); x++) {
            Element element = spans.getItem(x);
            CSSClassManager manager = new CSSClassManager(element);
            if (manager.isClassPresent(AnnotationConstant.DECORATE_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.DECORATE_CLASS_NAME);
                manager.addClass(AnnotationConstant.DECORATE_NOT_CLASS_NAME);
            }
        }

        for (int x = 0; x < divs.getLength(); x++) {
            Element element = divs.getItem(x);
            CSSClassManager manager = new CSSClassManager(element);
            if (manager.isClassPresent(AnnotationConstant.DECORATE_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.DECORATE_CLASS_NAME);
                manager.addClass(AnnotationConstant.DECORATE_NOT_CLASS_NAME);
            }
        }
        setAnnotationsShown(false);
    }

    private native void setAnnotationsShown(boolean annotationsShown) /*-{
                                                                      top['annotationsShown'] = annotationsShown;
                                                                      }-*/;

    public void showAnnotations() {
        BodyElement bodyElement = Document.get().getBody();
        NodeList<Element> spans = bodyElement.getElementsByTagName("span");
        NodeList<Element> divs = bodyElement.getElementsByTagName("div");

        for (int x = 0; x < spans.getLength(); x++) {
            Element element = spans.getItem(x);
            CSSClassManager manager = new CSSClassManager(element);
            if (manager.isClassPresent(AnnotationConstant.DECORATE_NOT_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.DECORATE_NOT_CLASS_NAME);
                manager.addClass(AnnotationConstant.DECORATE_CLASS_NAME);
            }
            if (manager.isClassPresent(AnnotationConstant.SELECTED_NOT_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.SELECTED_NOT_CLASS_NAME);
                manager.addClass(AnnotationConstant.SELECTED_CLASS_NAME);
            }
        }

        for (int x = 0; x < divs.getLength(); x++) {
            Element element = divs.getItem(x);
            CSSClassManager manager = new CSSClassManager(element);
            if (manager.isClassPresent(AnnotationConstant.DECORATE_NOT_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.DECORATE_NOT_CLASS_NAME);
                manager.addClass(AnnotationConstant.DECORATE_CLASS_NAME);
            }
            if (manager.isClassPresent(AnnotationConstant.SELECTED_NOT_CLASS_NAME)) {
                manager.removeClass(AnnotationConstant.SELECTED_NOT_CLASS_NAME);
                manager.addClass(AnnotationConstant.SELECTED_CLASS_NAME);
            }
        }
        setAnnotationsShown(true);
    }

    public native boolean isAnnotationsVisible() /*-{
                                                 if (top && typeof top['annotationsShown'] != "undefined") {
                                                 return top['annotationsShown'];
                                                 } else {
                                                 return true;
                                                 }
                                                 }-*/;

    private void removeAllAnnotatedAreas() {
        String className = isAnnotationsVisible() ? AnnotationConstant.DECORATE_CLASS_NAME
                : AnnotationConstant.DECORATE_NOT_CLASS_NAME;
        BodyElement bodyElement = Document.get().getBody();
        NodeList<Element> as = bodyElement.getElementsByTagName("div");
        removeAnchorAreas(as, className);
        removeSpanAreas(className);
    }

    private void removeAnchorAreas(NodeList<Element> nodes, String className) {
        List<Element> elements = getElementsToRemove(nodes, className);
        for (Element element : elements) {
            element.getParentElement().removeChild(element);
        }
    }

    private List<Element> getElementsToRemove(NodeList<Element> nodes, String className) {
        List<Element> elementsToRemove = new ArrayList<Element>();
        for (int i = 0; i < nodes.getLength(); ++i) {
            Element element = nodes.getItem(i);
            CSSClassManager manager = new CSSClassManager(element);
            if (manager.isClassPresent(className)) {
                elementsToRemove.add(element);
            }
        }
        return elementsToRemove;
    }

    private void removeSpanAreas(String className) {
        NodeList<Element> spans = Document.get().getBody().getElementsByTagName("span");
        List<Element> elements = getElementsToRemove(spans, className);
        while (!elements.isEmpty()) {
            Element element = elements.get(0);
            String elementHtml = element.getInnerHTML();
            Element parent = element.getParentElement();
            String parentHtml = parent.getInnerHTML();

            String escapedClassName = element.getClassName()
                    .replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}$^])", "\\\\$1");
            String escapedElementHtml = elementHtml.replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}$^])",
                    "\\\\$1");

            parentHtml = parentHtml.replaceFirst("<(span|SPAN) class=(\")?" + escapedClassName + "(\")?.*>"
                    + escapedElementHtml + "</(span|SPAN)>", elementHtml);
            parent.setInnerHTML(parentHtml);

            spans = Document.get().getBody().getElementsByTagName("span");
            elements = getElementsToRemove(spans, className);
        }
    }

    public void decorateSelectedText(Annotation annotation) {
        DecoratorVisitor processor = DecoratorVisitorFactory.forSelectedText(annotation);
        Visitor visitor = new Visitor(processor);
        StringRangeXPointer xpointer = (StringRangeXPointer) annotation.getXpointer();
        visitor.process(xpointer.getOwnerDocument());
    }

    public void removeSelectedTextDecoration(Annotation annotation) {
        String className = AnnotationConstant.SELECTED_TEXT_CLASS_NAME;
        removeSpanAreas(className);
    }

}