1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * 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  * $Id: NodeSortRecord.java,v 1.5 2005/09/28 13:48:36 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.dom;
  25 
  26 import java.text.CollationKey;
  27 import java.text.Collator;
  28 import java.util.Locale;
  29 
  30 import com.sun.org.apache.xalan.internal.xsltc.CollatorFactory;
  31 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  32 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  33 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  34 import com.sun.org.apache.xml.internal.utils.StringComparable;
  35 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
  36 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  37 
  38 /**
  39  * Base class for sort records containing application specific sort keys
  40  */
  41 public abstract class NodeSortRecord {
  42     public static final int COMPARE_STRING     = 0;
  43     public static final int COMPARE_NUMERIC    = 1;
  44 
  45     public static final int COMPARE_ASCENDING  = 0;
  46     public static final int COMPARE_DESCENDING = 1;
  47 
  48     /**
  49      * A reference to a collator. May be updated by subclass if the stylesheet
  50      * specifies a different language (will be updated iff _locale is updated).
  51      * @deprecated This field continues to exist for binary compatibility.
  52      *             New code should not refer to it.
  53      */
  54     private static final Collator DEFAULT_COLLATOR = Collator.getInstance();
  55 
  56     /**
  57      * A reference to the first Collator
  58      * @deprecated This field continues to exist for binary compatibility.
  59      *             New code should not refer to it.
  60      */
  61     protected Collator _collator = DEFAULT_COLLATOR;
  62     protected Collator[] _collators;
  63 
  64     /**
  65      * A locale field that might be set by an instance of a subclass.
  66      * @deprecated This field continues to exist for binary compatibility.
  67      *             New code should not refer to it.
  68      */
  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 }