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