1 /* 2 * Copyright (c) 2017, 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.utils.ObjectFactory; 24 import com.sun.org.apache.xalan.internal.xsltc.CollatorFactory; 25 import com.sun.org.apache.xalan.internal.xsltc.DOM; 26 import com.sun.org.apache.xalan.internal.xsltc.TransletException; 27 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 28 import com.sun.org.apache.xml.internal.utils.StringComparable; 29 import java.text.Collator; 30 import java.util.Locale; 31 import jdk.xml.internal.SecuritySupport; 32 33 /** 34 * Base class for sort records containing application specific sort keys 35 * 36 * @LastModified: Oct 2017 37 */ 38 public abstract class NodeSortRecord { 39 public static final int COMPARE_STRING = 0; 40 public static final int COMPARE_NUMERIC = 1; 41 42 public static final int COMPARE_ASCENDING = 0; 43 public static final int COMPARE_DESCENDING = 1; 44 45 /** 46 * A reference to a collator. May be updated by subclass if the stylesheet 47 * specifies a different language (will be updated iff _locale is updated). 48 * @deprecated This field continues to exist for binary compatibility. 49 * New code should not refer to it. 50 */ 51 @Deprecated 52 private static final Collator DEFAULT_COLLATOR = Collator.getInstance(); 53 54 /** 55 * A reference to the first Collator 56 * @deprecated This field continues to exist for binary compatibility. 57 * New code should not refer to it. 58 */ 59 @Deprecated 60 protected Collator _collator = DEFAULT_COLLATOR; 61 protected Collator[] _collators; 62 63 /** 64 * A locale field that might be set by an instance of a subclass. 65 * @deprecated This field continues to exist for binary compatibility. 66 * New code should not refer to it. 67 */ 68 @Deprecated 69 protected Locale _locale; 70 71 protected CollatorFactory _collatorFactory; 72 73 protected SortSettings _settings; 74 75 private DOM _dom = null; 76 private int _node; // The position in the current iterator 77 private int _last = 0; // Number of nodes in the current iterator 78 private int _scanned = 0; // Number of key levels extracted from DOM 79 80 private Object[] _values; // Contains Comparable objects 81 82 /** 83 * This constructor is run by a call to ClassLoader in the 84 * makeNodeSortRecord method in the NodeSortRecordFactory class. Since we 85 * cannot pass any parameters to the constructor in that case we just set 86 * the default values here and wait for new values through initialize(). 87 */ 88 public NodeSortRecord(int node) { 89 _node = node; 90 } 91 92 public NodeSortRecord() { 93 this(0); 94 } 95 96 /** 97 * This method allows the caller to set the values that could not be passed 98 * to the default constructor. 99 */ 100 public final void initialize(int node, int last, DOM dom, 101 SortSettings settings) 102 throws TransletException 103 { 104 _dom = dom; 105 _node = node; 106 _last = last; 107 _settings = settings; 108 109 int levels = settings.getSortOrders().length; 110 _values = new Object[levels]; 111 112 String colFactClassname = null; 113 try { 114 // -- W. Eliot Kimber (eliot@isogen.com) 115 colFactClassname = 116 SecuritySupport.getSystemProperty("com.sun.org.apache.xalan.internal.xsltc.COLLATOR_FACTORY"); 117 } 118 catch (SecurityException e) { 119 // If we can't read the propery, just use default collator 120 } 121 122 if (colFactClassname != null) { 123 try { 124 Object candObj = ObjectFactory.findProviderClass(colFactClassname, true); 125 _collatorFactory = (CollatorFactory)candObj; 126 } catch (ClassNotFoundException e) { 127 throw new TransletException(e); 128 } 129 Locale[] locales = settings.getLocales(); 130 _collators = new Collator[levels]; 131 for (int i = 0; i < levels; i++){ 132 _collators[i] = _collatorFactory.getCollator(locales[i]); 133 } 134 _collator = _collators[0]; 135 } else { 136 _collators = settings.getCollators(); 137 _collator = _collators[0]; 138 } 139 } 140 141 /** 142 * Returns the node for this sort object 143 */ 144 public final int getNode() { 145 return _node; 146 } 147 148 /** 149 * 150 */ 151 public final int compareDocOrder(NodeSortRecord other) { 152 return _node - other._node; 153 } 154 155 /** 156 * Get the string or numeric value of a specific level key for this sort 157 * element. The value is extracted from the DOM if it is not already in 158 * our sort key vector. 159 */ 160 @SuppressWarnings({"rawtypes", "unchecked"}) 161 private final Comparable stringValue(int level) { 162 // Get value from our array if possible 163 if (_scanned <= level) { 164 AbstractTranslet translet = _settings.getTranslet(); 165 Locale[] locales = _settings.getLocales(); 166 String[] caseOrder = _settings.getCaseOrders(); 167 168 // Get value from DOM if accessed for the first time 169 final String str = extractValueFromDOM(_dom, _node, level, 170 translet, _last); 171 final Comparable key = StringComparable.getComparator( 172 str, locales[level], _collators[level], caseOrder[level]); 173 _values[_scanned++] = key; 174 return(key); 175 } 176 return((Comparable)_values[level]); 177 } 178 179 private final Double numericValue(int level) { 180 // Get value from our vector if possible 181 if (_scanned <= level) { 182 AbstractTranslet translet = _settings.getTranslet(); 183 184 // Get value from DOM if accessed for the first time 185 final String str = extractValueFromDOM(_dom, _node, level, 186 translet, _last); 187 Double num; 188 try { 189 num = Double.parseDouble(str); 190 } 191 // Treat number as NaN if it cannot be parsed as a double 192 catch (NumberFormatException e) { 193 num = Double.NEGATIVE_INFINITY; 194 } 195 _values[_scanned++] = num; 196 return(num); 197 } 198 return((Double)_values[level]); 199 } 200 201 /** 202 * Compare this sort element to another. The first level is checked first, 203 * and we proceed to the next level only if the first level keys are 204 * identical (and so the key values may not even be extracted from the DOM) 205 * 206 * !!!!MUST OPTIMISE - THIS IS REALLY, REALLY SLOW!!!! 207 */ 208 @SuppressWarnings({"rawtypes", "unchecked"}) 209 public int compareTo(NodeSortRecord other) { 210 int cmp, level; 211 int[] sortOrder = _settings.getSortOrders(); 212 int levels = _settings.getSortOrders().length; 213 int[] compareTypes = _settings.getTypes(); 214 215 for (level = 0; level < levels; level++) { 216 // Compare the two nodes either as numeric or text values 217 if (compareTypes[level] == COMPARE_NUMERIC) { 218 final Double our = numericValue(level); 219 final Double their = other.numericValue(level); 220 cmp = our.compareTo(their); 221 } 222 else { 223 final Comparable our = stringValue(level); 224 final Comparable their = other.stringValue(level); 225 cmp = our.compareTo(their); 226 } 227 228 // Return inverse compare value if inverse sort order 229 if (cmp != 0) { 230 return sortOrder[level] == COMPARE_DESCENDING ? 0 - cmp : cmp; 231 } 232 } 233 // Compare based on document order if all sort keys are equal 234 return(_node - other._node); 235 } 236 237 /** 238 * Returns the array of Collators used for text comparisons in this object. 239 * May be overridden by inheriting classes 240 */ 241 public Collator[] getCollator() { 242 return _collators; 243 } 244 245 /** 246 * Extract the sort value for a level of this key. 247 */ 248 public abstract String extractValueFromDOM(DOM dom, int current, int level, 249 AbstractTranslet translet, 250 int last); 251 252 }