Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 4b473879fbf0c91f9e4018858100e508a5cdfd1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*******************************************************************************
 * Copyright (c) 2008-2011 Sonatype, Inc.
 * 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:
 *      Sonatype, Inc. - initial API and implementation
 *******************************************************************************/

package org.eclipse.m2e.core.ui.internal.editing;

import java.util.ArrayList;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.compare.rangedifferencer.RangeDifference;
import org.eclipse.compare.rangedifferencer.RangeDifferencer;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ltk.core.refactoring.DocumentChange;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;


/**
 * This class creates an org.eclipse.ltk.core.refactoring.DocumentChange instance based on old and new text values
 * 
 * @author Anton Kraev
 */
public class ChangeCreator {
  private static final Logger log = LoggerFactory.getLogger(ChangeCreator.class);

  private String label;

  private IDocument oldDocument;

  private IDocument newDocument;

  private IFile oldFile;

  public ChangeCreator(IFile oldFile, IDocument oldDocument, IDocument newDocument, String label) {
    this.newDocument = newDocument;
    this.oldDocument = oldDocument;
    this.oldFile = oldFile;
    this.label = label;
  }

  public TextChange createChange() throws Exception {
    TextChange change = oldFile == null ? new DocumentChange(label, oldDocument) : new TextFileChange(label, oldFile);
    // change.setSaveMode(TextFileChange.FORCE_SAVE);
    change.setEdit(new MultiTextEdit());
    Object leftSide = new LineComparator(oldDocument);
    Object rightSide = new LineComparator(newDocument);

    RangeDifference[] differences = RangeDifferencer.findDifferences((IRangeComparator) leftSide,
        (IRangeComparator) rightSide);
    for(int i = 0; i < differences.length; i++ ) {
      RangeDifference curr = differences[i];
      if(curr.leftLength() == 0 && curr.rightLength() == 0)
        continue;

      int rightOffset = newDocument.getLineOffset(curr.rightStart());
      int rightLength = curr.rightLength() == 0 ? 0 : newDocument.getLineOffset(curr.rightEnd() - 1) - rightOffset
          + newDocument.getLineLength(curr.rightEnd() - 1);

      int leftOffset = oldDocument.getLineOffset(curr.leftStart());
      int leftLength = curr.leftLength() == 0 ? 0 : oldDocument.getLineOffset(curr.leftEnd() - 1) - leftOffset
          + oldDocument.getLineLength(curr.leftEnd() - 1);

      String newText = newDocument.get(rightOffset, rightLength);
      addEdit(change, curr.leftStart(), new ReplaceEdit(leftOffset, leftLength, newText));
    }
    return change;
  }

  private void addEdit(TextChange change, int startLine, TextEdit edit) {
    change.addTextEditGroup(new TextEditGroup("Line " + (startLine + 1), edit)); //$NON-NLS-1$
    change.addEdit(edit);
  }

  public static class LineComparator implements IRangeComparator {
    private final IDocument document;

    private final ArrayList<Integer> hashes;

    /**
     * Create a line comparator for the given document.
     * 
     * @param document
     */
    public LineComparator(IDocument document) {
      this.document = document;
      this.hashes = new ArrayList<Integer>(Arrays.asList(new Integer[document.getNumberOfLines()]));
    }

    /*
     * @see org.eclipse.compare.rangedifferencer.IRangeComparator#getRangeCount()
     */
    public int getRangeCount() {
      return document.getNumberOfLines();
    }

    /*
     * @see org.eclipse.compare.rangedifferencer.IRangeComparator#rangesEqual(int, org.eclipse.compare.rangedifferencer.IRangeComparator, int)
     */
    public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) {
      try {
        return getHash(thisIndex).equals(((LineComparator) other).getHash(otherIndex));
      } catch(BadLocationException e) {
        log.error("Problem comparing", e);
        return false;
      }
    }

    /*
     * @see org.eclipse.compare.rangedifferencer.IRangeComparator#skipRangeComparison(int, int, org.eclipse.compare.rangedifferencer.IRangeComparator)
     */
    public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) {
      return false;
    }

    /**
     * @param line the number of the line in the document to get the hash for
     * @return the hash of the line
     * @throws BadLocationException if the line number is invalid
     */
    private Integer getHash(int line) throws BadLocationException {
      Integer hash = hashes.get(line);
      if(hash == null) {
        IRegion lineRegion;
        lineRegion = document.getLineInformation(line);
        String lineContents = document.get(lineRegion.getOffset(), lineRegion.getLength());
        hash = new Integer(computeDJBHash(lineContents));
        hashes.set(line, hash);
      }
      return hash;
    }

    /**
     * Compute a hash using the DJB hash algorithm
     * 
     * @param string the string for which to compute a hash
     * @return the DJB hash value of the string
     */
    private int computeDJBHash(String string) {
      int hash = 5381;
      int len = string.length();
      for(int i = 0; i < len; i++ ) {
        hash = (hash << 5) + hash + string.charAt(i);
      }
      return hash;
    }
  }

}

Back to the top