You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by mu...@apache.org on 2023/08/19 16:33:59 UTC
[xalan-java] branch xalan-j_xslt3.0 updated: committing implementation of xpath 3.1 collation support, that has implementations of 'unicode codepoint collation' and 'unicode collation algorithm' as defined by xpath 3.1 f&o spec. also committing an implementation of xpath 3.1 fn:compare function that makes use of these implemented xpath collations. also committing few new working test cases, for these xpath implementation features. committing a minor enhancement to implementation of xsl:for-each-group instruction as well, along with a [...]
This is an automated email from the ASF dual-hosted git repository.
mukulg pushed a commit to branch xalan-j_xslt3.0
in repository https://gitbox.apache.org/repos/asf/xalan-java.git
The following commit(s) were added to refs/heads/xalan-j_xslt3.0 by this push:
new bd6c5ab1 committing implementation of xpath 3.1 collation support, that has implementations of 'unicode codepoint collation' and 'unicode collation algorithm' as defined by xpath 3.1 f&o spec. also committing an implementation of xpath 3.1 fn:compare function that makes use of these implemented xpath collations. also committing few new working test cases, for these xpath implementation features. committing a minor enhancement to implementation of xsl:for-each-group instruction as [...]
new bdf0303e Merge pull request #54 from mukulga/xalan-j_xslt3.0_mukul
bd6c5ab1 is described below
commit bd6c5ab1fa9bdf0b958e74429e7d22404eccaf4e
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Sat Aug 19 21:57:02 2023 +0530
committing implementation of xpath 3.1 collation support, that has implementations of 'unicode codepoint collation' and 'unicode collation algorithm' as defined by xpath 3.1 f&o spec. also committing an implementation of xpath 3.1 fn:compare function that makes use of these implemented xpath collations. also committing few new working test cases, for these xpath implementation features. committing a minor enhancement to implementation of xsl:for-each-group instruction as well, along w [...]
---
.../apache/xalan/templates/ElemForEachGroup.java | 24 +-
.../xalan/templates/XSConstructorFunctionUtil.java | 13 +-
.../xslt/util/XslTransformEvaluationHelper.java | 7 +-
src/org/apache/xpath/XPathCollationSupport.java | 462 +++++++++++++++++++++
src/org/apache/xpath/XPathContext.java | 18 +
src/org/apache/xpath/compiler/FunctionTable.java | 9 +-
src/org/apache/xpath/compiler/Keywords.java | 6 +
src/org/apache/xpath/functions/FuncCompare.java | 145 +++++++
.../xpath/functions/FuncStringToCodepoints.java | 5 +-
src/org/apache/xpath/xs/types/XSString.java | 104 +++++
tests/fn_compare/gold/test1.out | 6 +
tests/fn_compare/gold/test2.out | 10 +
tests/fn_compare/gold/test3.out | 6 +
tests/fn_compare/test1.xsl | 46 ++
tests/fn_compare/test1_a.xml | 23 +
tests/fn_compare/test2.xsl | 54 +++
tests/fn_compare/test3.xsl | 44 ++
tests/grouping/gold/test25.out | 32 ++
tests/grouping/test1_g.xml | 50 +++
tests/grouping/test25.xsl | 47 +++
tests/org/apache/xalan/xpath3/FnCompareTests.java | 80 ++++
tests/org/apache/xalan/xslt3/AllXsl3Tests.java | 3 +-
tests/org/apache/xalan/xslt3/GroupingTests.java | 10 +
23 files changed, 1186 insertions(+), 18 deletions(-)
diff --git a/src/org/apache/xalan/templates/ElemForEachGroup.java b/src/org/apache/xalan/templates/ElemForEachGroup.java
index d47571e6..c41aaad1 100644
--- a/src/org/apache/xalan/templates/ElemForEachGroup.java
+++ b/src/org/apache/xalan/templates/ElemForEachGroup.java
@@ -33,6 +33,7 @@ import javax.xml.transform.TransformerException;
import org.apache.xalan.transformer.ForEachGroupXslSortSorter;
import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.utils.NodeVector;
@@ -41,6 +42,7 @@ import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.NodeSequence;
+import org.apache.xpath.composite.ForExpr;
import org.apache.xpath.composite.SimpleSequenceConstructor;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XBoolean;
@@ -409,8 +411,9 @@ public class ElemForEachGroup extends ElemTemplateElement
sourceNodes = getSourceNodesFromResultSequence(resultSeq, xctxt);
}
}
- else if (m_selectExpression instanceof SimpleSequenceConstructor) {
- XObject xObj = ((SimpleSequenceConstructor)m_selectExpression).execute(xctxt);
+ else if ((m_selectExpression instanceof SimpleSequenceConstructor) ||
+ (m_selectExpression instanceof ForExpr)) {
+ XObject xObj = m_selectExpression.execute(xctxt);
ResultSequence resultSeq = (ResultSequence)xObj;
sourceNodes = getSourceNodesFromResultSequence(resultSeq, xctxt);
@@ -422,7 +425,7 @@ public class ElemForEachGroup extends ElemTemplateElement
// hashmap to store groups formed for, either 'group-by' or 'group-adjacent' attributes.
// hashmap's key is the grouping key value, and value of that hashmap item entry is the
- // contents of that group.
+ // content of that group.
Map<Object, List<Integer>> xslForEachGroupMap = new HashMap<Object, List<Integer>>();
// list to store groups formed for, either 'group-starting-with' or 'group-ending-with'
@@ -811,7 +814,7 @@ public class ElemForEachGroup extends ElemTemplateElement
}
else {
// any other data type for grouping key, is treated as string
- xpathRawResult = xpathEvalResult.str();
+ xpathRawResult = XslTransformEvaluationHelper.getStrVal(xpathEvalResult);
}
return xpathRawResult;
@@ -856,17 +859,18 @@ public class ElemForEachGroup extends ElemTemplateElement
}
+ /*
+ * Get XML document source nodes (represented as an 'DTMIterator' object), from
+ * a list of XNodeSet objects contained within a 'ResultSequence' object.
+ */
private DTMIterator getSourceNodesFromResultSequence(ResultSequence resultSeq, XPathContext xctxt)
throws TransformerException {
DTMIterator sourceNodes = null;
NodeVector nodeVector = new NodeVector();
- // we're assuming here that, all items within this 'resultSeq'
- // object shall be XNodeSet objects.
-
- // how to do this 'resultSeq' object processing, when any of its
- // items are other than XML nodes?
+ // how to process the sequence object 'resultSeq', when any of
+ // its items are other than XNodeSet object instances?
// REVISIT
for (int idx = 0; idx < resultSeq.size(); idx++) {
XObject xObject = resultSeq.item(idx);
@@ -881,7 +885,7 @@ public class ElemForEachGroup extends ElemTemplateElement
sourceNodes = nodeSequence.cloneWithReset();
} catch (CloneNotSupportedException ex) {
throw new TransformerException("An error occured during XSL grouping with xsl:for-each-group "
- + "element.", xctxt.getSAXLocator());
+ + "instruction.", xctxt.getSAXLocator());
}
return sourceNodes;
diff --git a/src/org/apache/xalan/templates/XSConstructorFunctionUtil.java b/src/org/apache/xalan/templates/XSConstructorFunctionUtil.java
index 371afabf..2e44d4d9 100644
--- a/src/org/apache/xalan/templates/XSConstructorFunctionUtil.java
+++ b/src/org/apache/xalan/templates/XSConstructorFunctionUtil.java
@@ -38,6 +38,7 @@ import org.apache.xpath.xs.types.XSFloat;
import org.apache.xpath.xs.types.XSInt;
import org.apache.xpath.xs.types.XSInteger;
import org.apache.xpath.xs.types.XSLong;
+import org.apache.xpath.xs.types.XSString;
import org.apache.xpath.xs.types.XSYearMonthDuration;
import org.xml.sax.SAXException;
@@ -71,7 +72,17 @@ public class XSConstructorFunctionUtil {
// evaluate XPath 3.1 constructor function calls, corresponding to XML Schema
// built-in types.
- if ((Keywords.XS_DECIMAL).equals(funcExtFunction.getFunctionName())) {
+ if ((Keywords.XS_STRING).equals(funcExtFunction.getFunctionName())) {
+ ResultSequence argSequence = new ResultSequence();
+ for (int idx = 0; idx < funcExtFunction.getArgCount(); idx++) {
+ XObject argVal = (funcExtFunction.getArg(idx)).execute(xctxt);
+ argSequence.add(new XSString(XslTransformEvaluationHelper.getStrVal(argVal)));
+ }
+
+ ResultSequence rSeq = (new XSString()).constructor(argSequence);
+ evalResult = rSeq.item(0);
+ }
+ else if ((Keywords.XS_DECIMAL).equals(funcExtFunction.getFunctionName())) {
ResultSequence argSequence = new ResultSequence();
for (int idx = 0; idx < funcExtFunction.getArgCount(); idx++) {
XObject argVal = (funcExtFunction.getArg(idx)).execute(xctxt);
diff --git a/src/org/apache/xalan/xslt/util/XslTransformEvaluationHelper.java b/src/org/apache/xalan/xslt/util/XslTransformEvaluationHelper.java
index 1c7fdc32..360896e5 100644
--- a/src/org/apache/xalan/xslt/util/XslTransformEvaluationHelper.java
+++ b/src/org/apache/xalan/xslt/util/XslTransformEvaluationHelper.java
@@ -16,16 +16,15 @@
*/
package org.apache.xalan.xslt.util;
+import java.util.List;
+
+import org.apache.xalan.templates.XMLNSDecl;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.xs.types.XSAnyType;
import org.apache.xpath.xs.types.XSUntyped;
import org.apache.xpath.xs.types.XSUntypedAtomic;
-import java.util.List;
-
-import org.apache.xalan.templates.XMLNSDecl;
-
/**
* This class, has few utility methods, to help with certain
* XalanJ XSLT transformation implementation tasks.
diff --git a/src/org/apache/xpath/XPathCollationSupport.java b/src/org/apache/xpath/XPathCollationSupport.java
new file mode 100644
index 00000000..a903a87e
--- /dev/null
+++ b/src/org/apache/xpath/XPathCollationSupport.java
@@ -0,0 +1,462 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.xpath;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.xml.transform.TransformerException;
+
+/**
+ * This class provides, collation support to XalanJ's XPath 3.1
+ * implementation.
+ *
+ * Ref : https://www.w3.org/TR/xpath-functions-31/#collations
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class XPathCollationSupport {
+
+ public static final String UNICODE_CODEPOINT_COLLATION_URI = "http://www.w3.org/2005/xpath-functions/collation/codepoint";
+
+ public static final String UNICODE_COLLATION_ALGORITHM_URI = "http://www.w3.org/2013/collation/UCA";
+
+ public static final String HTML_ASCII_CASE_INSENSITIVE_COLLATION_URI = "http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive";
+
+ private final String UCA_KEYWORD_FALLBACK = "fallback";
+
+ private final String UCA_FALLBACK_YES = "yes";
+ private final String UCA_FALLBACK_NO = "no";
+
+ private final String UCA_KEYWORD_LANG = "lang";
+
+ private final String UCA_KEYWORD_STRENGTH = "strength";
+
+ private final String UCA_STRENGTH_PRIMARY = "primary";
+ private final String UCA_STRENGTH_SECONDARY = "secondary";
+ private final String UCA_STRENGTH_TERTIARY = "tertiary";
+ private final String UCA_STRENGTH_IDENTICAL = "identical";
+
+ private final String DEFAULT_UCA_FALLBACK_VALUE = UCA_FALLBACK_YES;
+
+ private final Locale DEFAULT_UCA_LOCALE = Locale.getDefault();
+
+ private final String DEFAULT_UCA_STRENGTH_VALUE = UCA_STRENGTH_TERTIARY;
+
+ private final String UCA_QUERY_STRING_PREFIX = "?";
+
+ private final String UCA_QUERY_STRING_PARTS_DELIM = ";";
+
+ private final String UCA_QUERY_STRING_PART_SUB_DELIM = "=";
+
+ private List<UCAParameter> fUcaSupportedParameters = new ArrayList<UCAParameter>();
+
+ // This class field, is needed by unicode collation algorithm
+ private String fQueryStrFallbackValue = null;
+
+ private String fDefaultCollationUri = null;
+
+ /**
+ * Class constructor.
+ */
+ public XPathCollationSupport(String defaultCollationUri) {
+ fDefaultCollationUri = defaultCollationUri;
+ buildSupportedUCAParamList();
+ }
+
+ /**
+ * This method, compares two string values, using a specified collation.
+ *
+ * @param str1 the first string
+ * @param str2 the second string
+ * @param collationUri collation uri
+ *
+ * @return the string comparison result represented as an integer value. The value -1
+ * indicates that string 'str1' collates before string 'str2', the value 1
+ * indicates that string 'str1' collates after string 'str2', the value 0
+ * indicates that string 'str1' is equal to string 'str2'.
+ *
+ * @throws javax.xml.transform.TransformerException
+ */
+ public int compareStringsUsingCollationUri(String str1, String str2, String collationUri)
+ throws javax.xml.transform.TransformerException {
+ int comparisonResult = 0;
+
+ if (UNICODE_CODEPOINT_COLLATION_URI.equals(collationUri)) {
+ comparisonResult = compareStringsUsingUnicodeCodepointCollation(str1, str2);
+ }
+ else if (collationUri.startsWith(UNICODE_COLLATION_ALGORITHM_URI)) {
+ try {
+ Collator collator = getCollatorFromCollationUri(collationUri);
+
+ if (collator != null) {
+ comparisonResult = collator.compare(str1, str2);
+ }
+ else if (UCA_FALLBACK_YES.equals(fQueryStrFallbackValue)) {
+ comparisonResult = compareStringsUsingCollationUri(str1, str2, fDefaultCollationUri);
+ }
+ else {
+ throw new javax.xml.transform.TransformerException("FOCH0002 : The requested collation '" + collationUri + "' is not supported.");
+ }
+ }
+ catch (javax.xml.transform.TransformerException ex) {
+ throw new javax.xml.transform.TransformerException(ex.getMessage());
+ }
+
+ if (comparisonResult < 0) {
+ comparisonResult = -1;
+ }
+ else if (comparisonResult > 0) {
+ comparisonResult = 1;
+ }
+ }
+ else if (HTML_ASCII_CASE_INSENSITIVE_COLLATION_URI.equals(collationUri)) {
+ // REVISIT
+ throw new javax.xml.transform.TransformerException("FOCH0002 : The requested collation '" + collationUri + "' "
+ + "is not supported.");
+ }
+ else {
+ throw new javax.xml.transform.TransformerException("FOCH0002 : The requested collation '" + collationUri + "' "
+ + "is not supported.");
+ }
+
+ return comparisonResult;
+ }
+
+ /**
+ * Given a string, get a corresponding primitive integer array of
+ * the codepoints of all the characters of the string in order.
+ */
+ public int[] getCodepointsFromString(String str) {
+ int[] codePointsArr = null;
+
+ codePointsArr = (str.codePoints()).toArray();
+
+ return codePointsArr;
+ }
+
+ /**
+ * This method compares, two string values using 'unicode codepoint collation'.
+ *
+ * @param str1 the first string
+ * @param str2 the second string
+ *
+ * @return an integer value denoting, the result of comparison
+ */
+ private int compareStringsUsingUnicodeCodepointCollation(String str1, String str2) {
+ int comparisonResult = 0;
+
+ int[] codePointsArr1 = getCodepointsFromString(str1);
+ int[] codePointsArr2 = getCodepointsFromString(str2);
+
+ comparisonResult = compareCodepointArrays(codePointsArr1, codePointsArr2);
+
+ return comparisonResult;
+ }
+
+ /**
+ * Compare two int[] arrays comprising unicode codepoints, according to
+ * 'unicode codepoint collation'.
+ */
+ private int compareCodepointArrays(int[] codePointsArr1, int[] codePointsArr2) {
+
+ int comparisonResult = 0;
+
+ if (((codePointsArr1 == null) || (codePointsArr1.length == 0)) &&
+ ((codePointsArr2 == null) || (codePointsArr2.length == 0))) {
+ // both strings are considered equal
+ comparisonResult = 0;
+ }
+ else if (((codePointsArr1 == null) || (codePointsArr1.length == 0)) &&
+ ((codePointsArr2 != null) && (codePointsArr2.length > 0))) {
+ // the first string is less than the second one
+ comparisonResult = -1;
+ }
+ else if (((codePointsArr1 != null) && (codePointsArr1.length > 0)) &&
+ ((codePointsArr2 == null) || (codePointsArr2.length == 0))) {
+ // the second string is less than the first one
+ comparisonResult = 1;
+ }
+ else {
+ // both the strings to be compared, have non empty code point arrays
+ int arr1FirstCodepoint = codePointsArr1[0];
+ int arr2FirstCodepoint = codePointsArr2[0];
+ if (arr1FirstCodepoint > arr2FirstCodepoint) {
+ comparisonResult = 1;
+ }
+ else if (arr2FirstCodepoint > arr1FirstCodepoint) {
+ comparisonResult = -1;
+ }
+ else {
+ List<Integer> list1 = getIntegerListFromIntArray(codePointsArr1);
+ List<Integer> list2 = getIntegerListFromIntArray(codePointsArr2);
+
+ // get all, but the first item in the list 'list1'
+ list1 = list1.subList(1, list1.size());
+
+ // get all, but the first item in the list 'list2'
+ list2 = list2.subList(1, list2.size());
+
+ // recursive call to this function
+ comparisonResult = compareCodepointArrays(getIntArrayFromIntegerList(list1),
+ getIntArrayFromIntegerList(list2));
+ }
+ }
+
+ return comparisonResult;
+ }
+
+ /**
+ * Given an array of primitive integers, get the corresponding
+ * list of type List<Integer>.
+ */
+ private List<Integer> getIntegerListFromIntArray(int[] intArr) {
+ List<Integer> integerList = new ArrayList<Integer>();
+
+ for (int idx = 0; idx < intArr.length; idx++) {
+ integerList.add(Integer.valueOf(intArr[idx]));
+ }
+
+ return integerList;
+ }
+
+ /**
+ * Given a list of type List<Integer>, get the corresponding array
+ * of primitive integers.
+ */
+ private int[] getIntArrayFromIntegerList(List<Integer> integerList) {
+ int[] intArray = new int[integerList.size()];
+
+ for (int idx = 0; idx < integerList.size(); idx++) {
+ intArray[idx] = (integerList.get(idx)).intValue();
+ }
+
+ return intArray;
+ }
+
+ /**
+ * This method helps to implement, unicode collation algorithm as specified by XPath 3.1 F&O
+ * spec, which in turn is based on UTS #10 (Unicode Technical Standard #10 : Unicode Collation
+ * Algorithm).
+ *
+ * @param collationUri the collation uri, specified by users of XPath 3.1 string comparison
+ * , or sorting of strings.
+ *
+ * @return a configured Java object of type java.text.Collator, that callers of
+ * this method can use to do locale specific string comparisons.
+ *
+ * @throws TransformerException
+ */
+ private Collator getCollatorFromCollationUri(String collationUri) throws TransformerException {
+
+ Collator strComparisonCollator = null;
+
+ try {
+ if (collationUri.equals(UNICODE_COLLATION_ALGORITHM_URI)) {
+ strComparisonCollator = Collator.getInstance(DEFAULT_UCA_LOCALE);
+ strComparisonCollator.setStrength(Collator.TERTIARY);
+ }
+ else {
+ int ucaUriPrefixLength = UNICODE_COLLATION_ALGORITHM_URI.length();
+ String uriAndQueryStrDelim = collationUri.substring(ucaUriPrefixLength, ucaUriPrefixLength + 1);
+
+ if (UCA_QUERY_STRING_PREFIX.equals(uriAndQueryStrDelim)) {
+ String uriQueryStr = collationUri.substring(collationUri.indexOf(UCA_QUERY_STRING_PREFIX) + 1);
+ Map<String, String> queryStrMap = getUCAQueryStrComponents(uriQueryStr);
+
+ String queryStrFallbackValue = queryStrMap.get(UCA_KEYWORD_FALLBACK);
+ String queryStrLangCode = queryStrMap.get(UCA_KEYWORD_LANG);
+ String queryStrStrengthValue = queryStrMap.get(UCA_KEYWORD_STRENGTH);
+
+ if (queryStrFallbackValue == null) {
+ fQueryStrFallbackValue = DEFAULT_UCA_FALLBACK_VALUE;
+ }
+ else {
+ fQueryStrFallbackValue = queryStrFallbackValue;
+ }
+
+ if (queryStrLangCode == null) {
+ queryStrLangCode = DEFAULT_UCA_LOCALE.getCountry();
+ }
+
+ if (queryStrStrengthValue == null) {
+ queryStrStrengthValue = DEFAULT_UCA_STRENGTH_VALUE;
+ }
+
+ strComparisonCollator = Collator.getInstance(new Locale(queryStrLangCode));
+
+ switch (queryStrStrengthValue) {
+ case UCA_STRENGTH_PRIMARY :
+ strComparisonCollator.setStrength(Collator.PRIMARY);
+ break;
+ case UCA_STRENGTH_SECONDARY :
+ strComparisonCollator.setStrength(Collator.SECONDARY);
+ break;
+ case UCA_STRENGTH_TERTIARY :
+ strComparisonCollator.setStrength(Collator.TERTIARY);
+ break;
+ case UCA_STRENGTH_IDENTICAL :
+ strComparisonCollator.setStrength(Collator.IDENTICAL);
+ break;
+ default:
+ // no op
+ }
+ }
+ else {
+ throw new TransformerException("FOCH0002 : The first character if present after collation uri '" +
+ UNICODE_COLLATION_ALGORITHM_URI + "' must be "
+ + "'" + UCA_QUERY_STRING_PREFIX + "', to denote the "
+ + "start of query string within the collation uri.");
+ }
+ }
+ }
+ catch (Exception ex) {
+ throw new TransformerException(ex.getMessage());
+ }
+
+ return strComparisonCollator;
+ }
+
+ /**
+ * From the requested collation uri, build a corresponding java.util.Map object
+ * representation.
+ */
+ private Map<String, String> getUCAQueryStrComponents(String uriQueryStr) throws TransformerException {
+ Map<String, String> queryStrMap = new HashMap<String, String>();
+
+ String[] queryStrParts = uriQueryStr.split(UCA_QUERY_STRING_PARTS_DELIM);
+
+ for (int idx = 0; idx < queryStrParts.length; idx++) {
+ String queryStrPart = queryStrParts[idx];
+ int delimIdx = queryStrPart.indexOf(UCA_QUERY_STRING_PART_SUB_DELIM);
+ String keyword = queryStrPart.substring(0, delimIdx);
+ String value = queryStrPart.substring(delimIdx + 1);
+ if (!queryStrMap.containsKey(keyword)) {
+ if (isUCAKeywordAndValueOk(keyword, value)) {
+ queryStrMap.put(keyword, value);
+ }
+ else {
+ throw new TransformerException("FOCH0002 : The keyword '"+keyword+"' and corresponding value '" +
+ value + "', provided within the "
+ + "requested collation uri is not supported.");
+ }
+ }
+ else {
+ throw new TransformerException("FOCH0002 : The keyword '" + keyword + "' occurs more than once, within "
+ + "the specified collation uri.");
+ }
+ }
+
+ return queryStrMap;
+ }
+
+ /**
+ * Check whether, within requested collation uri's query string, the given
+ * keyword and value is supported by XalanJ's XPath 3.1 processor.
+ */
+ private boolean isUCAKeywordAndValueOk(String keyword, String value) {
+ boolean isUCAKeywordAndValueOk = false;
+
+ for (int idx = 0; idx < fUcaSupportedParameters.size(); idx++) {
+ UCAParameter ucaParameter = fUcaSupportedParameters.get(idx);
+ if ((ucaParameter.getKeywordName()).equals(keyword)) {
+ List<String> paramValues = ucaParameter.getParamValues();
+ if (paramValues.contains(value)) {
+ isUCAKeywordAndValueOk = true;
+ break;
+ }
+ }
+ }
+
+ return isUCAKeywordAndValueOk;
+ }
+
+ /**
+ * This method configures, the collation support provided by
+ * XalanJ XPath 3.1 implementation.
+ */
+ private void buildSupportedUCAParamList() {
+ List<String> fallbackList = new ArrayList<String>();
+ fallbackList.add(UCA_FALLBACK_YES);
+ fallbackList.add(UCA_FALLBACK_NO);
+ UCAParameter ucaFallbackParam = new UCAParameter(UCA_KEYWORD_FALLBACK, fallbackList);
+
+ String[] isoLanguageCodes = Locale.getISOLanguages();
+ List<String> isoLanguageList = Arrays.asList(isoLanguageCodes);
+ UCAParameter ucaLanguageParam = new UCAParameter(UCA_KEYWORD_LANG, isoLanguageList);
+
+ List<String> collationStrengthList = new ArrayList<String>();
+ collationStrengthList.add(UCA_STRENGTH_PRIMARY);
+ collationStrengthList.add(UCA_STRENGTH_SECONDARY);
+ collationStrengthList.add(UCA_STRENGTH_TERTIARY);
+ collationStrengthList.add(UCA_STRENGTH_IDENTICAL);
+ UCAParameter ucaCollationStrengthParam = new UCAParameter(UCA_KEYWORD_STRENGTH, collationStrengthList);
+
+ fUcaSupportedParameters.add(ucaFallbackParam);
+ fUcaSupportedParameters.add(ucaLanguageParam);
+ fUcaSupportedParameters.add(ucaCollationStrengthParam);
+ }
+
+ /**
+ * An object of this class, stores definition of one
+ * "Unicode Collation Algorithm" (UCA) uri query
+ * parameter.
+ */
+ private class UCAParameter {
+
+ // Variable denoting, keyword/parameter name
+ // (for e.g, 'fallback', 'lang', 'strength').
+ private String keywordName;
+
+ // Variable denoting, permitted values for the keyword/parameter
+ // name.
+ // For e.g, the 'fallback' parameter has possible values 'yes'/'no'.
+ // The 'strength' parameter has possible values 'primary',
+ // 'secondary', 'tertiary', 'identical'.
+ private List<String> paramValues;
+
+ public UCAParameter(String keywordName, List<String> paramValues) {
+ this.keywordName = keywordName;
+ this.paramValues = paramValues;
+ }
+
+ public String getKeywordName() {
+ return keywordName;
+ }
+
+ public void setKeywordName(String keywordName) {
+ this.keywordName = keywordName;
+ }
+
+ public List<String> getParamValues() {
+ return paramValues;
+ }
+
+ public void setParamValues(List<String> paramValues) {
+ this.paramValues = paramValues;
+ }
+
+ }
+
+}
diff --git a/src/org/apache/xpath/XPathContext.java b/src/org/apache/xpath/XPathContext.java
index 71ac656d..6ed89a59 100644
--- a/src/org/apache/xpath/XPathContext.java
+++ b/src/org/apache/xpath/XPathContext.java
@@ -138,6 +138,16 @@ public class XPathContext extends DTMManager // implements ExpressionContext
* We don't use, XalanJ XPath context's variable stack for this purpose.
*/
private Map<QName, XObject> xpathVarMap = new HashMap<QName, XObject>();
+
+ /**
+ * The default collation uri.
+ */
+ private String m_default_collation = XPathCollationSupport.UNICODE_CODEPOINT_COLLATION_URI;
+
+ /**
+ * An XPathCollationSupport object instance to support, collations within XPath implementation.
+ */
+ private XPathCollationSupport m_collationSupport = new XPathCollationSupport(m_default_collation);
/**
* Though XPathContext context extends
@@ -1469,5 +1479,13 @@ public class XPathContext extends DTMManager // implements ExpressionContext
public void setXPathVarMap(Map<QName, XObject> xpathVarMap) {
this.xpathVarMap = xpathVarMap;
}
+
+ public XPathCollationSupport getXPathCollationSupport() {
+ return m_collationSupport;
+ }
+
+ public String getDefaultCollation() {
+ return m_default_collation;
+ }
}
diff --git a/src/org/apache/xpath/compiler/FunctionTable.java b/src/org/apache/xpath/compiler/FunctionTable.java
index cf3a2567..e3170717 100644
--- a/src/org/apache/xpath/compiler/FunctionTable.java
+++ b/src/org/apache/xpath/compiler/FunctionTable.java
@@ -269,6 +269,9 @@ public class FunctionTable
/** The 'string-to-codepoints()' id. */
public static final int FUNC_STRING_TO_CODE_POINTS = 81;
+
+ /** The 'compare()' id. */
+ public static final int FUNC_COMPARE = 82;
// Proprietary
@@ -326,7 +329,7 @@ public class FunctionTable
* Number of built in functions. Be sure to update this as
* built-in functions are added.
*/
- private static final int NUM_BUILT_IN_FUNCS = 82;
+ private static final int NUM_BUILT_IN_FUNCS = 83;
/**
* Number of built-in functions that may be added.
@@ -490,6 +493,8 @@ public class FunctionTable
org.apache.xpath.functions.FuncCodePointsToString.class;
m_functions[FUNC_STRING_TO_CODE_POINTS] =
org.apache.xpath.functions.FuncStringToCodepoints.class;
+ m_functions[FUNC_COMPARE] =
+ org.apache.xpath.functions.FuncCompare.class;
}
static{
@@ -661,6 +666,8 @@ public class FunctionTable
new Integer(FunctionTable.FUNC_CODE_POINTS_TO_STRING));
m_functionID.put(Keywords.FUNC_STRING_TO_CODE_POINTS,
new Integer(FunctionTable.FUNC_STRING_TO_CODE_POINTS));
+ m_functionID.put(Keywords.FUNC_COMPARE,
+ new Integer(FunctionTable.FUNC_COMPARE));
}
public FunctionTable(){
diff --git a/src/org/apache/xpath/compiler/Keywords.java b/src/org/apache/xpath/compiler/Keywords.java
index 483d3476..bc6d2764 100644
--- a/src/org/apache/xpath/compiler/Keywords.java
+++ b/src/org/apache/xpath/compiler/Keywords.java
@@ -347,8 +347,14 @@ public class Keywords
/** string-to-codepoints function string. */
public static final String FUNC_STRING_TO_CODE_POINTS = "string-to-codepoints";
+ /** compare function string. */
+ public static final String FUNC_COMPARE = "compare";
+
// XML Schema built-in data type name keywords
+ /** xs:string data type string. */
+ public static final String XS_STRING = "string";
+
/** xs:decimal data type string. */
public static final String XS_DECIMAL = "decimal";
diff --git a/src/org/apache/xpath/functions/FuncCompare.java b/src/org/apache/xpath/functions/FuncCompare.java
new file mode 100644
index 00000000..5c477d6c
--- /dev/null
+++ b/src/org/apache/xpath/functions/FuncCompare.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.xpath.functions;
+
+import java.math.BigInteger;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathCollationSupport;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xpath.xs.types.XSInteger;
+
+/**
+ * Implementation of the compare() function.
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class FuncCompare extends FunctionMultiArgs {
+
+ private static final long serialVersionUID = 4648998919300586767L;
+
+ /**
+ * The number of arguments passed to the fn:compare function
+ * call.
+ */
+ private int numOfArgs = 0;
+
+ /**
+ * Execute the function. The function must return a valid object.
+ *
+ * @param xctxt The current execution context.
+ * @return A valid XObject.
+ *
+ * @throws javax.xml.transform.TransformerException
+ */
+ public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+ {
+
+ XSInteger result = null;
+
+ SourceLocator srcLocator = xctxt.getSAXLocator();
+
+ Expression arg0 = m_arg0;
+ Expression arg1 = m_arg1;
+ Expression arg2 = null;
+
+ XPathCollationSupport xPathCollationSupport = xctxt.getXPathCollationSupport();
+
+ try {
+ if (numOfArgs == 2) {
+ XObject xObject0 = arg0.execute(xctxt);
+ XObject xObject1 = arg1.execute(xctxt);
+
+ String str0 = XslTransformEvaluationHelper.getStrVal(xObject0);
+ String str1 = XslTransformEvaluationHelper.getStrVal(xObject1);
+
+ // set the collation to default collation
+ String collationUri = xctxt.getDefaultCollation();
+
+ int comparisonResult = xPathCollationSupport.compareStringsUsingCollationUri(
+ str0, str1, collationUri);
+
+ result = new XSInteger(BigInteger.valueOf((long)comparisonResult));
+ }
+ else {
+ // a collation uri was, explicitly provided during the function call fn:compare
+
+ arg2 = m_arg2;
+
+ XObject xObject0 = arg0.execute(xctxt);
+ XObject xObject1 = arg1.execute(xctxt);
+
+ XObject xObject2 = arg2.execute(xctxt);
+
+ String str0 = XslTransformEvaluationHelper.getStrVal(xObject0);
+ String str1 = XslTransformEvaluationHelper.getStrVal(xObject1);
+
+ String collationUri = XslTransformEvaluationHelper.getStrVal(xObject2);
+
+ int comparisonResult = xPathCollationSupport.compareStringsUsingCollationUri(
+ str0, str1, collationUri);
+
+ result = new XSInteger(BigInteger.valueOf((long)comparisonResult));
+ }
+ }
+ catch (javax.xml.transform.TransformerException ex) {
+ throw new javax.xml.transform.TransformerException(ex.getMessage(), srcLocator);
+ }
+
+ return result;
+ }
+
+ /**
+ * Check that the number of arguments passed to this function is correct.
+ *
+ * @param argNum The number of arguments that is being passed to the function.
+ *
+ * @throws WrongNumberArgsException
+ */
+ public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+ {
+ if (!(argNum > 1 && argNum <= 3)) {
+ reportWrongNumberArgs();
+ }
+ else {
+ numOfArgs = argNum;
+ }
+ }
+
+ /**
+ * Constructs and throws a WrongNumberArgException with the appropriate
+ * message for this function object.
+ *
+ * @throws WrongNumberArgsException
+ */
+ protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+ throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(
+ XPATHErrorResources.ER_TWO_OR_THREE,
+ null));
+ }
+
+}
diff --git a/src/org/apache/xpath/functions/FuncStringToCodepoints.java b/src/org/apache/xpath/functions/FuncStringToCodepoints.java
index b132c90b..a0de7117 100644
--- a/src/org/apache/xpath/functions/FuncStringToCodepoints.java
+++ b/src/org/apache/xpath/functions/FuncStringToCodepoints.java
@@ -22,6 +22,7 @@ package org.apache.xpath.functions;
import java.math.BigInteger;
+import org.apache.xpath.XPathCollationSupport;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XObject;
@@ -55,7 +56,9 @@ public class FuncStringToCodepoints extends FunctionDef1Arg
String inpStr = (getArg0AsString(xctxt)).toString();
- int[] codePointsArr = (inpStr.codePoints()).toArray();
+ XPathCollationSupport xPathCollationSupport = xctxt.getXPathCollationSupport();
+
+ int[] codePointsArr = xPathCollationSupport.getCodepointsFromString(inpStr);
ResultSequence resultSeq = new ResultSequence();
diff --git a/src/org/apache/xpath/xs/types/XSString.java b/src/org/apache/xpath/xs/types/XSString.java
new file mode 100644
index 00000000..c933460b
--- /dev/null
+++ b/src/org/apache/xpath/xs/types/XSString.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.xpath.xs.types;
+
+import org.apache.xpath.objects.ResultSequence;
+
+/**
+ * An XML Schema data type representation, of the xs:string datatype.
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class XSString extends XSCtrType {
+
+ private static final long serialVersionUID = -7351932310979358488L;
+
+ private static final String XS_STRING = "xs:string";
+
+ private String _value;
+
+ /*
+ * Class constructor.
+ */
+ public XSString(String str) {
+ _value = str;
+ }
+
+ /*
+ * Class constructor.
+ */
+ public XSString() {
+ this(null);
+ }
+
+ @Override
+ public ResultSequence constructor(ResultSequence arg) {
+ ResultSequence resultSeq = new ResultSequence();
+
+ if (arg.size() == 0) {
+ return resultSeq;
+ }
+
+ XSAnyType xsAnyType = (XSAnyType)arg.item(0);
+
+ resultSeq.add(new XSString(xsAnyType.stringValue()));
+
+ return resultSeq;
+ }
+
+ @Override
+ public String typeName() {
+ return "string";
+ }
+
+ @Override
+ public String stringType() {
+ return XS_STRING;
+ }
+
+ @Override
+ public String stringValue() {
+ return _value;
+ }
+
+ /**
+ * Get the actual string value stored, within this object.
+ *
+ * @return the actual string value stored
+ */
+ public String value() {
+ return stringValue();
+ }
+
+ public boolean equals(XSString xsStr) {
+ // TO DO
+ return false;
+ }
+
+ public boolean lt(XSString xsStr) {
+ // TO DO
+ return false;
+ }
+
+ public boolean gt(XSString xsStr) {
+ // TO DO
+ return false;
+ }
+
+}
diff --git a/tests/fn_compare/gold/test1.out b/tests/fn_compare/gold/test1.out
new file mode 100644
index 00000000..bf104997
--- /dev/null
+++ b/tests/fn_compare/gold/test1.out
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <one>0</one>
+ <two>-1</two>
+ <three>0</three>
+ <four>-1</four>
+</result>
diff --git a/tests/fn_compare/gold/test2.out b/tests/fn_compare/gold/test2.out
new file mode 100644
index 00000000..84f7cb10
--- /dev/null
+++ b/tests/fn_compare/gold/test2.out
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <one>0</one>
+ <two>-1</two>
+ <three>0</three>
+ <four>-1</four>
+ <five>0</five>
+ <six>-1</six>
+ <seven>0</seven>
+ <eight>-1</eight>
+</result>
diff --git a/tests/fn_compare/gold/test3.out b/tests/fn_compare/gold/test3.out
new file mode 100644
index 00000000..6ac8e133
--- /dev/null
+++ b/tests/fn_compare/gold/test3.out
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <one>0</one>
+ <two>-1</two>
+ <three>-1</three>
+ <four>-1</four>
+</result>
diff --git a/tests/fn_compare/test1.xsl b/tests/fn_compare/test1.xsl
new file mode 100644
index 00000000..f2334f82
--- /dev/null
+++ b/tests/fn_compare/test1.xsl
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- Test for the XPath 3.1 fn:compare() function. This stylesheet
+ test case, borrows fn:compare function examples from XPath 3.1
+ F&O spec.
+
+ Whereever within fn:compare function calls, if the third argument
+ (for the collation to be used) is not present, then XalanJ uses
+ the collation 'unicode codepoint collation' as its default
+ collation.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/">
+ <result>
+ <one><xsl:value-of select="compare('abc', 'abc')"/></one>
+ <two><xsl:value-of select="compare('Strasse', 'Stra�e')"/></two>
+ <three><xsl:value-of select="compare('Strasse', 'Stra�e', 'http://www.w3.org/2013/collation/UCA?lang=de;strength=primary')"/></three>
+ <four><xsl:value-of select="compare('Strassen', 'Stra�e')"/></four>
+ </result>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/tests/fn_compare/test1_a.xml b/tests/fn_compare/test1_a.xml
new file mode 100644
index 00000000..a5964b97
--- /dev/null
+++ b/tests/fn_compare/test1_a.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document>
+ <elem>
+ <str1>abc</str1>
+ <str2>abc</str2>
+ </elem>
+ <elem>
+ <str1>Strasse</str1>
+ <str2>Straße</str2>
+ </elem>
+ <elem>
+ <str1>Strasse</str1>
+ <str2>Straße</str2>
+ </elem>
+ <elem>
+ <str1>Strassen</str1>
+ <str2>Straße</str2>
+ </elem>
+ <data str1="abc" str2="abc"/>
+ <data str1="Strasse" str2="Straße"/>
+ <data str1="Strasse" str2="Straße"/>
+ <data str1="Strassen" str2="Straße"/>
+</document>
\ No newline at end of file
diff --git a/tests/fn_compare/test2.xsl b/tests/fn_compare/test2.xsl
new file mode 100644
index 00000000..cfd17f25
--- /dev/null
+++ b/tests/fn_compare/test2.xsl
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1_a.xml -->
+
+ <!-- Test for the XPath 3.1 fn:compare() function. This stylesheet
+ test case, borrows fn:compare function examples from XPath 3.1
+ F&O spec. This stylesheet, reads input data to be transformed
+ from an XML external document.
+
+ Whereever within fn:compare function calls, if the third argument
+ (for the collation to be used) is not present, then XalanJ uses
+ the collation 'unicode codepoint collation' as its default
+ collation.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/document">
+ <result>
+ <one><xsl:value-of select="compare(elem[1]/str1, elem[1]/str2)"/></one>
+ <two><xsl:value-of select="compare(elem[2]/str1, elem[2]/str2)"/></two>
+ <three><xsl:value-of select="compare(elem[3]/str1, elem[3]/str2, 'http://www.w3.org/2013/collation/UCA?lang=de;strength=primary')"/></three>
+ <four><xsl:value-of select="compare(elem[4]/str1, elem[4]/str2)"/></four>
+
+ <five><xsl:value-of select="compare(data[1]/@str1, data[1]/@str2)"/></five>
+ <six><xsl:value-of select="compare(data[2]/@str1, data[2]/@str2)"/></six>
+ <seven><xsl:value-of select="compare(data[3]/@str1, data[3]/@str2, 'http://www.w3.org/2013/collation/UCA?lang=de;strength=primary')"/></seven>
+ <eight><xsl:value-of select="compare(data[4]/@str1, data[4]/@str2)"/></eight>
+ </result>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/tests/fn_compare/test3.xsl b/tests/fn_compare/test3.xsl
new file mode 100644
index 00000000..f76e504c
--- /dev/null
+++ b/tests/fn_compare/test3.xsl
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- Test for the XPath 3.1 fn:compare() function.
+
+ Whereever within fn:compare function calls, if the third argument
+ (for the collation to be used) is not present, then XalanJ uses
+ the collation 'unicode codepoint collation' as its default
+ collation.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/">
+ <result>
+ <one><xsl:value-of select="compare('Strasse', 'Stra�e', 'http://www.w3.org/2013/collation/UCA?lang=de;strength=primary')"/></one>
+ <two><xsl:value-of select="compare('Strasse', 'Stra�e', 'http://www.w3.org/2013/collation/UCA?lang=de;strength=identical')"/></two>
+ <three><xsl:value-of select="compare('Strasse', 'Stra�e')"/></three>
+ <four><xsl:value-of select="compare('Strasse', 'Stra�e', 'http://www.w3.org/2005/xpath-functions/collation/codepoint')"/></four>
+ </result>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/tests/grouping/gold/test25.out b/tests/grouping/gold/test25.out
new file mode 100644
index 00000000..59f0a46f
--- /dev/null
+++ b/tests/grouping/gold/test25.out
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?><document>
+ <personList profession="Software Development">
+ <person>
+ <id>1</id>
+ <name>Michael Glavassevich</name>
+ </person>
+ <person>
+ <id>2</id>
+ <name>Joseph Kessselman</name>
+ </person>
+ <person>
+ <id>5</id>
+ <name>Mukul Gandhi</name>
+ </person>
+ <person>
+ <id>6</id>
+ <name>Gary Gregory</name>
+ </person>
+ </personList>
+ <personList profession="Finance">
+ <person>
+ <id>3</id>
+ <name>Dave Carver</name>
+ </person>
+ </personList>
+ <personList profession="IT Support">
+ <person>
+ <id>4</id>
+ <name>Mary Holstege</name>
+ </person>
+ </personList>
+</document>
diff --git a/tests/grouping/test1_g.xml b/tests/grouping/test1_g.xml
new file mode 100644
index 00000000..14cb1fa7
--- /dev/null
+++ b/tests/grouping/test1_g.xml
@@ -0,0 +1,50 @@
+<document>
+ <info>
+ <info>
+ <person id="1">
+ <name>Michael Glavassevich</name>
+ <profession>Software Development</profession>
+ </person>
+ </info>
+ </info>
+ <info>
+ <info>
+ <person id="2">
+ <name>Joseph Kessselman</name>
+ <profession>Software Development</profession>
+ </person>
+ </info>
+ </info>
+ <info>
+ <info>
+ <person id="3">
+ <name>Dave Carver</name>
+ <profession>Finance</profession>
+ </person>
+ </info>
+ </info>
+ <info>
+ <info>
+ <person id="4">
+ <name>Mary Holstege</name>
+ <profession>IT Support</profession>
+ </person>
+ </info>
+ </info>
+ <info>
+ <info>
+ <person id="5">
+ <name>Mukul Gandhi</name>
+ <profession>Software Development</profession>
+ </person>
+ </info>
+ </info>
+ <info>
+ <info>
+ <person id="6">
+ <name>Gary Gregory</name>
+ <profession>Software Development</profession>
+ </person>
+ </info>
+ </info>
+</document>
\ No newline at end of file
diff --git a/tests/grouping/test25.xsl b/tests/grouping/test25.xsl
new file mode 100644
index 00000000..a0c60ccd
--- /dev/null
+++ b/tests/grouping/test25.xsl
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1_g.xml -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/document">
+ <document>
+ <xsl:variable name="seq1" select="for $person in .//person return $person"/>
+ <xsl:for-each-group select="$seq1" group-by="profession">
+ <personList profession="{current-grouping-key()}">
+ <xsl:apply-templates select="current-group()"/>
+ </personList>
+ </xsl:for-each-group>
+ </document>
+ </xsl:template>
+
+ <xsl:template match="person">
+ <person>
+ <id><xsl:value-of select="@id"/></id>
+ <name><xsl:value-of select="name"/></name>
+ </person>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/tests/org/apache/xalan/xpath3/FnCompareTests.java b/tests/org/apache/xalan/xpath3/FnCompareTests.java
new file mode 100644
index 00000000..8ea56aad
--- /dev/null
+++ b/tests/org/apache/xalan/xpath3/FnCompareTests.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.xalan.xpath3;
+
+import org.apache.xalan.util.XslTransformTestsUtil;
+import org.apache.xalan.xslt3.XSLConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 function fn:compare test cases.
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class FnCompareTests extends XslTransformTestsUtil {
+
+ private static final String XSL_TRANSFORM_INPUT_DIRPATH = XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "fn_compare/";
+
+ private static final String XSL_TRANSFORM_GOLD_DIRPATH = XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "fn_compare/gold/";
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ // no op
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ xmlDocumentBuilderFactory = null;
+ xmlDocumentBuilder = null;
+ xslTransformerFactory = null;
+ }
+
+ @Test
+ public void xslFnCompareTest1() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test1.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xslFnCompareTest2() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_a.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test2.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test2.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xslFnCompareTest3() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test3.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+}
diff --git a/tests/org/apache/xalan/xslt3/AllXsl3Tests.java b/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
index fbd2e753..75169a3e 100644
--- a/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
+++ b/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
@@ -21,6 +21,7 @@ import org.apache.xalan.xpath3.XsDurationComponentExtractionFunctionTests;
import org.apache.xalan.xpath3.DynamicFunctionCallTests;
import org.apache.xalan.xpath3.FnAbsTests;
import org.apache.xalan.xpath3.FnCodepointsToStringTests;
+import org.apache.xalan.xpath3.FnCompareTests;
import org.apache.xalan.xpath3.FnDistinctValuesTests;
import org.apache.xalan.xpath3.FnFilterTests;
import org.apache.xalan.xpath3.FnFoldLeftTests;
@@ -80,7 +81,7 @@ import org.junit.runners.Suite.SuiteClasses;
XsDurationComponentExtractionFunctionTests.class, XPathArithmeticOnDurationValuesTests.class,
NodeComparisonTests.class, SimpleMapOperatorTests.class, FnFoldLeftTests.class,
FnFoldRightTests.class, FnForEachPairTests.class, FnSortTests.class, FnCodepointsToStringTests.class,
- FnStringToCodepointsTests.class })
+ FnStringToCodepointsTests.class, FnCompareTests.class })
public class AllXsl3Tests {
}
diff --git a/tests/org/apache/xalan/xslt3/GroupingTests.java b/tests/org/apache/xalan/xslt3/GroupingTests.java
index 43daf667..99cc4b9e 100644
--- a/tests/org/apache/xalan/xslt3/GroupingTests.java
+++ b/tests/org/apache/xalan/xslt3/GroupingTests.java
@@ -289,5 +289,15 @@ public class GroupingTests extends XslTransformTestsUtil {
runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
}
+
+ @Test
+ public void xslGroupingTest25() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_g.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test25.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test25.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org