package ognl.helperfunction;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.webobjects.appserver.WOAssociation;
import com.webobjects.appserver._private.WOConstantValueAssociation;
import com.webobjects.appserver._private.WODeclaration;
import com.webobjects.appserver._private.WOShared;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSLog;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation._NSStringUtilities;
public class WOHelperFunctionDeclarationParser {
public static Logger log = Logger.getLogger(WOHelperFunctionDeclarationParser.class);
private NSMutableDictionary _quotedStrings;
private static final int STATE_OUTSIDE = 0;
private static final int STATE_INSIDE_COMMENT = 2;
private static final String ESCAPED_QUOTE_STRING = "_WO_ESCAPED_QUOTE_";
private static final String QUOTED_STRING_KEY = "_WODP_";
static {
WOHelperFunctionDeclarationParser.log.setLevel(Level.WARN);
}
public WOHelperFunctionDeclarationParser() {
_quotedStrings = new NSMutableDictionary();
}
public static NSMutableDictionary declarationsWithString(String declarationStr) throws WOHelperFunctionDeclarationFormatException {
WOHelperFunctionDeclarationParser declarationParser = new WOHelperFunctionDeclarationParser();
NSMutableDictionary declarations = declarationParser.parseDeclarations(declarationStr);
return declarations;
}
@Override
public String toString() {
return "<WOHelperFunctionDeclarationParser quotedStrings = " + _quotedStrings.toString() + ">";
}
public NSMutableDictionary parseDeclarations(String declarationStr) throws WOHelperFunctionDeclarationFormatException {
String strWithoutComments = _removeOldStyleCommentsFromString(declarationStr);
strWithoutComments = _removeNewStyleCommentsAndQuotedStringsFromString(strWithoutComments);
NSMutableDictionary declarations = parseDeclarationsWithoutComments(strWithoutComments);
return declarations;
}
private String _removeOldStyleCommentsFromString(String str) {
StringBuilder stringbuffer = new StringBuilder(100);
StringBuilder stringbuffer1 = new StringBuilder(100);
StringTokenizer tokenizer = new StringTokenizer(str, "/", true);
int state = WOHelperFunctionDeclarationParser.STATE_OUTSIDE;
try {
do {
if (!tokenizer.hasMoreTokens()) {
break;
}
String token = tokenizer.nextToken();
switch (state) {
case STATE_OUTSIDE:
if (token.equals("/")) {
token = tokenizer.nextToken();
if (token.startsWith("*")) {
state = WOHelperFunctionDeclarationParser.STATE_INSIDE_COMMENT;
stringbuffer1.append('/');
stringbuffer1.append(token);
}
else {
stringbuffer.append('/');
stringbuffer.append(token);
}
}
else {
stringbuffer.append(token);
}
break;
case STATE_INSIDE_COMMENT:
stringbuffer1.append(token);
String s2 = stringbuffer1.toString();
if (s2.endsWith("*/") && !s2.equals("/*/")) {
state = WOHelperFunctionDeclarationParser.STATE_OUTSIDE;
}
break;
}
}
while (true);
}
catch (NoSuchElementException e) {
log.debug("Parsing failed.", e);
}
return stringbuffer.toString();
}
private String _removeNewStyleCommentsAndQuotedStringsFromString(String declarationsStr) {
String escapedQuoteStr = _NSStringUtilities.replaceAllInstancesOfString(declarationsStr, "\\\"", WOHelperFunctionDeclarationParser.ESCAPED_QUOTE_STRING);
StringBuilder declarationWithoutCommentsBuffer = new StringBuilder(100);
StringTokenizer tokenizer = new StringTokenizer(escapedQuoteStr, "/\"", true);
try {
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken("/\"");
if (token.equals("/")) {
token = tokenizer.nextToken("\n");
if (token.startsWith("/")) {
token = _NSStringUtilities.replaceAllInstancesOfString(token, WOHelperFunctionDeclarationParser.ESCAPED_QUOTE_STRING, "\\\"");
declarationWithoutCommentsBuffer.append('\n');
tokenizer.nextToken();
}
else {
declarationWithoutCommentsBuffer.append('/');
declarationWithoutCommentsBuffer.append(token);
}
}
else if (token.equals("\"")) {
token = tokenizer.nextToken("\"");
if (token.equals("\"")) {
token = "";
}
else {
tokenizer.nextToken();
}
String quotedStringKey = WOHelperFunctionDeclarationParser.QUOTED_STRING_KEY + _quotedStrings.count();
if (NSLog.debugLoggingAllowedForLevelAndGroups(3, 0x0L)) {
NSLog.debug.appendln("Found a quoted string: " + quotedStringKey + "='" + token + "';");
}
token = _NSStringUtilities.replaceAllInstancesOfString(token, WOHelperFunctionDeclarationParser.ESCAPED_QUOTE_STRING, "\"");
_quotedStrings.setObjectForKey(token, quotedStringKey);
declarationWithoutCommentsBuffer.append(quotedStringKey);
}
else {
declarationWithoutCommentsBuffer.append(token);
}
}
}
catch (NoSuchElementException e) {
log.debug("Parsing failed.", e);
}
return declarationWithoutCommentsBuffer.toString();
}
private NSMutableDictionary parseDeclarationsWithoutComments(String declarationWithoutComment) throws WOHelperFunctionDeclarationFormatException {
NSMutableDictionary declarations = new NSMutableDictionary();
NSMutableDictionary rawDeclarations = _rawDeclarationsWithoutComment(declarationWithoutComment);
String tagName;
WODeclaration declaration;
Enumeration rawDeclarationHeaderEnum = rawDeclarations.keyEnumerator();
while (rawDeclarationHeaderEnum.hasMoreElements()) {
String declarationHeader = (String) rawDeclarationHeaderEnum.nextElement();
String declarationBody = (String) rawDeclarations.objectForKey(declarationHeader);
int colonIndex = declarationHeader.indexOf(':');
if (colonIndex < 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Missing ':' for declaration:\n" + declarationHeader + " " + declarationBody);
}
tagName = declarationHeader.substring(0, colonIndex).trim();
if (tagName.length() == 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Missing tag name for declaration:\n" + declarationHeader + " " + declarationBody);
}
if (declarations.objectForKey(tagName) != null) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Duplicate tag name '" + tagName + "' in declaration:\n" + declarationBody);
}
String type = declarationHeader.substring(colonIndex + 1).trim();
if (type.length() == 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Missing element name for declaration:\n" + declarationHeader + " " + declarationBody);
}
NSMutableDictionary associations = _associationsForDictionaryString(declarationHeader, declarationBody);
declaration = WOHelperFunctionParser.createDeclaration(tagName, type, associations);
declarations.setObjectForKey(declaration, tagName);
}
return declarations;
}
private NSMutableDictionary _associationsForDictionaryString(String declarationHeader, String declarationBody) throws WOHelperFunctionDeclarationFormatException {
NSMutableDictionary associations = new NSMutableDictionary();
String trimmedDeclarationBody = declarationBody.trim();
if (!trimmedDeclarationBody.startsWith("{") && !trimmedDeclarationBody.endsWith("}")) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Internal inconsistency : invalid dictionary for declaration:\n" + declarationHeader + " " + declarationBody);
}
int declarationBodyLength = trimmedDeclarationBody.length();
if (declarationBodyLength <= 2) {
return associations;
}
trimmedDeclarationBody = trimmedDeclarationBody.substring(1, declarationBodyLength - 1).trim();
NSArray bindings = NSArray.componentsSeparatedByString(trimmedDeclarationBody, ";");
Enumeration bindingsEnum = bindings.objectEnumerator();
do {
if (!bindingsEnum.hasMoreElements()) {
break;
}
String binding = ((String) bindingsEnum.nextElement()).trim();
if (binding.length() != 0) {
int equalsIndex = binding.indexOf('=');
if (equalsIndex < 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Invalid line. No equal in line:\n" + binding + "\nfor declaration:\n" + declarationHeader + " " + declarationBody);
}
String key = binding.substring(0, equalsIndex).trim();
if (key.length() == 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Missing binding in line:\n" + binding + "\nfor declaration:\n" + declarationHeader + " " + declarationBody);
}
String value = binding.substring(equalsIndex + 1).trim();
if (value.length() == 0) {
throw new WOHelperFunctionDeclarationFormatException("<WOHelperFunctionDeclarationParser> Missing value in line:\n" + binding + "\nfor declaration:\n" + declarationHeader + " " + declarationBody);
}
WOAssociation association = WOHelperFunctionDeclarationParser._associationWithKey(value, _quotedStrings);
Object quotedString = _quotedStrings.objectForKey(key);
if (quotedString != null) {
associations.setObjectForKey(association, quotedString);
}
else {
associations.setObjectForKey(association, key);
}
}
}
while (true);
// if (log.isDebugEnabled()) {
// log.debug("Parsed '" + s + "' declarations:\n" + nsmutabledictionary
// + "\n--------");
// }
return associations;
}
public static WOAssociation _associationWithKey(String associationValue, NSDictionary quotedStrings) {
WOAssociation association = null;
if (associationValue != null && associationValue.startsWith("~")) {
int associationValueLength = associationValue.length();
StringBuilder ognlValue = new StringBuilder();
int lastIndex = 0;
int index = 0;
while ((index = associationValue.indexOf(WOHelperFunctionDeclarationParser.QUOTED_STRING_KEY, lastIndex)) != -1) {
ognlValue.append(associationValue.substring(lastIndex, index));
int wodpValueStartIndex = index + WOHelperFunctionDeclarationParser.QUOTED_STRING_KEY.length();
int wodpValueEndIndex = wodpValueStartIndex;
for (; wodpValueEndIndex < associationValueLength && Character.isDigit(associationValue.charAt(wodpValueEndIndex)); wodpValueEndIndex++) {
// do nothing
}
String wodpKey = WOHelperFunctionDeclarationParser.QUOTED_STRING_KEY + associationValue.substring(wodpValueStartIndex, wodpValueEndIndex);
String quotedString = (String) quotedStrings.objectForKey(wodpKey);
if (quotedString != null) {
quotedString = quotedString.replaceAll("\\\"", "\\\\\"");
ognlValue.append("\"");
ognlValue.append(quotedString);
ognlValue.append("\"");
}
lastIndex = wodpValueEndIndex;
}
ognlValue.append(associationValue.substring(lastIndex));
associationValue = ognlValue.toString();
association = WOHelperFunctionAssociation.associationWithValue(associationValue);
}
else {
String quotedString = (String) quotedStrings.objectForKey(associationValue);
// MS: WO 5.4 converts \n to an actual newline. I don't know if WO 5.3 does, too, but let's go ahead and be compatible with them as long as nobody is yelling.
if (quotedString != null) {
int backslashIndex = quotedString.indexOf('\\');
if (backslashIndex != -1) {
StringBuilder sb = new StringBuilder(quotedString);
int length = sb.length();
for (int i = backslashIndex; i < length; i ++) {
char ch = sb.charAt(i);
if (ch == '\\' && i < length) {
char nextCh = sb.charAt(i + 1);
if (nextCh == 'n') {
sb.replace(i, i + 2, "\n");
}
else if (nextCh == 'r') {
sb.replace(i, i + 2, "\r");
}
else if (nextCh == 't') {
sb.replace(i, i + 2, "\t");
}
else {
sb.replace(i, i + 2, String.valueOf(nextCh));
}
length --;
}
}
quotedString = sb.toString();
}
association = WOHelperFunctionAssociation.associationWithValue(quotedString);
}
else if (_NSStringUtilities.isNumber(associationValue)) {
Number number = null;
if (associationValue != null && associationValue.contains(".")) {
number = Double.valueOf(associationValue);
}
else {
number = WOShared.unsignedIntNumber(Integer.parseInt(associationValue));
}
association = WOHelperFunctionAssociation.associationWithValue(number);
}
else if ("true".equalsIgnoreCase(associationValue) || "yes".equalsIgnoreCase(associationValue)) {
association = WOConstantValueAssociation.TRUE;
}
else if ("false".equalsIgnoreCase(associationValue) || "no".equalsIgnoreCase(associationValue) || "nil".equalsIgnoreCase(associationValue) || "null".equalsIgnoreCase(associationValue)) {
association = WOConstantValueAssociation.FALSE;
}
else {
association = WOHelperFunctionAssociation.associationWithKeyPath(associationValue);
}
}
return association;
}
private NSMutableDictionary _rawDeclarationsWithoutComment(String declarationStr) {
NSMutableDictionary declarations = new NSMutableDictionary();
StringBuilder declarationWithoutCommentBuffer = new StringBuilder(100);
StringTokenizer tokenizer = new StringTokenizer(declarationStr, "{", true);
try {
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken("{");
if (token.equals("{")) {
token = tokenizer.nextToken("}");
if (token.equals("}")) {
token = "";
}
else {
tokenizer.nextToken();
}
String declarationWithoutComment = declarationWithoutCommentBuffer.toString();
if (declarationWithoutComment.startsWith(";")) {
declarationWithoutComment = declarationWithoutComment.substring(1);
}
declarations.setObjectForKey("{" + token + "}", declarationWithoutComment.trim());
declarationWithoutCommentBuffer.setLength(0);
}
else {
declarationWithoutCommentBuffer.append(token);
}
}
}
catch (NoSuchElementException e) {
log.debug("Failed to parse.", e);
}
return declarations;
}
}