/* * $RCSfile: AbstractParser.java,v $ * * Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights * Reserved. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package com.sun.perseus.parser; /** * <code>AbstractParser</code> is the base class for parsers found * in this package. <br /> * All parsers work on a <code>String</code> and the <code>AbstractParser</code> * keeps a reference to that string along with the current position * (@see #currentPos) and current character (@see current). <br /> * The key methods for this class are <code>read</code> which reads the next * character in the parsed string, <code>setString</code> which sets the string * to be parsed, and the utility methods <code>skipCommaSpaces</code>, * <code>skipSpaces</code> and <code>skipSpacesCommaSpaces</code> which can * be used by descendants to skip common separators. * <br /> * For an implementation example, see {@link TransformListParser}. * * @version $Id: AbstractParser.java,v 1.2 2006/04/21 06:40:21 st125089 Exp $ */ public abstract class AbstractParser { /** * The current position in the string */ protected int currentPos; /** * The String being parsed */ protected String s; protected int length; /** * The current character being parsed * This is accessible by sub-classes */ protected int current; /** * @return the next character. Returns -1 when the * end of the String has been reached. */ protected final int read() { return (currentPos < length)? s.charAt(currentPos++): -1; } /** * Sets this parser's String. This also resets the * current position to 0 * * @param str the string this parser should parse. Should * not be null. */ protected final void setString(final String str) { if (str == null) { throw new IllegalArgumentException(); } this.s = str; this.length = str.length(); this.currentPos = 0; this.current = -1; } /** * Skips the whitespaces in the current reader. */ protected final void skipSpaces() { for (;;) { switch (current) { default: return; case 0x20: case 0x09: case 0x0D: case 0x0A: } current = (currentPos < length)? s.charAt(currentPos++): -1; } } /** * Skips the whitespaces and an optional comma. */ protected final void skipCommaSpaces() { skipSepSpaces(','); } /** * Skips the whitespaces and an optional comma. * * @param sep seperator to skip in addition to spaces. */ protected final void skipSepSpaces(final char sep) { wsp1: for (;;) { switch (current) { default: break wsp1; case 0x20: case 0x9: case 0xD: case 0xA: } current = (currentPos < length)? s.charAt(currentPos++): -1; } if (current == sep) { wsp2: for (;;) { current = (currentPos < length)? s.charAt(currentPos++): -1; switch (current) { default: break wsp2; case 0x20: case 0x9: case 0xD: case 0xA: } } } } /** * Skips wsp*,wsp* and throws an IllegalArgumentException * if no comma is found. */ protected final void skipSpacesCommaSpaces() { skipSpaces(); if (current != ',') { throw new IllegalArgumentException(); } current = (currentPos < length)? s.charAt(currentPos++): -1; skipSpaces(); } /** * Tests if the current substring (i.e. the substring beginning at the * current position) starts with the specified prefix. If the current * substring starts with the specified prefix, the current character will * be updated to point to the character immediately following the last * character in the prefix; otherwise, the <code>currentPos</code> will * not be affected. For example, if the string being parsed is * "timingAttr", and the current character is 'A': * <pre> * currentStartsWith("Att") returns true, current == 'r' * currentStartsWith("Attr") returns true, current == -1 * currentStartsWith("Attx") returns false, current == 'A' * </pre> * * @param str the prefix to be tested * @return <code>true</code> if the current substring starts with the * specified prefix. The result is <code>false</code> if * <code>currentPos</code> is non-positive, or if the current substring * does not start with the specified prefix. */ protected final boolean currentStartsWith(final String str) { if (currentPos <= 0) { return false; } if (s.startsWith(str, currentPos - 1)) { currentPos += str.length() - 1; current = (currentPos < length)? s.charAt(currentPos++): -1; return true; } return false; } }