org.eclipse.xtext.ui.editor.model.edit.DefaultTextEditComposer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.ui.editor.model.edit.DefaultTextEditComposer.java

Source

/*******************************************************************************
 * Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.xtext.ui.editor.model.edit;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.SaveOptions;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.util.ReplaceRegion;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;

/**
 * @author Knut Wannheden - Initial contribution and API
 * @author Jan Koehnlein
 */
public class DefaultTextEditComposer extends EContentAdapter implements ITextEditComposer {

    @Inject
    private ISerializer serializer;

    private Resource resource;
    private int resourceSize;
    private boolean resourceChanged;

    private Collection<EObject> modifiedObjects = new LinkedHashSet<EObject>();

    private boolean recording = false;

    @Override
    public void notifyChanged(Notification notification) {
        super.notifyChanged(notification);

        if (!doRecord(notification))
            return;

        if (notification.getNotifier() instanceof EObject) {
            recordObjectModification((EObject) notification.getNotifier());
        } else if (notification.getNotifier() instanceof Resource) {
            recordResourceModification((Resource) notification.getNotifier());
        }
    }

    protected void recordObjectModification(EObject obj) {
        if (obj.eResource() == null || obj.eResource() != resource)
            getModifiedObjects().remove(obj);
        else
            getModifiedObjects().add(obj);
    }

    protected void recordResourceModification(Resource notifier) {
        resourceChanged = true;
    }

    protected Collection<EObject> getModifiedObjects() {
        return modifiedObjects;
    }

    protected boolean doRecord(Notification notification) {
        if (!recording || notification.isTouch())
            return false;

        switch (notification.getEventType()) {
        case Notification.ADD:
        case Notification.ADD_MANY:
        case Notification.MOVE:
        case Notification.REMOVE:
        case Notification.REMOVE_MANY:
        case Notification.SET:
        case Notification.UNSET:
            return true;
        default:
            return false;
        }
    }

    protected void reset() {
        getModifiedObjects().clear();
        resourceChanged = false;
    }

    @Override
    public void beginRecording(Resource newResource) {
        reset();

        if (newResource != resource) {
            if (resource != null)
                resource.eAdapters().remove(this);
            newResource.eAdapters().add(this);
            resource = newResource;
        }
        if (resource.getContents().isEmpty()) {
            resourceSize = 0;
        } else {
            final EObject root = resource.getContents().get(0);
            ICompositeNode rootNode = NodeModelUtils.getNode(root);
            if (rootNode == null) {
                throw new IllegalStateException("Cannot find root node in resource " + resource.getURI());
            }
            resourceSize = rootNode.getTotalLength();
        }
        recording = true;
    }

    @Override
    public TextEdit endRecording() {
        recording = false;
        TextEdit textEdit = getTextEdit();

        reset();
        return textEdit;
    }

    @Override
    public TextEdit getTextEdit() {
        TextEdit result = null;

        if (resourceChanged) {
            String text = resource.getContents().isEmpty() ? ""
                    : serializer.serialize(resource.getContents().get(0));
            result = new ReplaceEdit(0, resourceSize, text);
        } else {
            final Collection<EObject> modifiedObjects = getModifiedObjects();
            if (!modifiedObjects.isEmpty()) {
                List<TextEdit> edits = getObjectEdits();
                if (edits.size() == 1)
                    result = edits.get(0);
                else {
                    result = new MultiTextEdit();
                    for (TextEdit edit : edits) {
                        result.addChild(edit);
                    }
                }
            }
        }

        return result;
    }

    protected List<TextEdit> getObjectEdits() {
        final Collection<EObject> modifiedObjects = getModifiedObjects();
        Collection<EObject> topLevelObjects = EcoreUtil.filterDescendants(modifiedObjects);
        Iterable<EObject> containedModifiedObjects = Collections.emptyList();
        if (!resource.getContents().isEmpty()) {
            final EObject root = resource.getContents().get(0);
            containedModifiedObjects = Iterables.filter(topLevelObjects, new Predicate<EObject>() {
                @Override
                public boolean apply(EObject input) {
                    return EcoreUtil.isAncestor(root, input);
                }
            });
        }
        List<TextEdit> edits = Lists.newArrayListWithExpectedSize(Iterables.size(containedModifiedObjects));
        for (EObject modifiedObject : containedModifiedObjects) {
            ReplaceRegion replaceRegion = serializer.serializeReplacement(modifiedObject, getSaveOptions());
            TextEdit edit = new ReplaceEdit(replaceRegion.getOffset(), replaceRegion.getLength(),
                    replaceRegion.getText());
            edits.add(edit);
        }
        return edits;
    }

    protected SaveOptions getSaveOptions() {
        return SaveOptions.defaultOptions();
    }

    /**
     * @deprecated use {@link #setSerializer(ISerializer)} instead.
     */
    @Deprecated
    public void setSerializerUtil(ISerializer serializer) {
        setSerializer(serializer);
    }

    /**
     * If used in a non-Guice environment, we need to be able to set this.
     * @since 2.0
     */
    public void setSerializer(ISerializer serializer) {
        this.serializer = serializer;
    }
}