001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.treeprocessor.variables;
018:
019: import org.apache.cocoon.sitemap.PatternException;
020:
021: /**
022: * Parses "Text {module:{module:attribute}} more text {variable}" types of
023: * expressions. Supports escaping of braces with '\' character, and nested
024: * expressions.
025: *
026: * @version CVS $Id: VariableExpressionTokenizer.java 433543 2006-08-22 06:22:54Z crossley $
027: */
028: public final class VariableExpressionTokenizer {
029:
030: /**
031: * Callback for tokenizer
032: */
033: public interface TokenReciever {
034: int OPEN = -2;
035: int CLOSE = -3;
036: int COLON = -4;
037: int TEXT = -5;
038: int MODULE = -6;
039: int VARIABLE = -8;
040:
041: /**
042: * Reports parsed tokens.
043: */
044: void addToken(int type, String value) throws PatternException;
045: }
046:
047: /**
048: * Tokenizes specified expression. Passes tokens to the
049: * reciever.
050: *
051: * @throws PatternException if expression is not valid
052: */
053: public static void tokenize(String expression,
054: TokenReciever reciever) throws PatternException {
055:
056: int lastTokenType = 0;
057:
058: int openCount = 0;
059: int closeCount = 0;
060:
061: int pos = 0;
062: int i;
063: boolean escape = false;
064:
065: for (i = 0; i < expression.length(); i++) {
066: final char c = expression.charAt(i);
067:
068: if (escape) {
069: escape = false;
070: } else if (c == '\\' && i < expression.length()) {
071: char nextChar = expression.charAt(i + 1);
072: if (nextChar == '{' || nextChar == '}') {
073: expression = expression.substring(0, i)
074: + expression.substring(i + 1);
075: escape = true;
076: i--;
077: }
078: } else if (c == '{') {
079: if (i > pos) {
080: reciever.addToken(
081: lastTokenType = TokenReciever.TEXT,
082: expression.substring(pos, i));
083: }
084:
085: openCount++;
086: reciever.addToken(lastTokenType = TokenReciever.OPEN,
087: null);
088:
089: int colonPos = indexOf(expression, ':', i);
090: int closePos = indexOf(expression, '}', i);
091: int openPos = indexOf(expression, '{', i);
092:
093: if (openPos < colonPos && openPos < closePos) {
094: throw new PatternException(
095: "Invalid '{' at position " + i
096: + " in expression \"" + expression
097: + "\"");
098: }
099:
100: if (colonPos < closePos) {
101: // we've found a module
102: String module = expression.substring(i + 1,
103: colonPos);
104: reciever.addToken(
105: lastTokenType = TokenReciever.MODULE,
106: module);
107: i = colonPos - 1;
108: } else {
109: // Unprefixed name: variable
110: reciever.addToken(
111: lastTokenType = TokenReciever.VARIABLE,
112: expression.substring(i + 1, closePos));
113: i = closePos - 1;
114: }
115:
116: pos = i + 1;
117: } else if (c == '}') {
118: if (i > 0 && expression.charAt(i - 1) == '\\') {
119: continue;
120: }
121: if (i > pos) {
122: reciever.addToken(
123: lastTokenType = TokenReciever.TEXT,
124: expression.substring(pos, i));
125: }
126:
127: closeCount++;
128: reciever.addToken(lastTokenType = TokenReciever.CLOSE,
129: null);
130:
131: pos = i + 1;
132: } else if (c == ':') {
133: if (lastTokenType != TokenReciever.MODULE || i != pos) {
134: // this colon isn't part of a module reference
135: continue;
136: }
137:
138: reciever.addToken(lastTokenType = TokenReciever.COLON,
139: null);
140: pos = i + 1;
141: }
142: }
143:
144: if (i > pos) {
145: reciever.addToken(lastTokenType = TokenReciever.TEXT,
146: expression.substring(pos, i));
147: }
148:
149: if (openCount != closeCount) {
150: throw new PatternException(
151: "Mismatching braces in expression \"" + expression
152: + "\"");
153: }
154: }
155:
156: private static int indexOf(String expression, char chr, int pos) {
157: int location;
158: return (location = expression.indexOf(chr, pos + 1)) != -1 ? location
159: : expression.length();
160: }
161: }
|