org.eclipse.ltk.internal.ui.refactoring.CompilationUnitChangeNode.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ltk.internal.ui.refactoring.CompilationUnitChangeNode.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2015 Codenvy, S.A.
 * 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
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.ltk.internal.ui.refactoring;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.ltk.core.refactoring.MultiStateTextFileChange;
import org.eclipse.ltk.core.refactoring.TextEditBasedChange;
import org.eclipse.ltk.core.refactoring.TextEditBasedChangeGroup;
import org.eclipse.ltk.ui.refactoring.LanguageElementNode;
import org.eclipse.ltk.ui.refactoring.TextEditChangeNode;
import org.eclipse.text.edits.TextEdit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CompilationUnitChangeNode extends TextEditChangeNode {

    static final ChildNode[] EMPTY_CHILDREN = new ChildNode[0];

    private static class JavaLanguageNode extends LanguageElementNode {

        private IJavaElement fJavaElement;
        private static JavaElementImageProvider fgImageProvider = new JavaElementImageProvider();

        public JavaLanguageNode(TextEditChangeNode parent, IJavaElement element) {
            super(parent);
            fJavaElement = element;
            Assert.isNotNull(fJavaElement);
        }

        public JavaLanguageNode(ChildNode parent, IJavaElement element) {
            super(parent);
            fJavaElement = element;
            Assert.isNotNull(fJavaElement);
        }

        @Override
        public String getText() {
            return JavaElementLabels.getElementLabel(fJavaElement, JavaElementLabels.ALL_DEFAULT);
        }

        @Override
        public ImageDescriptor getImageDescriptor() {
            return fgImageProvider.getJavaImageDescriptor(fJavaElement,
                    JavaElementImageProvider.OVERLAY_ICONS | JavaElementImageProvider.SMALL_ICONS);
        }

        @Override
        public IRegion getTextRange() throws CoreException {
            ISourceRange range = ((ISourceReference) fJavaElement).getSourceRange();
            return new Region(range.getOffset(), range.getLength());
        }
    }

    public CompilationUnitChangeNode(TextEditBasedChange change) {
        super(change);
    }

    @Override
    protected ChildNode[] createChildNodes() {
        TextEditBasedChange change = getTextEditBasedChange();
        if (change instanceof MultiStateTextFileChange) {
            return new ChildNode[0]; // no edit preview & edit disabling possible in the MultiStateTextFileChange (edits must be applied
            // in sequence)
        }

        ICompilationUnit cunit = (ICompilationUnit) change.getAdapter(ICompilationUnit.class);
        if (cunit != null) {
            List<ChildNode> children = new ArrayList<ChildNode>(5);
            Map<IJavaElement, JavaLanguageNode> map = new HashMap<IJavaElement, JavaLanguageNode>(20);
            TextEditBasedChangeGroup[] changes = getSortedChangeGroups(change);
            for (int i = 0; i < changes.length; i++) {
                TextEditBasedChangeGroup tec = changes[i];
                try {
                    IJavaElement element = getModifiedJavaElement(tec, cunit);
                    if (element.equals(cunit)) {
                        children.add(createTextEditGroupNode(this, tec));
                    } else {
                        JavaLanguageNode pjce = getChangeElement(map, element, children, this);
                        pjce.addChild(createTextEditGroupNode(pjce, tec));
                    }
                } catch (JavaModelException e) {
                    children.add(createTextEditGroupNode(this, tec));
                }
            }
            return children.toArray(new ChildNode[children.size()]);
        } else {
            return EMPTY_CHILDREN;
        }
    }

    private static class OffsetComparator implements Comparator<TextEditBasedChangeGroup> {
        public int compare(TextEditBasedChangeGroup c1, TextEditBasedChangeGroup c2) {
            int p1 = getOffset(c1);
            int p2 = getOffset(c2);
            if (p1 < p2)
                return -1;
            if (p1 > p2)
                return 1;
            // same offset
            return 0;
        }

        private int getOffset(TextEditBasedChangeGroup edit) {
            return edit.getRegion().getOffset();
        }
    }

    private TextEditBasedChangeGroup[] getSortedChangeGroups(TextEditBasedChange change) {
        TextEditBasedChangeGroup[] edits = change.getChangeGroups();
        List<TextEditBasedChangeGroup> result = new ArrayList<TextEditBasedChangeGroup>(edits.length);
        for (int i = 0; i < edits.length; i++) {
            if (!edits[i].getTextEditGroup().isEmpty())
                result.add(edits[i]);
        }
        Comparator<TextEditBasedChangeGroup> comparator = new OffsetComparator();
        Collections.sort(result, comparator);
        return result.toArray(new TextEditBasedChangeGroup[result.size()]);
    }

    private IJavaElement getModifiedJavaElement(TextEditBasedChangeGroup edit, ICompilationUnit cunit)
            throws JavaModelException {
        IRegion range = edit.getRegion();
        if (range.getOffset() == 0 && range.getLength() == 0)
            return cunit;
        IJavaElement result = cunit.getElementAt(range.getOffset());
        if (result == null)
            return cunit;

        try {
            while (true) {
                ISourceReference ref = (ISourceReference) result;
                IRegion sRange = new Region(ref.getSourceRange().getOffset(), ref.getSourceRange().getLength());
                if (result.getElementType() == IJavaElement.COMPILATION_UNIT || result.getParent() == null
                        || coveredBy(edit, sRange))
                    break;
                result = result.getParent();
            }
        } catch (JavaModelException e) {
            // Do nothing, use old value.
        } catch (ClassCastException e) {
            // Do nothing, use old value.
        }
        return result;
    }

    private JavaLanguageNode getChangeElement(Map<IJavaElement, JavaLanguageNode> map, IJavaElement element,
            List<ChildNode> children, TextEditChangeNode cunitChange) {
        JavaLanguageNode result = map.get(element);
        if (result != null)
            return result;
        IJavaElement parent = element.getParent();
        if (parent instanceof ICompilationUnit) {
            result = new JavaLanguageNode(cunitChange, element);
            children.add(result);
            map.put(element, result);
        } else {
            JavaLanguageNode parentChange = getChangeElement(map, parent, children, cunitChange);
            result = new JavaLanguageNode(parentChange, element);
            parentChange.addChild(result);
            map.put(element, result);
        }
        return result;
    }

    private boolean coveredBy(TextEditBasedChangeGroup group, IRegion sourceRegion) {
        int sLength = sourceRegion.getLength();
        if (sLength == 0)
            return false;
        int sOffset = sourceRegion.getOffset();
        int sEnd = sOffset + sLength - 1;
        TextEdit[] edits = group.getTextEdits();
        for (int i = 0; i < edits.length; i++) {
            TextEdit edit = edits[i];
            if (edit.isDeleted())
                return false;
            int rOffset = edit.getOffset();
            int rLength = edit.getLength();
            int rEnd = rOffset + rLength - 1;
            if (rLength == 0) {
                if (!(sOffset < rOffset && rOffset <= sEnd))
                    return false;
            } else {
                if (!(sOffset <= rOffset && rEnd <= sEnd))
                    return false;
            }
        }
        return true;
    }
}