1 /*
2  * Copyright (c) 2017, 2019, 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.xsltc.DOM;
24 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
25 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
26 import com.sun.org.apache.xml.internal.utils.StringComparable;
27 import java.text.Collator;
28 import java.util.Locale;
29 
30 /**
31  * Base class for sort records containing application specific sort keys
32  *
33  * @LastModified: May 2019
34  */
35 public abstract class NodeSortRecord {
36     public static final int COMPARE_STRING     = 0;
37     public static final int COMPARE_NUMERIC    = 1;
38 
39     public static final int COMPARE_ASCENDING  = 0;
40     public static final int COMPARE_DESCENDING = 1;
41 
42     /**
43      * A reference to a collator. May be updated by subclass if the stylesheet
44      * specifies a different language (will be updated iff _locale is updated).
45      * @deprecated This field continues to exist for binary compatibility.
46      *             New code should not refer to it.
47      */
48     @Deprecated
49     private static final Collator DEFAULT_COLLATOR = Collator.getInstance();
50 
51     /**
52      * A reference to the first Collator
53      * @deprecated This field continues to exist for binary compatibility.
54      *             New code should not refer to it.
55      */
56     @Deprecated
57     protected Collator _collator = DEFAULT_COLLATOR;
58     protected Collator[] _collators;
59 
60     /**
61      * A locale field that might be set by an instance of a subclass.
62      * @deprecated This field continues to exist for binary compatibility.
63      *             New code should not refer to it.
64      */
65     @Deprecated
66     protected Locale _locale;
67 
68     protected SortSettings _settings;
69 
70     private DOM    _dom = null;
71     private int    _node;           // The position in the current iterator
72     private int    _last = 0;       // Number of nodes in the current iterator
73     private int    _scanned = 0;    // Number of key levels extracted from DOM
74 
75     private Object[] _values; // Contains Comparable  objects
76 
77     /**
78      * This constructor is run by a call to ClassLoader in the
79      * makeNodeSortRecord method in the NodeSortRecordFactory class. Since we
80      * cannot pass any parameters to the constructor in that case we just set
81      * the default values here and wait for new values through initialize().
82      */
NodeSortRecord(int node)83     public NodeSortRecord(int node) {
84         _node = node;
85     }
86 
NodeSortRecord()87     public NodeSortRecord() {
88         this(0);
89     }
90 
91     /**
92      * This method allows the caller to set the values that could not be passed
93      * to the default constructor.
94      */
initialize(int node, int last, DOM dom, SortSettings settings)95     public final void initialize(int node, int last, DOM dom,
96          SortSettings settings)
97         throws TransletException
98     {
99         _dom = dom;
100         _node = node;
101         _last = last;
102         _settings = settings;
103 
104         int levels = settings.getSortOrders().length;
105         _values = new Object[levels];
106 
107         _collators = settings.getCollators();
108         _collator = _collators[0];
109     }
110 
111     /**
112      * Returns the node for this sort object
113      */
getNode()114     public final int getNode() {
115         return _node;
116     }
117 
118     /**
119      *
120      */
compareDocOrder(NodeSortRecord other)121     public final int compareDocOrder(NodeSortRecord other) {
122         return _node - other._node;
123     }
124 
125     /**
126      * Get the string or numeric value of a specific level key for this sort
127      * element. The value is extracted from the DOM if it is not already in
128      * our sort key vector.
129      */
130     @SuppressWarnings({"rawtypes", "unchecked"})
stringValue(int level)131     private final Comparable stringValue(int level) {
132         // Get value from our array if possible
133         if (_scanned <= level) {
134             AbstractTranslet translet = _settings.getTranslet();
135             Locale[] locales = _settings.getLocales();
136             String[] caseOrder = _settings.getCaseOrders();
137 
138             // Get value from DOM if accessed for the first time
139             final String str = extractValueFromDOM(_dom, _node, level,
140                                                    translet, _last);
141             final Comparable key = StringComparable.getComparator(
142                     str, locales[level], _collators[level], caseOrder[level]);
143             _values[_scanned++] = key;
144             return(key);
145         }
146         return((Comparable)_values[level]);
147   }
148 
numericValue(int level)149     private final Double numericValue(int level) {
150         // Get value from our vector if possible
151         if (_scanned <= level) {
152             AbstractTranslet translet = _settings.getTranslet();
153 
154             // Get value from DOM if accessed for the first time
155             final String str = extractValueFromDOM(_dom, _node, level,
156                                                    translet, _last);
157             Double num;
158             try {
159                 num = Double.parseDouble(str);
160             }
161             // Treat number as NaN if it cannot be parsed as a double
162             catch (NumberFormatException e) {
163                 num = Double.NEGATIVE_INFINITY;
164             }
165             _values[_scanned++] = num;
166             return(num);
167         }
168         return((Double)_values[level]);
169     }
170 
171     /**
172      * Compare this sort element to another. The first level is checked first,
173      * and we proceed to the next level only if the first level keys are
174      * identical (and so the key values may not even be extracted from the DOM)
175      *
176      * !!!!MUST OPTIMISE - THIS IS REALLY, REALLY SLOW!!!!
177      */
178     @SuppressWarnings({"rawtypes", "unchecked"})
compareTo(NodeSortRecord other)179     public int compareTo(NodeSortRecord other) {
180         int cmp, level;
181         int[] sortOrder = _settings.getSortOrders();
182         int levels = _settings.getSortOrders().length;
183         int[] compareTypes = _settings.getTypes();
184 
185         for (level = 0; level < levels; level++) {
186             // Compare the two nodes either as numeric or text values
187             if (compareTypes[level] == COMPARE_NUMERIC) {
188                 final Double our = numericValue(level);
189                 final Double their = other.numericValue(level);
190                 cmp = our.compareTo(their);
191             }
192             else {
193                 final Comparable our = stringValue(level);
194                 final Comparable their = other.stringValue(level);
195                 cmp = our.compareTo(their);
196             }
197 
198             // Return inverse compare value if inverse sort order
199             if (cmp != 0) {
200                 return sortOrder[level] == COMPARE_DESCENDING ? 0 - cmp : cmp;
201             }
202         }
203         // Compare based on document order if all sort keys are equal
204         return(_node - other._node);
205     }
206 
207     /**
208      * Returns the array of Collators used for text comparisons in this object.
209      * May be overridden by inheriting classes
210      */
getCollator()211     public Collator[] getCollator() {
212         return _collators;
213     }
214 
215     /**
216      * Extract the sort value for a level of this key.
217      */
extractValueFromDOM(DOM dom, int current, int level, AbstractTranslet translet, int last)218     public abstract String extractValueFromDOM(DOM dom, int current, int level,
219                                                AbstractTranslet translet,
220                                                int last);
221 
222 }
223