blob: b8bca4e18f53f953d43ebf653ecf26966af9b9e5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.jsp.core.el;
import java.io.StringReader;
import org.apache.commons.el.Expression;
import org.apache.commons.el.ExpressionString;
import org.apache.commons.el.parser.ELParser;
import org.apache.commons.el.parser.ParseException;
/**
* Utility class to implement support functionality to "morph" JSP EL into JSF
* EL
*
* @author mengbo
* @version 1.5
*/
public final class JSFELParserHelper {
/**
* an EL left brace
*/
public static String JSF_EL_LEFT_BRACE = "#{"; //$NON-NLS-1$
/**
* an EL right brace
*/
public static String JSF_EL_RIGHT_BRACE = "}"; //$NON-NLS-1$
private JSFELParserHelper() {
// util class, do not instantiate
}
/**
* Gets the parsed form of the given expression string. Returns either an
* Expression or ExpressionString.
* @param expressionString
* @return the result of parsing expressionString
*/
public static Object parseExpression(String expressionString) {
expressionString = toJspElExpression(expressionString);
ELParser parser = new ELParser(new StringReader(expressionString));
try {
Object expression = parser.ExpressionString();
if (!(expression instanceof Expression)
&& !(expression instanceof ExpressionString)) {
return null;
}
return expression;
} catch (ParseException e) {
// String msg = "Invalid expression: '" + expressionString + "'";
// log.debug(msg, e);
// throw new ReferenceSyntaxException(msg, e);
}
return null;
}
/**
* @param expressionString
* @return true if the expression is 'valid'
*/
public static boolean isValidEL(String expressionString) {
if (expressionString == null || expressionString.length() == 0) {
return false;
}
return expressionString.startsWith(JSF_EL_LEFT_BRACE)
&& expressionString.endsWith(JSF_EL_RIGHT_BRACE);
}
/**
* @param expressionString
* @return expressionString with the left and right braces removed
* or the original string if isValidEL(expression) == false
*/
public static String trimELBrace(String expressionString) {
if (!isValidEL(expressionString)) {
return expressionString;
}
String trimedExpression = null;
trimedExpression = expressionString.substring(JSF_EL_LEFT_BRACE
.length(), expressionString.length()
- JSF_EL_RIGHT_BRACE.length());
return trimedExpression;
}
/**
* Convert ValueBinding syntax #{ } to JSP EL syntax ${ }
*
* @param expressionString
* <code>ValueBinding</code> reference expression
*
* @return JSP EL compatible expression
*/
public static String toJspElExpression(String expressionString) {
StringBuffer sb = new StringBuffer(expressionString.length());
int remainsPos = 0;
for (int posOpenBrace = expressionString.indexOf('{'); posOpenBrace >= 0; posOpenBrace = expressionString
.indexOf('{', remainsPos)) {
if (posOpenBrace > 0) {
if (posOpenBrace - 1 > remainsPos)
sb.append(expressionString.substring(remainsPos,
posOpenBrace - 1));
if (expressionString.charAt(posOpenBrace - 1) == '$') {
sb.append("${'${'}"); //$NON-NLS-1$
remainsPos = posOpenBrace + 1;
continue;
} else if (expressionString.charAt(posOpenBrace - 1) == '#') {
// TODO: should use \\ as escape for \ always, not just when
// before #{
// allow use of '\' as escape symbol for #{ (for
// compatibility with Sun's extended implementation)
/*
* if (isEscaped(expressionString, posOpenBrace - 1)) {
* escapes: { for (int i = sb.length() - 1; i >= 0; i--) {
* if (sb.charAt(i) != '\\') { sb.setLength( sb.length() -
* (sb.length() - i) / 2); break escapes; } }
* sb.setLength(sb.length() / 2); } sb.append("#{"); } else {
*/
sb.append("${"); //$NON-NLS-1$
int posCloseBrace = indexOfMatchingClosingBrace(
expressionString, posOpenBrace);
sb.append(expressionString.substring(posOpenBrace + 1,
posCloseBrace + 1));
remainsPos = posCloseBrace + 1;
continue;
// }
} else {
if (posOpenBrace > remainsPos)
sb.append(expressionString.charAt(posOpenBrace - 1));
}
}
// Standalone brace
sb.append('{');
remainsPos = posOpenBrace + 1;
}
sb.append(expressionString.substring(remainsPos));
// Create a new String to shrink mem size since we are caching
return new String(sb.toString());
}
private static int findQuote(String expressionString, int start) {
int indexofSingleQuote = expressionString.indexOf('\'', start);
int indexofDoubleQuote = expressionString.indexOf('"', start);
return minIndex(indexofSingleQuote, indexofDoubleQuote);
}
/**
* Return the index of the matching closing brace, skipping over quoted text
*
* @param expressionString
* string to search
* @param indexofOpeningBrace
* the location of opening brace to match
*
* @return the index of the matching closing brace
*
* @throws ReferenceSyntaxException
* if matching brace cannot be found
*/
private static int indexOfMatchingClosingBrace(String expressionString,
int indexofOpeningBrace) {
int len = expressionString.length();
int i = indexofOpeningBrace + 1;
// Loop through quoted strings
for (;;) {
if (i >= len) {
throw new IllegalStateException(
"Missing closing brace. Expression: '" //$NON-NLS-1$
+ expressionString + "'"); //$NON-NLS-1$
}
int indexofClosingBrace = expressionString.indexOf('}', i);
i = minIndex(indexofClosingBrace, findQuote(expressionString, i));
if (i < 0) {
// No delimiter found
throw new IllegalStateException(
"Missing closing brace. Expression: '" //$NON-NLS-1$
+ expressionString + "'"); //$NON-NLS-1$
}
// 1. If quoted literal, find closing quote
if (i != indexofClosingBrace) {
i = indexOfMatchingClosingQuote(expressionString, i) + 1;
if (i == 0) {
// Note: if no match, i==0 because -1 + 1 = 0
throw new IllegalStateException(
"Missing closing quote. Expression: '" //$NON-NLS-1$
+ expressionString + "'"); //$NON-NLS-1$
}
} else {
// Closing brace
return i;
}
}
}
/**
* Returns the index of the matching closing quote, skipping over escaped
* quotes
*
* @param expressionString
* string to scan
* @param indexOfOpeningQuote
* start from this position in the string
* @return -1 if no match, the index of closing quote otherwise
*/
private static int indexOfMatchingClosingQuote(String expressionString,
int indexOfOpeningQuote) {
char quote = expressionString.charAt(indexOfOpeningQuote);
for (int i = expressionString.indexOf(quote, indexOfOpeningQuote + 1); i >= 0; i = expressionString
.indexOf(quote, i + 1)) {
if (!isEscaped(expressionString, i)) {
return i;
}
}
// No matching quote found
return -1;
}
private static boolean isEscaped(String expressionString, int i) {
int escapeCharCount = 0;
while ((--i >= 0) && (expressionString.charAt(i) == '\\')) {
escapeCharCount++;
}
return (escapeCharCount % 2) != 0;
}
/**
* Returns the minimum index >= 0, if any
*
* <p>
* Use to find the first of two characters in a string:<br>
* <code>minIndex(s.indexOf('/'), indexOf('\'))</code>
* </p>
* @param a
* @param b
* @return the minimum index >= 0, if any
*
*/
public static int minIndex(int a, int b) {
return (a < 0) ? b : (b < 0) ? a : (a < b) ? a : b;
}
}