/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is TransforMiiX XSLT processor. * * The Initial Developer of the Original Code is The MITRE Corporation. * Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation. * * Portions created by Keith Visco as a Non MITRE employee, * (C) 1999 Keith Visco. All Rights Reserved. * * Contributor(s): * Keith Visco, kvisco@ziplink.net * -- original author. * */ /** * StringFunctionCall * A representation of the XPath String funtions **/ #include "FunctionLib.h" #include "XMLDOMUtils.h" #include /** * Creates a StringFunctionCall of the given type **/ StringFunctionCall::StringFunctionCall(StringFunctions aType) : mType(aType) { switch (aType) { case CONCAT: name = XPathNames::CONCAT_FN; break; case CONTAINS: name = XPathNames::CONTAINS_FN; break; case STARTS_WITH: name = XPathNames::STARTS_WITH_FN; break; case STRING_LENGTH: name = XPathNames::STRING_LENGTH_FN; break; case SUBSTRING: name = XPathNames::SUBSTRING_FN; break; case SUBSTRING_AFTER: name = XPathNames::SUBSTRING_AFTER_FN; break; case SUBSTRING_BEFORE: name = XPathNames::SUBSTRING_BEFORE_FN; break; case TRANSLATE: name = XPathNames::TRANSLATE_FN; break; default: name = XPathNames::STRING_FN; break; } } /** * Evaluates this Expr based on the given context node and processor state * @param context the context node for evaluation of this Expr * @param ps the ContextState containing the stack information needed * for evaluation * @return the result of the evaluation **/ ExprResult* StringFunctionCall::evaluate(Node* aContext, ContextState* aCs) { ListIterator iter(¶ms); switch (mType) { case CONCAT: { if (!requireParams(2, aCs)) return new StringResult("error"); String resultStr; while (iter.hasNext()) { evaluateToString((Expr*)iter.next(), aContext, aCs, resultStr); } return new StringResult(resultStr); } case CONTAINS: { if (!requireParams(2, 2, aCs)) return new StringResult("error"); String arg1, arg2; evaluateToString((Expr*)iter.next(), aContext, aCs, arg1); evaluateToString((Expr*)iter.next(), aContext, aCs, arg2); return new BooleanResult(arg1.indexOf(arg2) >= 0); } case NORMALIZE_SPACE: { if (!requireParams(0, 1, aCs)) return new StringResult("error"); String resultStr; if (iter.hasNext()) evaluateToString((Expr*)iter.next(), aContext, aCs, resultStr); else XMLDOMUtils::getNodeValue(aContext, resultStr); MBool addSpace = MB_FALSE; MBool first = MB_TRUE; String normed(resultStr.length()); UNICODE_CHAR c; PRInt32 src; for (src = 0; src < resultStr.length(); src++) { c = resultStr.charAt(src); if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { addSpace = MB_TRUE; } else { if (addSpace && !first) normed.append(' '); normed.append(c); addSpace = MB_FALSE; first = MB_FALSE; } } return new StringResult(normed); } case STARTS_WITH: { if (!requireParams(2, 2, aCs)) return new StringResult("error"); String arg1, arg2; evaluateToString((Expr*)iter.next(), aContext, aCs, arg1); evaluateToString((Expr*)iter.next(), aContext, aCs, arg2); return new BooleanResult(arg1.indexOf(arg2) == 0); } case STRING_LENGTH: { if (!requireParams(0, 1, aCs)) return new StringResult("error"); String resultStr; if (iter.hasNext()) evaluateToString((Expr*)iter.next(), aContext, aCs, resultStr); else XMLDOMUtils::getNodeValue(aContext, resultStr); return new NumberResult(resultStr.length()); } case SUBSTRING: { if (!requireParams(2, 3, aCs)) return new StringResult("error"); String src; double start, end; evaluateToString((Expr*)iter.next(), aContext, aCs, src); start = evaluateToNumber((Expr*)iter.next(), aContext, aCs); // check for NaN or +/-Inf if (Double::isNaN(start) || Double::isInfinite(start) || start + 0.5 >= src.length()) return new StringResult(); start = floor(start + 0.5) - 1; if (iter.hasNext()) { end = start + evaluateToNumber((Expr*)iter.next(), aContext, aCs); if (Double::isNaN(end) || end < 0) return new StringResult(); if (end > src.length()) end = src.length(); else end = floor(end + 0.5); } else { end = src.length(); } if (start < 0) start = 0; if (start > end) return new StringResult(); String resultStr; src.subString((PRInt32)start, (PRInt32)end, resultStr); return new StringResult(resultStr); } case SUBSTRING_AFTER: { if (!requireParams(2, 2, aCs)) return new StringResult("error"); String arg1, arg2; evaluateToString((Expr*)iter.next(), aContext, aCs, arg1); evaluateToString((Expr*)iter.next(), aContext, aCs, arg2); PRInt32 idx = arg1.indexOf(arg2); if (idx >= 0) { PRInt32 len = arg2.length(); arg1.subString(idx + len, arg2); return new StringResult(arg2); } return new StringResult(); } case SUBSTRING_BEFORE: { if (!requireParams(2, 2, aCs)) return new StringResult("error"); String arg1, arg2; evaluateToString((Expr*)iter.next(), aContext, aCs, arg1); evaluateToString((Expr*)iter.next(), aContext, aCs, arg2); PRInt32 idx = arg1.indexOf(arg2); if (idx >= 0) { arg2.clear(); arg1.subString(0, idx, arg2); return new StringResult(arg2); } return new StringResult(); } case TRANSLATE: { if (!requireParams(3, 3, aCs)) return new StringResult("error"); String src; evaluateToString((Expr*)iter.next(), aContext, aCs, src); if (src.isEmpty()) return new StringResult(); String oldChars, newChars, dest; evaluateToString((Expr*)iter.next(), aContext, aCs, oldChars); evaluateToString((Expr*)iter.next(), aContext, aCs, newChars); PRInt32 i; for (i = 0; i < src.length(); i++) { PRInt32 idx = oldChars.indexOf(src.charAt(i)); if (idx >= 0) { if (idx < newChars.length()) dest.append(newChars.charAt(idx)); } else { dest.append(src.charAt(i)); } } return new StringResult(dest); } case STRING: { if (!requireParams(0, 1, aCs)) return new StringResult("error"); String resultStr; if (iter.hasNext()) evaluateToString((Expr*)iter.next(), aContext, aCs, resultStr); else XMLDOMUtils::getNodeValue(aContext, resultStr); return new StringResult(resultStr); } } String err("Internal error"); aCs->recieveError(err); return new StringResult("error"); }