// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT License. See LICENSE file in the project root for license information. package com.microsoft.javapkgsrv; import org.eclipse.jface.text.Assert; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; @SuppressWarnings("deprecation") public class JavaParamHelpMatcher { private class CharPairs { private final char[] fPairs; public CharPairs(char[] pairs) { fPairs= pairs; } /** * Returns true if the specified character occurs in one of the character pairs. * * @param c a character * @return true exactly if the character occurs in one of the pairs */ public boolean contains(char c) { char[] pairs= fPairs; for (int i= 0, n= pairs.length; i < n; i++) { if (c == pairs[i]) return true; } return false; } /** * Returns true if the specified character opens a character pair * when scanning in the specified direction. * * @param c a character * @param searchForward the direction of the search * @return whether or not the character opens a character pair */ public boolean isOpeningCharacter(char c, boolean searchForward) { for (int i= 0; i < fPairs.length; i += 2) { if (searchForward && getStartChar(i) == c) return true; else if (!searchForward && getEndChar(i) == c) return true; } return false; } /** * Returns true if the specified character is a start character. * * @param c a character * @return true exactly if the character is a start character */ public boolean isStartCharacter(char c) { return this.isOpeningCharacter(c, true); } /** * Returns true if the specified character is an end character. * * @param c a character * @return true exactly if the character is an end character * @since 3.8 */ public boolean isEndCharacter(char c) { return this.isOpeningCharacter(c, false); } /** * Returns the matching character for the specified character. * * @param c a character occurring in a character pair * @return the matching character */ public char getMatching(char c) { for (int i= 0; i < fPairs.length; i += 2) { if (getStartChar(i) == c) return getEndChar(i); else if (getEndChar(i) == c) return getStartChar(i); } Assert.isTrue(false); return '\0'; } private char getStartChar(int i) { return fPairs[i]; } private char getEndChar(int i) { return fPairs[i + 1]; } } private CharPairs fPairs; private char[] fSeparators; private boolean isSeparator(char c) { for (int i= 0, n= fSeparators.length; i < n; i++) { if (c == fSeparators[i]) return true; } return false; } public JavaParamHelpMatcher(char[] chars, char[] separators) { fPairs= new CharPairs(chars); fSeparators = separators; } private class DocumentAccessor { private IDocument fDocument; public DocumentAccessor(IDocument document) { fDocument = document; } public char getChar(int pos) throws BadLocationException { return fDocument.getChar(pos); } public int getNextPosition(int currentPos, boolean searchForward) { return currentPos + (searchForward ? 1 : -1); } public boolean inPartition(int pos) { return true; } } public class ParamRegion { public IRegion region = null; public int paramSeparatorCount = 0; } public ParamRegion findEnclosingPeerCharacters(IDocument document, int offset, int length) { if (document == null || offset < 0 || offset > document.getLength()) return null; try { DocumentAccessor doc = new DocumentAccessor(document); return findEnclosingPeers(document, doc, offset, length, 0, document.getLength()); } catch(BadLocationException ble) { return null; } } private ParamRegion findEnclosingPeers(IDocument document, DocumentAccessor doc, int offset, int length, int lowerBoundary, int upperBoundary) throws BadLocationException { char[] pairs= fPairs.fPairs; /* Special ParamHelp added here */ int cSeparators = 0; /* Special ParamHelp end here */ int start; int end; if (length >= 0) { start= offset; end= offset + length; } else { end= offset; start= offset + length; } boolean lowerFound= false; boolean upperFound= false; int[][] counts= new int[pairs.length][2]; char currChar= (start != document.getLength()) ? doc.getChar(start) : Character.MIN_VALUE; int pos1; int pos2; if (fPairs.isEndCharacter(currChar)) { pos1= doc.getNextPosition(start, false); pos2= start; } /* Special ParamHelp added here */ else if (isSeparator(currChar)) { pos1 = doc.getNextPosition(start, false); pos2 = doc.getNextPosition(start, true); } /* Special ParamHelp end here */ else { pos1= start; pos2= doc.getNextPosition(start, true); } while ((pos1 >= lowerBoundary && !lowerFound) || (pos2 < upperBoundary && !upperFound)) { for (int i= 0; i < counts.length; i++) { counts[i][0]= counts[i][1]= 0; } outer1: while (pos1 >= lowerBoundary && !lowerFound) { final char c= doc.getChar(pos1); int i= getCharacterIndex(c, document, pos1); if (i != -1 && doc.inPartition(pos1)) { if (i % 2 == 0) { counts[i / 2][0]--; //start } else { counts[i / 2][0]++; //end } for (int j= 0; j < counts.length; j++) { if (counts[j][0] == -1) { lowerFound= true; break outer1; } } } /* Special ParamHelp added here */ else if (isSeparator(c)) { boolean nestedSeparator = false; for (int j= 0; j < counts.length; j++) { if (counts[j][0] != 0) { nestedSeparator = true; } } if (!nestedSeparator) cSeparators++; } /* Special ParamHelp end here */ pos1= doc.getNextPosition(pos1, false); } outer2: while (pos2 < upperBoundary && !upperFound) { final char c= doc.getChar(pos2); int i= getCharacterIndex(c, document, pos2); if (i != -1 && doc.inPartition(pos2)) { if (i % 2 == 0) { counts[i / 2][1]++; //start } else { counts[i / 2][1]--; //end } for (int j= 0; j < counts.length; j++) { if (counts[j][1] == -1 && counts[j][0] == -1) { upperFound= true; break outer2; } } } pos2= doc.getNextPosition(pos2, true); } if (pos1 > start || pos2 < end - 1) { //match inside selection => discard pos1= doc.getNextPosition(pos1, false); pos2= doc.getNextPosition(pos2, true); lowerFound= false; upperFound= false; } } pos2++; if (pos1 < lowerBoundary || pos2 > upperBoundary) return null; ParamRegion ret = new ParamRegion(); ret.region = new Region(pos1, pos2 - pos1); ret.paramSeparatorCount = cSeparators; return ret; } private int getCharacterIndex(char ch, IDocument document, int offset) { char[] pairs= fPairs.fPairs; for (int i= 0; i < pairs.length; i++) { if (pairs[i] == ch && isMatchedChar(ch, document, offset)) { return i; } } return -1; } public boolean isMatchedChar(char ch, IDocument document, int offset) { return isMatchedChar(ch); } public boolean isMatchedChar(char ch) { return fPairs.contains(ch); } }