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 java.text.CollationKey; 24 import java.text.Collator; 25 import java.util.Locale; 26 27 import com.sun.org.apache.xalan.internal.xsltc.CollatorFactory; 28 import com.sun.org.apache.xalan.internal.xsltc.DOM; 29 import com.sun.org.apache.xalan.internal.xsltc.TransletException; 30 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 31 import com.sun.org.apache.xml.internal.utils.StringComparable; 32 import com.sun.org.apache.xalan.internal.utils.ObjectFactory; 33 import com.sun.org.apache.xalan.internal.utils.SecuritySupport; 34 35 /** 36 * Base class for sort records containing application specific sort keys 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 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 = 171 StringComparable.getComparator(str, locales[level], 172 _collators[level], 173 caseOrder[level]); 174 _values[_scanned++] = key; 175 return(key); 176 } 177 return((Comparable)_values[level]); 178 } 179 180 private final Double numericValue(int level) { 181 // Get value from our vector if possible 182 if (_scanned <= level) { 183 AbstractTranslet translet = _settings.getTranslet(); 184 185 // Get value from DOM if accessed for the first time 186 final String str = extractValueFromDOM(_dom, _node, level, 187 translet, _last); 188 Double num; 189 try { 190 num = new Double(str); 191 } 192 // Treat number as NaN if it cannot be parsed as a double 193 catch (NumberFormatException e) { 194 num = new Double(Double.NEGATIVE_INFINITY); 195 } 196 _values[_scanned++] = num; 197 return(num); 198 } 199 return((Double)_values[level]); 200 } 201 202 /** 203 * Compare this sort element to another. The first level is checked first, 204 * and we proceed to the next level only if the first level keys are 205 * identical (and so the key values may not even be extracted from the DOM) 206 * 207 * !!!!MUST OPTIMISE - THIS IS REALLY, REALLY SLOW!!!! 208 */ 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 }