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