1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: NodeSortRecord.java,v 1.19 2004/02/27 01:58:29 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.dom;
  20. import java.text.CollationKey;
  21. import java.text.Collator;
  22. import java.util.Locale;
  23. import com.sun.org.apache.xalan.internal.xsltc.CollatorFactory;
  24. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  25. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  26. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  27. import com.sun.org.apache.xml.internal.utils.StringComparable;
  28. /**
  29. * Base class for sort records containing application specific sort keys
  30. */
  31. public abstract class NodeSortRecord {
  32. public static final int COMPARE_STRING = 0;
  33. public static final int COMPARE_NUMERIC = 1;
  34. public static final int COMPARE_ASCENDING = 0;
  35. public static final int COMPARE_DESCENDING = 1;
  36. /**
  37. * A reference to a collator. May be updated by subclass if the stylesheet
  38. * specifies a different language (will be updated iff _locale is updated).
  39. * @deprecated This field continues to exist for binary compatibility.
  40. * New code should not refer to it.
  41. */
  42. private static final Collator DEFAULT_COLLATOR = Collator.getInstance();
  43. /**
  44. * A reference to the first Collator
  45. * @deprecated This field continues to exist for binary compatibility.
  46. * New code should not refer to it.
  47. */
  48. protected Collator _collator = DEFAULT_COLLATOR;
  49. protected Collator[] _collators;
  50. /**
  51. * A locale field that might be set by an instance of a subclass.
  52. * @deprecated This field continues to exist for binary compatibility.
  53. * New code should not refer to it.
  54. */
  55. protected Locale _locale;
  56. protected CollatorFactory _collatorFactory;
  57. protected SortSettings _settings;
  58. private DOM _dom = null;
  59. private int _node; // The position in the current iterator
  60. private int _last = 0; // Number of nodes in the current iterator
  61. private int _scanned = 0; // Number of key levels extracted from DOM
  62. private Object[] _values; // Contains Comparable objects
  63. /**
  64. * This constructor is run by a call to ClassLoader in the
  65. * makeNodeSortRecord method in the NodeSortRecordFactory class. Since we
  66. * cannot pass any parameters to the constructor in that case we just set
  67. * the default values here and wait for new values through initialize().
  68. */
  69. public NodeSortRecord(int node) {
  70. _node = node;
  71. }
  72. public NodeSortRecord() {
  73. this(0);
  74. }
  75. /**
  76. * This method allows the caller to set the values that could not be passed
  77. * to the default constructor.
  78. */
  79. public final void initialize(int node, int last, DOM dom,
  80. SortSettings settings)
  81. throws TransletException
  82. {
  83. _dom = dom;
  84. _node = node;
  85. _last = last;
  86. _settings = settings;
  87. int levels = settings.getSortOrders().length;
  88. _values = new Object[levels];
  89. // -- W. Eliot Kimber (eliot@isogen.com)
  90. String colFactClassname =
  91. System.getProperty("com.sun.org.apache.xalan.internal.xsltc.COLLATOR_FACTORY");
  92. if (colFactClassname != null) {
  93. try {
  94. Object candObj = ObjectFactory.findProviderClass(
  95. colFactClassname, ObjectFactory.findClassLoader(), true);
  96. _collatorFactory = (CollatorFactory)candObj;
  97. } catch (ClassNotFoundException e) {
  98. throw new TransletException(e);
  99. }
  100. Locale[] locales = settings.getLocales();
  101. _collators = new Collator[levels];
  102. for (int i = 0; i < levels; i++){
  103. _collators[i] = _collatorFactory.getCollator(locales[i]);
  104. }
  105. _collator = _collators[0];
  106. } else {
  107. _collators = settings.getCollators();
  108. _collator = _collators[0];
  109. }
  110. }
  111. /**
  112. * Returns the node for this sort object
  113. */
  114. public final int getNode() {
  115. return _node;
  116. }
  117. /**
  118. *
  119. */
  120. public final int compareDocOrder(NodeSortRecord other) {
  121. return _node - other._node;
  122. }
  123. /**
  124. * Get the string or numeric value of a specific level key for this sort
  125. * element. The value is extracted from the DOM if it is not already in
  126. * our sort key vector.
  127. */
  128. private final Comparable stringValue(int level) {
  129. // Get value from our array if possible
  130. if (_scanned <= level) {
  131. AbstractTranslet translet = _settings.getTranslet();
  132. Locale[] locales = _settings.getLocales();
  133. String[] caseOrder = _settings.getCaseOrders();
  134. // Get value from DOM if accessed for the first time
  135. final String str = extractValueFromDOM(_dom, _node, level,
  136. translet, _last);
  137. final Comparable key =
  138. StringComparable.getComparator(str, locales[level],
  139. _collators[level],
  140. caseOrder[level]);
  141. _values[_scanned++] = key;
  142. return(key);
  143. }
  144. return((Comparable)_values[level]);
  145. }
  146. private final Double numericValue(int level) {
  147. // Get value from our vector if possible
  148. if (_scanned <= level) {
  149. AbstractTranslet translet = _settings.getTranslet();
  150. // Get value from DOM if accessed for the first time
  151. final String str = extractValueFromDOM(_dom, _node, level,
  152. translet, _last);
  153. Double num;
  154. try {
  155. num = new Double(str);
  156. }
  157. // Treat number as NaN if it cannot be parsed as a double
  158. catch (NumberFormatException e) {
  159. num = new Double(Double.NEGATIVE_INFINITY);
  160. }
  161. _values[_scanned++] = num;
  162. return(num);
  163. }
  164. return((Double)_values[level]);
  165. }
  166. /**
  167. * Compare this sort element to another. The first level is checked first,
  168. * and we proceed to the next level only if the first level keys are
  169. * identical (and so the key values may not even be extracted from the DOM)
  170. *
  171. * !!!!MUST OPTIMISE - THIS IS REALLY, REALLY SLOW!!!!
  172. */
  173. public int compareTo(NodeSortRecord other) {
  174. int cmp, level;
  175. int[] sortOrder = _settings.getSortOrders();
  176. int levels = _settings.getSortOrders().length;
  177. int[] compareTypes = _settings.getTypes();
  178. for (level = 0; level < levels; level++) {
  179. // Compare the two nodes either as numeric or text values
  180. if (compareTypes[level] == COMPARE_NUMERIC) {
  181. final Double our = numericValue(level);
  182. final Double their = other.numericValue(level);
  183. cmp = our.compareTo(their);
  184. }
  185. else {
  186. final Comparable our = stringValue(level);
  187. final Comparable their = other.stringValue(level);
  188. cmp = our.compareTo(their);
  189. }
  190. // Return inverse compare value if inverse sort order
  191. if (cmp != 0) {
  192. return sortOrder[level] == COMPARE_DESCENDING ? 0 - cmp : cmp;
  193. }
  194. }
  195. // Compare based on document order if all sort keys are equal
  196. return(_node - other._node);
  197. }
  198. /**
  199. * Returns the array of Collators used for text comparisons in this object.
  200. * May be overridden by inheriting classes
  201. */
  202. public Collator[] getCollator() {
  203. return _collators;
  204. }
  205. /**
  206. * Extract the sort value for a level of this key.
  207. */
  208. public abstract String extractValueFromDOM(DOM dom, int current, int level,
  209. AbstractTranslet translet,
  210. int last);
  211. }