/******************************************************************************* * Copyright (c) 2007, 2013 Red Hat, Inc. * 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: * Red Hat - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.rpm.ui.editor.rules; import org.eclipse.jface.text.rules.ICharacterScanner; import org.eclipse.jface.text.rules.IPredicateRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.Token; import org.eclipse.linuxtools.internal.rpm.ui.editor.scanners.SpecfileChangelogScanner; public class VersionReleaseRule implements IPredicateRule { /** Buffer used for pattern detection */ private StringBuilder fBuffer = new StringBuilder(); /** Buffer to keep track of trailing whitespace */ private StringBuilder fWhiteSpaceBuffer = new StringBuilder(); /** The success token */ private IToken fToken; /** The token that has to preceed this token */ private IToken fPreceedingToken; /** Where we can find out what the preceeding token was */ private SpecfileChangelogScanner fChangelogScanner; protected static final char CHARS_SEPERATOR = '-'; protected static final int STATE_START = 0; protected static final int STATE_VERSION = 1; protected static final int STATE_RELEASE = 2; protected static final int STATE_TRAIL = 3; protected static final int STATE_DONE = 4; public VersionReleaseRule(IToken token) { this.fToken = token; } public VersionReleaseRule(IToken successToken, IToken preceedingToken, SpecfileChangelogScanner scanner) { fToken = successToken; fPreceedingToken = preceedingToken; fChangelogScanner = scanner; } @Override public IToken getSuccessToken() { return fToken; } @Override public IToken evaluate(ICharacterScanner scanner, boolean resume) { // if the last token successfully read was not the fPreceedingToke fail IToken lastToken = getLastToken(); if (lastToken != fPreceedingToken) { return Token.UNDEFINED; } fBuffer.setLength(0); int state = STATE_START; int c; int numPreceedingBlanks = 0; do { c = scanner.read(); fBuffer.append((char) c); // preceeding white space if (state == STATE_START) { if (Character.isWhitespace((char) c) || c == '-') { numPreceedingBlanks++; } else { state++; } } // version state (first part of version-release) if (state == STATE_VERSION) { // if we've read some semblance of a version and we've reached // the separator character if (fBuffer.length() > numPreceedingBlanks && c == CHARS_SEPERATOR) { state++; } // otherwise we allow only digits, letters, underscores, ':' or // '.' in the version else if (!(Character.isLetterOrDigit((char) c) || c == '.' || c == '_' || c == ':')) { unreadBuffer(scanner, fBuffer); return Token.UNDEFINED; } } // release state (second part of version-release) else if (state == STATE_RELEASE) { // an EOF or EOL indicates success if (c == ICharacterScanner.EOF || c == '\n') { state = STATE_DONE; } // if we encounter a space, we enter the optional trailing // space section which we consider valid (but not part of the // token) if and only if it is ended by and EOF or EOL else if (Character.isWhitespace((char) c)) { state++; fWhiteSpaceBuffer.setLength(0); fWhiteSpaceBuffer.append(c); } else if (!(Character.isLetterOrDigit((char) c) || c == '.' || c == '_')) { // allow digits, characters or '.' in the release unreadBuffer(scanner, fBuffer); return Token.UNDEFINED; } } // whitespace state, we finished redeaing the ver-rel and are // now looking for an EOF or EOL for success else if (state == STATE_TRAIL) { // success, unwind the whitespace if (c == ICharacterScanner.EOF || c == '\n') { unreadBuffer(scanner, fWhiteSpaceBuffer); state++; } // some other illegal token after ver-rel unwind the whole // deal else if (!Character.isWhitespace((char) c)) { unreadBuffer(scanner, fBuffer); return Token.UNDEFINED; } else { // white space, keep reading fWhiteSpaceBuffer.append((char) c); } } } while (state != STATE_DONE); // we've gone through all states until we've reached STATE_DONE, success return fToken; } @Override public IToken evaluate(ICharacterScanner scanner) { return evaluate(scanner, false); } /** * Returns the characters in the buffer to the scanner. * * @param scanner * the scanner to be used */ protected void unreadBuffer(ICharacterScanner scanner, StringBuilder buffer) { for (int i = buffer.length() - 1; i >= 0; i--) { scanner.unread(); } } protected IToken getLastToken() { return fChangelogScanner.getLastToken(); } }