blob: 1ed6139db3c4718a22426bc3f93be35727af13a0 [file] [log] [blame]
// Copyright 2004 The Apache Software Foundation
//
// 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 org.apache.tapestry.script;
import org.apache.tapestry.ILocation;
import org.apache.tapestry.util.xml.BaseRule;
import org.apache.tapestry.util.xml.RuleDirectedParser;
/**
* Base class for the rules that build {@link org.apache.tapestry.script.IScriptToken}s.
* Used with classes that can contain a mix of text and elements (those that
* accept "full content").
*
*
* @author Howard Lewis Ship
* @version $Id$
* @since 3.0
**/
abstract class AbstractTokenRule extends BaseRule
{
/**
* Adds a token to its parent, the top object on the stack.
*/
protected void addToParent(RuleDirectedParser parser, IScriptToken token)
{
IScriptToken parent = (IScriptToken) parser.peek();
parent.addToken(token);
}
/**
* Peeks at the top object on the stack (which must be a {@link IScriptToken}),
* and converts the text into a series of {@link org.apache.tapestry.script.StaticToken} and
* {@link org.apache.tapestry.script.InsertToken}s.
*/
public void content(RuleDirectedParser parser, String content)
{
IScriptToken token = (IScriptToken) parser.peek();
addTextTokens(token, content, parser.getLocation());
}
private static final int STATE_START = 0;
private static final int STATE_DOLLAR = 1;
private static final int STATE_COLLECT_EXPRESSION = 2;
/**
* Parses the provided text and converts it into a series of
*/
protected void addTextTokens(IScriptToken token, String text, ILocation location)
{
char[] buffer = text.toCharArray();
int state = STATE_START;
int blockStart = 0;
int blockLength = 0;
int expressionStart = -1;
int expressionLength = 0;
int i = 0;
int braceDepth = 0;
while (i < buffer.length)
{
char ch = buffer[i];
switch (state)
{
case STATE_START :
if (ch == '$')
{
state = STATE_DOLLAR;
i++;
continue;
}
blockLength++;
i++;
continue;
case STATE_DOLLAR :
if (ch == '{')
{
state = STATE_COLLECT_EXPRESSION;
i++;
expressionStart = i;
expressionLength = 0;
braceDepth = 1;
continue;
}
// The '$' was just what it was, not the start of a ${} expression
// block, so include it as part of the static text block.
blockLength++;
state = STATE_START;
continue;
case STATE_COLLECT_EXPRESSION :
if (ch != '}')
{
if (ch == '{')
braceDepth++;
i++;
expressionLength++;
continue;
}
braceDepth--;
if (braceDepth > 0)
{
i++;
expressionLength++;
continue;
}
// Hit the closing brace of an expression.
// Degenerate case: the string "${}".
if (expressionLength == 0)
blockLength += 3;
if (blockLength > 0)
token.addToken(constructStatic(text, blockStart, blockLength, location));
if (expressionLength > 0)
{
String expression =
text.substring(expressionStart, expressionStart + expressionLength);
token.addToken(new InsertToken(expression, location));
}
i++;
blockStart = i;
blockLength = 0;
// And drop into state start
state = STATE_START;
continue;
}
}
// OK, to handle the end. Couple of degenerate cases where
// a ${...} was incomplete, so we adust the block length.
if (state == STATE_DOLLAR)
blockLength++;
if (state == STATE_COLLECT_EXPRESSION)
blockLength += expressionLength + 2;
if (blockLength > 0)
token.addToken(constructStatic(text, blockStart, blockLength, location));
}
private IScriptToken constructStatic(
String text,
int blockStart,
int blockLength,
ILocation location)
{
String literal = text.substring(blockStart, blockStart + blockLength);
return new StaticToken(literal, location);
}
}