// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.collide.shared.util; /** * Searches lines of text to find the matching scopeEnd character. It will keep * a stack of additional scopeStart characters it comes across to find the right * scopeEnd. Examples of this are for finding matching { }, [ ] and ( ) pairs. */ public class ScopeMatcher { private final boolean searchingForward; private final char scopeEndChar; private final char scopeStartChar; private int stack = 0; private int nextMatchColumn; public ScopeMatcher(boolean searchingForward, char start, char end) { this.searchingForward = searchingForward; scopeStartChar = start; scopeEndChar = end; } /** * Search the next line for {@link #scopeEndChar}, keeping the {@link #stack} * in mind. * * @param text * @return columnIndex, or -1 if no match on this line. */ public int searchNextLine(String text) { nextMatchColumn = searchingForward ? 0 : text.length() - 1; while (true) { if (isStartColumnNext(text)) { stack++; } else if (nextMatchColumn >= 0) { stack--; } if (stack == 0 && nextMatchColumn >= 0) { return nextMatchColumn; } else if (nextMatchColumn == -1) { break; } proceedForward(); } return -1; } /** * Move to the next occurrence of either {@link #scopeStartChar} or * {@link #scopeEndChar} and update {@link #nextMatchColumn}. */ private boolean isStartColumnNext(String text) { int startCharColumn; if (searchingForward) { startCharColumn = text.indexOf(scopeStartChar, nextMatchColumn); nextMatchColumn = text.indexOf(scopeEndChar, nextMatchColumn); if ((startCharColumn < nextMatchColumn || nextMatchColumn == -1) && startCharColumn != -1) { nextMatchColumn = startCharColumn; return true; } } else { if (nextMatchColumn == -1) { /* * TODO: Firefox/Chrome bug where lastIndexOf with a * negative offset parameter will return a match for the first * character. http://code.google.com/p/google-web-toolkit/issues/detail?id=6615 */ return false; } startCharColumn = text.lastIndexOf(scopeStartChar, nextMatchColumn); nextMatchColumn = text.lastIndexOf(scopeEndChar, nextMatchColumn); if ((startCharColumn > nextMatchColumn || nextMatchColumn == -1) && startCharColumn != -1) { nextMatchColumn = startCharColumn; return true; } } return false; } private void proceedForward() { if (searchingForward) { nextMatchColumn++; } else { nextMatchColumn--; } } }