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