/*=============================================================================#
# Copyright (c) 2005-2016 Stephan Wahlbrink (WalWare.de) 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.ecommons.text;
import static de.walware.ecommons.text.ITokenScanner.CLOSING_PEER;
import static de.walware.ecommons.text.ITokenScanner.OPENING_PEER;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import de.walware.ecommons.text.core.IPartitionConstraint;
import de.walware.ecommons.text.core.sections.IDocContentSections;
/**
* Helper class for match pairs of characters.
*/
public class PairMatcher implements ICharPairMatcher {
private static final char IGNORE = '\n';
private static final byte NOTHING_FOUND = 0;
private static final byte OPENING_NOT_FOUND = 1;
private static final byte CLOSING_NOT_FOUND = 2;
private static final byte PAIR_FOUND = 3;
protected final char[][] fPairs;
protected final String fPartitioning;
protected final String[] fApplicablePartitions;
protected final char fEscapeChar;
protected final ITokenScanner fScanner;
protected int fOffset;
protected int fBeginPos;
protected int fEndPos;
protected int fAnchor;
protected String fPartition;
public PairMatcher(final char[][] pairs, final String partitioning,
final String[] partitions, final ITokenScanner scanner, final char escapeChar) {
fPairs = pairs;
fScanner = scanner;
fPartitioning = partitioning;
fApplicablePartitions = partitions;
fEscapeChar = escapeChar;
}
/**
* Constructor using <code>BasicHeuristicTokenScanner</code>.
*/
public PairMatcher(final char[][] pairs, final IDocContentSections documentContentInfo,
final String[] partitions, final char escapeChar) {
this(pairs, documentContentInfo.getPartitioning(), partitions,
new BasicHeuristicTokenScanner(documentContentInfo, new IPartitionConstraint() {
@Override
public boolean matches(final String partitionType) {
for (int i= 0; i < partitions.length; i++) {
if (partitions[i] == partitionType) {
return true;
}
}
return false;
}
}),
escapeChar );
}
/**
* @return Returns the fPairs.
*/
public char[][] getPairs(final IDocument document, final int offset) {
return fPairs;
}
@Override
public IRegion match(final IDocument document, final int offset) {
if (document == null || offset < 0) {
return null;
}
fOffset = offset;
if (matchPairsAt(document, true) == PAIR_FOUND) {
return new Region(fBeginPos, fEndPos - fBeginPos + 1);
}
else {
return null;
}
}
@Override
public IRegion match(final IDocument document, final int offset, final boolean auto) {
if (document == null || offset < 0) {
return null;
}
fOffset = offset;
switch (matchPairsAt(document, auto)) {
case OPENING_NOT_FOUND:
return new Region(fEndPos, -1);
case CLOSING_NOT_FOUND:
return new Region(fBeginPos, -1);
case PAIR_FOUND:
return new Region(fBeginPos, fEndPos - fBeginPos + 1);
default:
return null;
}
}
@Override
public int getAnchor() {
return fAnchor;
}
@Override
public void dispose() {
clear();
}
@Override
public void clear() {
}
/**
* Search Pairs
* @param document
* @param auto
*/
protected byte matchPairsAt(final IDocument document, final boolean auto) {
fBeginPos = -1;
fEndPos = -1;
// get the chars preceding and following the start position
try {
final ITypedRegion thisPartition = TextUtilities.getPartition(document, fPartitioning, fOffset, false);
final ITypedRegion prevPartition = (fOffset > 0) ? TextUtilities.getPartition(document, fPartitioning, fOffset-1, false) : null;
char thisChar = IGNORE;
char prevChar = IGNORE;
final int thisPart = checkPartition(thisPartition.getType());
if (thisPart >= 0 && fOffset < document.getLength()) {
thisChar = document.getChar(fOffset);
}
// check, if escaped
int prevPart = -1;
if (prevPartition != null) {
prevPart = checkPartition(prevPartition.getType());
if (auto && prevPart >= 0) {
prevChar = document.getChar(fOffset-1);
final int partitionOffset = prevPartition.getOffset();
int checkOffset = fOffset-2;
final char escapeChar = getEscapeChar(prevPartition.getType());
while (checkOffset >= partitionOffset) {
if (document.getChar(checkOffset) == escapeChar) {
checkOffset--;
}
else {
break;
}
}
if ( (fOffset - checkOffset) % 2 == 1) {
// prev char is escaped
prevChar = IGNORE;
}
else if (prevPart == thisPart && prevChar == escapeChar) {
// this char is escaped
thisChar = IGNORE;
}
}
}
final int pairIdx = findChar(prevChar, prevPart, thisChar, thisPart);
if (fBeginPos > -1) { // closing peer
fAnchor = LEFT;
fScanner.configure(document, fPartition);
fEndPos = fScanner.findClosingPeer(fBeginPos + 1, fPairs[pairIdx], getEscapeChar(fPartition));
return (fEndPos > -1 && fBeginPos != fEndPos) ? PAIR_FOUND : CLOSING_NOT_FOUND;
}
else if (fEndPos > -1) { // opening peer
fAnchor = RIGHT;
fScanner.configure(document, fPartition);
fBeginPos = fScanner.findOpeningPeer(fEndPos - 1, fPairs[pairIdx], getEscapeChar(fPartition));
return (fBeginPos > -1 && fBeginPos != fEndPos) ? PAIR_FOUND : OPENING_NOT_FOUND;
}
} catch (final BadLocationException x) {
} // ignore
return NOTHING_FOUND;
}
private int checkPartition(final String id) {
for (int i = 0; i < fApplicablePartitions.length; i++) {
if (fApplicablePartitions[i].equals(id)) {
return i;
}
}
return -1;
}
/**
* @param prevChar
* @param thisChar
* @return
*/
private int findChar(final char prevChar, final int prevPart, final char thisChar, final int thisPart) {
// search order 3{2 1}4
for (int i = 0; i < fPairs.length; i++) {
if (thisChar == fPairs[i][CLOSING_PEER]) {
fEndPos = fOffset;
fPartition = fApplicablePartitions[thisPart];
return i;
}
}
for (int i = 0; i < fPairs.length; i++) {
if (prevChar == fPairs[i][OPENING_PEER]) {
fBeginPos = fOffset-1;
fPartition = fApplicablePartitions[prevPart];
return i;
}
}
for (int i = 0; i < fPairs.length; i++) {
if (thisChar == fPairs[i][OPENING_PEER]) {
fBeginPos = fOffset;
fPartition = fApplicablePartitions[thisPart];
return i;
}
}
for (int i = 0; i < fPairs.length; i++) {
if (prevChar == fPairs[i][CLOSING_PEER]) {
fEndPos = fOffset-1;
fPartition = fApplicablePartitions[prevPart];
return i;
}
}
fPartition = null;
return -1;
}
protected char getEscapeChar(final String contentType) {
return fEscapeChar;
}
}