Skip to main content
summaryrefslogtreecommitdiffstats
blob: 29fa1f82a84a4ec04b8ac06ac93528a7b55c8d31 (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
/*******************************************************************************
 * Copyright (c) 2005, 2015 IBM Corporation 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
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;

import org.eclipse.core.runtime.Assert;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;

/**
 * @since 3.2
 */
public final class DocumentEquivalenceClass {

	private static final boolean DEBUG= false;

	private final ArrayList<Hash> fHashes;
	private IDocument fDocument;
	private final IHashFunction fHashFunction;

	public DocumentEquivalenceClass(IDocument document) {
		this(document, new DJBHashFunction());
	}

	public DocumentEquivalenceClass(IDocument document, IHashFunction hashFunction) {
		fDocument= document;
		Hash[] nulls= new Hash[fDocument.getNumberOfLines()];
		fHashes= new ArrayList<>(Arrays.asList(nulls));

		if (hashFunction == null)
			throw new NullPointerException("hashFunction"); //$NON-NLS-1$
		fHashFunction= hashFunction;
	}

	/**
	 * Returns the equivalence hash for line <code>line</code>.
	 *
	 * @param line the line for which to get the equivalent hash
	 * @return the hash in the equivalence class defined by the hash
	 *         function
	 * @throws IndexOutOfBoundsException if <code>line</code> is not a
	 *         legal document line
	 * @throws ConcurrentModificationException if the document is
	 *         modified concurrently to this method call
	 */
	public Hash getHash(int line) {
		try {
			return internalGetHash(line);
		} catch (BadLocationException x) {
			throw new ConcurrentModificationException();
		}
	}

	private Hash internalGetHash(int line) throws BadLocationException {
		Hash hash= fHashes.get(line);
		if (hash == null) {
			if (fDocument == null)
				throw new AssertionError("hash cannot be null after loadAndForget"); //$NON-NLS-1$

			IRegion lineRegion= fDocument.getLineInformation(line);
			String lineContents= fDocument.get(lineRegion.getOffset(), lineRegion.getLength());
			hash= fHashFunction.computeHash(lineContents);
			fHashes.set(line, hash);
		}

		return hash;
	}

	/**
	 * Cleanses the lines affected by the document event from the
	 * internal hash cache. Must be called before the document is
	 * modified (in documentAboutToBeChanged).
	 *
	 * @param event the document event
	 */
	public void update(DocumentEvent event) {
		if (fDocument == null)
			throw new IllegalStateException("update must not be called after loadAndForget"); //$NON-NLS-1$
		try {
			internalUpdate(event);
		} catch (BadLocationException x) {
			throw new ConcurrentModificationException();
		}
	}

	private void internalUpdate(DocumentEvent event) throws BadLocationException {
		int linesBefore= fDocument.getNumberOfLines(event.getOffset(), event.getLength());
		String text= event.getText();
		int linesAfter= (text == null ? 0 : fDocument.computeNumberOfLines(text)) + 1;
		int firstLine= fDocument.getLineOfOffset(event.getOffset());

		int delta= linesAfter - linesBefore;
		int changed= Math.min(linesAfter, linesBefore);

		if (delta > 0) {
			Hash[] nulls= new Hash[delta];
			fHashes.addAll(firstLine + changed, Arrays.asList(nulls));
		} else if (delta < 0) {
			fHashes.subList(firstLine, firstLine - delta).clear();
		}
		Collections.fill(fHashes.subList(firstLine, firstLine + changed), null);
	}

	/**
	 * @return the number of items
	 */
	public int getCount() {
		return fHashes.size();
	}

	public void setDocument(IDocument document) {
		Assert.isNotNull(document);
		if (DEBUG)
			Assert.isTrue(document.get().equals(fDocument.get()));
		fDocument= document;
	}

	/**
	 * Computes all hashes and forgets the document. Don't call update
	 * afterwards.
	 */
	public void loadAndForget() {
		int count= getCount();
		for (int line= 0; line < count; line++)
			getHash(line);

		fDocument= null;
	}
}

Back to the top