001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: NodeSortRecord.java 468651 2006-10-28 07:04:25Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.dom; 023 024 import java.text.CollationKey; 025 import java.text.Collator; 026 import java.util.Locale; 027 028 import org.apache.xalan.xsltc.CollatorFactory; 029 import org.apache.xalan.xsltc.DOM; 030 import org.apache.xalan.xsltc.TransletException; 031 import org.apache.xalan.xsltc.runtime.AbstractTranslet; 032 import org.apache.xml.utils.StringComparable; 033 034 /** 035 * Base class for sort records containing application specific sort keys 036 */ 037 public abstract class NodeSortRecord { 038 public static final int COMPARE_STRING = 0; 039 public static final int COMPARE_NUMERIC = 1; 040 041 public static final int COMPARE_ASCENDING = 0; 042 public static final int COMPARE_DESCENDING = 1; 043 044 /** 045 * A reference to a collator. May be updated by subclass if the stylesheet 046 * specifies a different language (will be updated iff _locale is updated). 047 * @deprecated This field continues to exist for binary compatibility. 048 * New code should not refer to it. 049 */ 050 private static final Collator DEFAULT_COLLATOR = Collator.getInstance(); 051 052 /** 053 * A reference to the first Collator 054 * @deprecated This field continues to exist for binary compatibility. 055 * New code should not refer to it. 056 */ 057 protected Collator _collator = DEFAULT_COLLATOR; 058 protected Collator[] _collators; 059 060 /** 061 * A locale field that might be set by an instance of a subclass. 062 * @deprecated This field continues to exist for binary compatibility. 063 * New code should not refer to it. 064 */ 065 protected Locale _locale; 066 067 protected CollatorFactory _collatorFactory; 068 069 protected SortSettings _settings; 070 071 private DOM _dom = null; 072 private int _node; // The position in the current iterator 073 private int _last = 0; // Number of nodes in the current iterator 074 private int _scanned = 0; // Number of key levels extracted from DOM 075 076 private Object[] _values; // Contains Comparable objects 077 078 /** 079 * This constructor is run by a call to ClassLoader in the 080 * makeNodeSortRecord method in the NodeSortRecordFactory class. Since we 081 * cannot pass any parameters to the constructor in that case we just set 082 * the default values here and wait for new values through initialize(). 083 */ 084 public NodeSortRecord(int node) { 085 _node = node; 086 } 087 088 public NodeSortRecord() { 089 this(0); 090 } 091 092 /** 093 * This method allows the caller to set the values that could not be passed 094 * to the default constructor. 095 */ 096 public final void initialize(int node, int last, DOM dom, 097 SortSettings settings) 098 throws TransletException 099 { 100 _dom = dom; 101 _node = node; 102 _last = last; 103 _settings = settings; 104 105 int levels = settings.getSortOrders().length; 106 _values = new Object[levels]; 107 108 // -- W. Eliot Kimber (eliot@isogen.com) 109 String colFactClassname = 110 System.getProperty("org.apache.xalan.xsltc.COLLATOR_FACTORY"); 111 112 if (colFactClassname != null) { 113 try { 114 Object candObj = ObjectFactory.findProviderClass( 115 colFactClassname, ObjectFactory.findClassLoader(), true); 116 _collatorFactory = (CollatorFactory)candObj; 117 } catch (ClassNotFoundException e) { 118 throw new TransletException(e); 119 } 120 Locale[] locales = settings.getLocales(); 121 _collators = new Collator[levels]; 122 for (int i = 0; i < levels; i++){ 123 _collators[i] = _collatorFactory.getCollator(locales[i]); 124 } 125 _collator = _collators[0]; 126 } else { 127 _collators = settings.getCollators(); 128 _collator = _collators[0]; 129 } 130 } 131 132 /** 133 * Returns the node for this sort object 134 */ 135 public final int getNode() { 136 return _node; 137 } 138 139 /** 140 * 141 */ 142 public final int compareDocOrder(NodeSortRecord other) { 143 return _node - other._node; 144 } 145 146 /** 147 * Get the string or numeric value of a specific level key for this sort 148 * element. The value is extracted from the DOM if it is not already in 149 * our sort key vector. 150 */ 151 private final Comparable stringValue(int level) { 152 // Get value from our array if possible 153 if (_scanned <= level) { 154 AbstractTranslet translet = _settings.getTranslet(); 155 Locale[] locales = _settings.getLocales(); 156 String[] caseOrder = _settings.getCaseOrders(); 157 158 // Get value from DOM if accessed for the first time 159 final String str = extractValueFromDOM(_dom, _node, level, 160 translet, _last); 161 final Comparable key = 162 StringComparable.getComparator(str, locales[level], 163 _collators[level], 164 caseOrder[level]); 165 _values[_scanned++] = key; 166 return(key); 167 } 168 return((Comparable)_values[level]); 169 } 170 171 private final Double numericValue(int level) { 172 // Get value from our vector if possible 173 if (_scanned <= level) { 174 AbstractTranslet translet = _settings.getTranslet(); 175 176 // Get value from DOM if accessed for the first time 177 final String str = extractValueFromDOM(_dom, _node, level, 178 translet, _last); 179 Double num; 180 try { 181 num = new Double(str); 182 } 183 // Treat number as NaN if it cannot be parsed as a double 184 catch (NumberFormatException e) { 185 num = new Double(Double.NEGATIVE_INFINITY); 186 } 187 _values[_scanned++] = num; 188 return(num); 189 } 190 return((Double)_values[level]); 191 } 192 193 /** 194 * Compare this sort element to another. The first level is checked first, 195 * and we proceed to the next level only if the first level keys are 196 * identical (and so the key values may not even be extracted from the DOM) 197 * 198 * !!!!MUST OPTIMISE - THIS IS REALLY, REALLY SLOW!!!! 199 */ 200 public int compareTo(NodeSortRecord other) { 201 int cmp, level; 202 int[] sortOrder = _settings.getSortOrders(); 203 int levels = _settings.getSortOrders().length; 204 int[] compareTypes = _settings.getTypes(); 205 206 for (level = 0; level < levels; level++) { 207 // Compare the two nodes either as numeric or text values 208 if (compareTypes[level] == COMPARE_NUMERIC) { 209 final Double our = numericValue(level); 210 final Double their = other.numericValue(level); 211 cmp = our.compareTo(their); 212 } 213 else { 214 final Comparable our = stringValue(level); 215 final Comparable their = other.stringValue(level); 216 cmp = our.compareTo(their); 217 } 218 219 // Return inverse compare value if inverse sort order 220 if (cmp != 0) { 221 return sortOrder[level] == COMPARE_DESCENDING ? 0 - cmp : cmp; 222 } 223 } 224 // Compare based on document order if all sort keys are equal 225 return(_node - other._node); 226 } 227 228 /** 229 * Returns the array of Collators used for text comparisons in this object. 230 * May be overridden by inheriting classes 231 */ 232 public Collator[] getCollator() { 233 return _collators; 234 } 235 236 /** 237 * Extract the sort value for a level of this key. 238 */ 239 public abstract String extractValueFromDOM(DOM dom, int current, int level, 240 AbstractTranslet translet, 241 int last); 242 243 }