/* * Copyright (c) 2011, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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.dart.tools.ui.internal.text.functions; import com.google.dart.tools.ui.text.DartPartitions; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; /** * Helper class for match pairs of characters. */ public final class DartPairMatcher extends DefaultCharacterPairMatcher { public DartPairMatcher(char[] pairs) { super(pairs, DartPartitions.DART_PARTITIONING); } /* @see ICharacterPairMatcher#match(IDocument, int) */ @Override public IRegion match(IDocument document, int offset) { try { return performMatch(document, offset); } catch (BadLocationException ble) { return null; } } /** * Returns true if the character at the specified offset is a less-than sign, rather than an type * parameter list open angle bracket. * * @param document a document * @param offset an offset within the document * @return true if the character at the specified offset is not a type parameter start bracket * @throws BadLocationException */ private boolean isLessThanOperator(IDocument document, int offset) throws BadLocationException { if (offset < 0) { return false; } DartHeuristicScanner scanner = new DartHeuristicScanner( document, DartPartitions.DART_PARTITIONING, TextUtilities.getContentType(document, DartPartitions.DART_PARTITIONING, offset, false)); return !isTypeParameterBracket(offset, document, scanner); } /** * Checks if the angular bracket at <code>offset</code> is a type parameter bracket. * * @param offset the offset of the opening bracket * @param document the document * @param scanner a java heuristic scanner on <code>document</code> * @return <code>true</code> if the bracket is part of a type parameter, <code>false</code> * otherwise */ private boolean isTypeParameterBracket(int offset, IDocument document, DartHeuristicScanner scanner) { /* * type parameter come after braces (closing or opening), semicolons, or after a Type name * (heuristic: starts with capital character, or after a modifier keyword in a method * declaration (visibility, static, synchronized, final) */ try { IRegion line = document.getLineInformationOfOffset(offset); int prevToken = scanner.previousToken(offset - 1, line.getOffset()); int prevTokenOffset = scanner.getPosition() + 1; String previous = prevToken == Symbols.TokenEOF ? null : document.get( prevTokenOffset, offset - prevTokenOffset).trim(); if (prevToken == Symbols.TokenLBRACE || prevToken == Symbols.TokenRBRACE || prevToken == Symbols.TokenSEMICOLON // || prevToken == Symbols.TokenSYNCHRONIZED || prevToken == Symbols.TokenSTATIC || (prevToken == Symbols.TokenIDENT && isTypeParameterIntroducer(previous)) || prevToken == Symbols.TokenEOF) { return true; } } catch (BadLocationException e) { return false; } return false; } /** * Returns <code>true</code> if <code>identifier</code> is an identifier that could come right * before a type parameter list. It uses a heuristic: if the identifier starts with an upper case, * it is assumed a type name. Also, if <code>identifier</code> is a method modifier, it is assumed * that the angular bracket is part of the generic type parameter of a method. * * @param identifier the identifier to check * @return <code>true</code> if the identifier could introduce a type parameter list */ private boolean isTypeParameterIntroducer(String identifier) { return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0)) || identifier.startsWith("final") //$NON-NLS-1$ || identifier.startsWith("public") //$NON-NLS-1$ || identifier.startsWith("public") //$NON-NLS-1$ || identifier.startsWith("protected") //$NON-NLS-1$ || identifier.startsWith("private")); //$NON-NLS-1$ } /* * Performs the actual work of matching for #match(IDocument, int). */ private IRegion performMatch(IDocument document, int offset) throws BadLocationException { if (offset < 0 || document == null) { return null; } final char prevChar = document.getChar(Math.max(offset - 1, 0)); if (prevChar == '<' && isLessThanOperator(document, offset - 1)) { return null; } final IRegion region = super.match(document, offset); if (region == null) { return region; } if (prevChar == '>') { final int peer = region.getOffset(); if (isLessThanOperator(document, peer)) { return null; } } return region; } }