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