/* * Copyright 2012, Ryan J. McDonough * * 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.damnhandy.uri.template.impl; import com.damnhandy.uri.template.Expression; import com.damnhandy.uri.template.Literal; import com.damnhandy.uri.template.MalformedUriTemplateException; import com.damnhandy.uri.template.UriTemplateComponent; import java.util.LinkedList; /** * Utility class used to parse the URI template string into a series of components * * @author <a href="ryan@damnhandy.com">Ryan J. McDonough</a> * @version $Revision: 1.1 $ * @since 2.0 */ public final class UriTemplateParser { private static final char EXPR_START = '{'; private static final char EXPR_END = '}'; private boolean startedTemplate = false; private boolean expressionCaptureOn = false; private boolean literalCaptureOn = false; private LinkedList<UriTemplateComponent> components = new LinkedList<UriTemplateComponent>(); private StringBuilder buffer; private int startPosition; /** * Scans the URI template looking for literal string components and expressions. * * @param templateString the URI template string to scan * @since 2.0 */ public LinkedList<UriTemplateComponent> scan(String templateString) throws MalformedUriTemplateException { char[] template = templateString.toCharArray(); startTemplate(); int i; for (i = 0; i < template.length; i++) { char c = template[i]; if (c == EXPR_START) { if (literalCaptureOn) { endLiteral(i); } startExpression(i); } if (c != EXPR_START || c != EXPR_END) { startLiteral(i); } if (expressionCaptureOn || literalCaptureOn) { capture(c); } if (c == EXPR_END) { endExpression(i); startLiteral(i); } } if (literalCaptureOn) { endLiteral(i); } endTemplate(i); return components; } /** * If capture is active, collect the characters into the buffer * * @param currentChar */ private void capture(char currentChar) { if (buffer == null) { buffer = new StringBuilder(); } buffer.append(currentChar); } /** * Called when the {@link UriTemplateParser} has started on the template. */ private void startTemplate() { startedTemplate = true; } /** * Called when the end of the template is reached. If an expression has * not been closed, an exception will be raised. */ private void endTemplate(int position) throws MalformedUriTemplateException { startedTemplate = false; if (expressionCaptureOn) { throw new MalformedUriTemplateException("The expression at position " + startPosition + " was never terminated", startPosition); } } /** * Marks the start of * * @param position */ private void startLiteral(int position) throws MalformedUriTemplateException { if (startedTemplate) { if (!literalCaptureOn) { //throw new IllegalStateException("Literal capture already started at character "+template[position]); literalCaptureOn = true; startPosition = position; } } else { throw new IllegalStateException("Cannot start a literal without beginning the template"); } } private void endLiteral(int position) throws MalformedUriTemplateException { if (startedTemplate) { if (!literalCaptureOn) { throw new IllegalStateException("Can't end a literal without starting it first"); } // in the case that we have back to back expressions ({foo}{?bar}), we can get into a state // we started capturing a literal but never actually collected anything yet. if (buffer != null) { components.add(new Literal(buffer.toString(), this.startPosition)); literalCaptureOn = false; buffer = null; } } else { throw new IllegalStateException("Cannot end a literal without beginning the template"); } } /** * Called when the start of an expression has been encountered. */ private void startExpression(int position) throws MalformedUriTemplateException { if (startedTemplate) { if (expressionCaptureOn) { throw new MalformedUriTemplateException("A new expression start brace found at " + position + " but another unclosed expression was found at " + startPosition, position); } literalCaptureOn = false; expressionCaptureOn = true; startPosition = position; } else { throw new IllegalStateException("Cannot start an expression without beginning the template"); } } /** * Called when the end of an expression has been encountered. */ private void endExpression(int position) throws MalformedUriTemplateException { // an expression close brace is found without a start if (startedTemplate) { if (!expressionCaptureOn) { throw new MalformedUriTemplateException("Expression close brace was found at position " + position + " yet there was no start brace.", position); } expressionCaptureOn = false; components.add(new Expression(buffer.toString(), this.startPosition)); buffer = null; } else { throw new IllegalStateException("Cannot end an expression without beginning the template"); } } }