All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jsft.facelets.CommandParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 * Portions Copyright (c) 2011 Ken Paulsen
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.jsft.facelets;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Stack;

import com.sun.jsft.util.LogUtil;


/**
 *  

This class is responsible for the actual parsing of a template.

* *

This class is intended to read the template one time. Often it may be * useful to cache the result as it would be inefficient to reread a * template multiple times. Templates that are generated from this class * are intended to be static and safe to share. However, this class * itself is not thread safe.

* * @author Ken Paulsen ([email protected]) */ public class CommandParser { /** *

Constructor which accepts a InputStream.

* * @param stream InputStream for the template. */ public CommandParser(InputStream stream) { _inputStream = stream; } /** *

Accessor for the InputStream. This comes from * the supplied InputStream.

*/ public InputStream getInputStream() throws IOException { return _inputStream; } /** *

Creates the Reader Object.

* * @throws IOException */ public void open() throws IOException { if (_reader != null) { // Generally this should not happen, but just in case... start over close(); } // FIXME: It is possible while evaluating the file an #include may need to log a message to the screen! Provide a callback mechanism to do this in a Template-specific way // Create the reader from the stream _reader = new BufferedReader( new InputStreamReader( new BufferedInputStream(getInputStream()))); // Initialize the queue we will use to push values back _stack = new Stack(); } /** *

This method closes the stream if it is open. It doesn't throw an * exception, instead it logs any exceptions at the CONFIG level.

*/ public void close() { try { if (_reader != null) { _reader.close(); } } catch (Exception ex) { if (LogUtil.configEnabled(this)) { LogUtil.config("Exception while closing stream.", ex); } } } /** *

This method returns the next character.

*/ public int nextChar() throws IOException { if (!_stack.empty()) { // We have values in the queue return _stack.pop().charValue(); } return _reader.read(); } /** *

This method pushes a character on the read queue so that it will * be read next.

*/ public void unread(int ch) { _stack.push(new Character((char) ch)); } /** *

This method returns a String of characters from the * current position in the file until the given character (or end of * file) is encountered. It will leave the given character in the * buffer, so the next character to be read will be the * endingChar or -1.

* * @param endingChar The character to read up to, but not including * @param skipComments true to strip comments. */ public String readUntil(int endingChar, boolean skipComments) throws IOException { return readUntil(new int[] {endingChar}, skipComments); } /** * */ public String readUntil(int[] endingChars, boolean skipComments) throws IOException { if (skipComments) { // In case we start on a comment and should skip it... skipCommentsAndWhiteSpace(""); } int tmpch; int next = nextChar(); StringBuffer buf = new StringBuffer(); while (!isInArray(endingChars, next) && (next != -1)) { switch (next) { case '\'' : case '\"' : if (skipComments) { // In this case, we want to make sure no comments are // skipped when inside a quote // // NOTE: Also means endingChars will not be found in // a quote. buf.append((char) next); buf.append(readUntil(next, false)); buf.append((char) next); nextChar(); // Throw away the last char read... } else { buf.append((char) next); } break; case '#' : case '/' : case '<' : // When reading we want to ignore comments, don't skip // whitespace, though... if (skipComments) { unread(next); skipCommentsAndWhiteSpace(""); // If same char, read next to prevent infinite loop // We don't have to go through switch again b/c its // not the ending char and its not escaped -- so it is // safe to add. tmpch = nextChar(); if (next == tmpch) { buf.append((char) next); } else { // We're somewhere different, unread unread(tmpch); } } else { buf.append((char) next); } break; case '\\' : // Escape Character... next = nextChar(); if (next == 'n') { // Special case, insert a '\n' character. buf.append('\n'); } else if (next == 't') { // Special case, insert a '\t' character. buf.append('\t'); } else if (next != '\n') { // add the next char unless it's a return char buf.append((char) next); } break; default: buf.append((char) next); break; } next = nextChar(); } if (next != -1) { unread(next); } // Return the result return buf.toString(); } /** *

This method returns a String of characters from the * current position in the file until the given String (or end of * file) is encountered. It will not leave the given String in the * buffer, so the next character to be read will be the character * following the given character.

* * @param endingStr The terminating String. * @param skipComments true to ignore comments. */ public String readUntil(String endingStr, boolean skipComments) throws IOException { // Sanity Check if ((endingStr == null) || (endingStr.length() == 0)) { return ""; } // Break String into characters char arr[] = endingStr.toCharArray(); int arrlen = arr.length; StringBuffer buf = new StringBuffer(""); int ch = nextChar(); // Read a char to unread int idx = 1; do { // We didn't find the end, push read values back on queue unread(ch); for (int cnt = idx-1; cnt > 0; cnt--) { unread(arr[cnt]); } // Read until the beginning of the end (maybe) buf.append(readUntil(arr[0], skipComments)); //buf.append(arr[0]); // readUntil reads but doesn't return this char // Check to see if we are at the end for (idx = 1; idx < arrlen; idx++) { ch = nextChar(); if (ch != arr[idx]) { // This is not the end! break; } } } while ((ch != -1) && (idx < arrlen)); // Append the remaining characters (use idx in case we hit eof)... for (int cnt = 1; cnt < idx; cnt++) { buf.append(arr[cnt]); } if (arrlen != idx) { // Didn't find it! throw new IllegalStateException("Unable to find: '" + endingStr + "'. Read to EOF and gave up. Read: \n" + buf.toString()); } // Return the result return buf.toString(); } /** *

This method skips the given String of characters (usually used to * skip white space. The contents of the String that is skipped is * lost. Often you may wish to skip comments as well, use * {@link CommandParser#skipCommentsAndWhiteSpace(String)} in this * case.

* * @param skipChars The white space characters to skip. * * @see CommandParser#skipCommentsAndWhiteSpace(String) */ public void skipWhiteSpace(String skipChars) throws IOException { int next = nextChar(); while ((next != -1) && (skipChars.indexOf(next) != -1)) { // Skip... next = nextChar(); } // This will skip one too many unread(next); } /** *

Normally you don't just want to skip white space, you also want to * skip comments. This method allows you to do that. It skips * comments of the following types:

* * *
  • // - Comment extends to the rest of the line.
  • *
  • # - Comment extends to the rest of the line.
  • *
  • /* - Comment extends until closing '*' and '/'.
  • *
  • <!-- - Comment extends until closing -->.
*
* * @param skipChars The white space characters to skip * * @see CommandParser#skipWhiteSpace(String) */ public void skipCommentsAndWhiteSpace(String skipChars) throws IOException { int ch = 0; while (ch != -1) { ch = nextChar(); switch (ch) { case '#' : // Skip rest of line readLine(); break; case '/' : ch = nextChar(); if (ch == '/') { // Skip rest of line readLine(); } else if (ch == '*') { // Throw away everything until '*' & '/'. readUntil("*/", false); nextChar(); // Throw away the last char read... } else { // Not a comment, don't read unread(ch); unread('/'); ch = -1; // Exit loop } break; case '<' : ch = nextChar(); // ! if (ch == '!') { ch = nextChar(); // - if (ch == '-') { ch = nextChar(); // - if (ch == '-') { // Ignore HTML-style comment readUntil("-->", false); nextChar(); // Throw away the last char read... } else { // Not a comment, probably a mistake... lets // throw an exception unread(ch); unread('-'); unread('!'); unread('<'); throw new IllegalArgumentException("Invalid " + "comment! Expected comment to begin " + "with \"