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