• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xmlunit / xmlunit / 667

pending completion
667

push

travis-ci-com

bodewig
add Cyclone DX SBOM generation to build

5824 of 6326 relevant lines covered (92.06%)

3.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.0
/xmlunit-legacy/src/main/java/org/custommonkey/xmlunit/NewDifferenceEngine.java
1
/*
2
******************************************************************
3
Copyright (c) 2001-2010,2013,2015-2016,2018 Jeff Martin, Tim Bacon
4
All rights reserved.
5

6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions
8
are met:
9

10
    * Redistributions of source code must retain the above copyright
11
      notice, this list of conditions and the following disclaimer.
12
    * Redistributions in binary form must reproduce the above
13
      copyright notice, this list of conditions and the following
14
      disclaimer in the documentation and/or other materials provided
15
      with the distribution.
16
    * Neither the name of the XMLUnit nor the names
17
      of its contributors may be used to endorse or promote products
18
      derived from this software without specific prior written
19
      permission.
20

21
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
POSSIBILITY OF SUCH DAMAGE.
33

34
******************************************************************
35
*/
36

37
package org.custommonkey.xmlunit;
38

39
import java.util.Collections;
40
import java.util.HashMap;
41
import java.util.Iterator;
42
import java.util.LinkedList;
43
import java.util.List;
44
import java.util.Map;
45
import javax.xml.transform.Source;
46

47
import org.xmlunit.builder.Input;
48
import org.xmlunit.diff.ByNameAndTextRecSelector;
49
import org.xmlunit.diff.Comparison;
50
import org.xmlunit.diff.ComparisonListener;
51
import org.xmlunit.diff.ComparisonResult;
52
import org.xmlunit.diff.ComparisonType;
53
import org.xmlunit.diff.DOMDifferenceEngine;
54
import org.xmlunit.diff.DefaultNodeMatcher;
55
import org.xmlunit.diff.DifferenceEvaluator;
56
import org.xmlunit.diff.DifferenceEvaluators;
57
import org.xmlunit.diff.ElementSelector;
58
import org.xmlunit.diff.ElementSelectors;
59
import org.xmlunit.diff.NodeFilters;
60
import org.xmlunit.diff.NodeMatcher;
61
import org.xmlunit.input.CommentLessSource;
62
import org.xmlunit.input.WhitespaceNormalizedSource;
63
import org.xmlunit.input.WhitespaceStrippedSource;
64
import org.xmlunit.util.IterableNodeList;
65
import org.xmlunit.util.Linqy;
66
import org.xmlunit.util.Predicate;
67
import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
68

69
import org.w3c.dom.CDATASection;
70
import org.w3c.dom.Comment;
71
import org.w3c.dom.Document;
72
import org.w3c.dom.DocumentType;
73
import org.w3c.dom.Element;
74
import org.w3c.dom.Node;
75

76
/**
77
 * Class that has responsibility for comparing Nodes and notifying a
78
 * DifferenceListener of any differences or dissimilarities that are found.
79
 * Knows how to compare namespaces and nested child nodes, but currently
80
 * only compares nodes of type ELEMENT_NODE, CDATA_SECTION_NODE,
81
 * COMMENT_NODE, DOCUMENT_TYPE_NODE, PROCESSING_INSTRUCTION_NODE and TEXT_NODE.
82
 * Nodes of other types (eg ENTITY_NODE) will be skipped.
83
 * @see DifferenceListener#differenceFound(Difference)
84
 */
85
public class NewDifferenceEngine
86
    implements DifferenceConstants, DifferenceEngineContract {
87

88
    private static final Integer ZERO = Integer.valueOf(0);
4✔
89
    private static final Map<Class<? extends ElementQualifier>, ElementSelector>
90
        KNOWN_SELECTORS;
91
    static {
92
        Map<Class<? extends ElementQualifier>, ElementSelector> m =
4✔
93
            new HashMap<Class<? extends ElementQualifier>, ElementSelector>();
94
        m.put(ElementNameAndTextQualifier.class,
4✔
95
              ElementSelectors.byNameAndText);
96
        m.put(ElementNameQualifier.class, ElementSelectors.byName);
4✔
97
        m.put(RecursiveElementNameAndTextQualifier.class,
4✔
98
              new ByNameAndTextRecSelector());
99
        KNOWN_SELECTORS = Collections.unmodifiableMap(m);
4✔
100
    }
4✔
101

102
    private final ComparisonController controller;
103
    private MatchTracker matchTracker;
104

105
    /**
106
     * Simple constructor that uses no MatchTracker at all.
107
     * @param controller the instance used to determine whether a Difference
108
     * detected by this class should halt further comparison or not
109
     * @see ComparisonController#haltComparison(Difference)
110
     */
111
    public NewDifferenceEngine(ComparisonController controller) {
112
        this(controller, null);
4✔
113
    }
4✔
114

115
    /**
116
     * Simple constructor
117
     * @param controller the instance used to determine whether a Difference
118
     * detected by this class should halt further comparison or not
119
     * @param matchTracker the instance that is notified on each
120
     * successful match.  May be null.
121
     * @see ComparisonController#haltComparison(Difference)
122
     * @see MatchTracker#matchFound(Difference)
123
     */
124
    public NewDifferenceEngine(ComparisonController controller,
125
                               MatchTracker matchTracker) {
4✔
126
        this.controller = controller;
4✔
127
        this.matchTracker = matchTracker;
4✔
128
    }
4✔
129

130
    /**
131
     * @param matchTracker the instance that is notified on each
132
     * successful match.  May be null.
133
     */
134
    public void setMatchTracker(MatchTracker matchTracker) {
135
        this.matchTracker = matchTracker;
4✔
136
    }
4✔
137

138
    /**
139
     * Entry point for Node comparison testing.
140
     * @param control Control XML to compare
141
     * @param test Test XML to compare
142
     * @param listener Notified of any {@link Difference differences} detected
143
     * during node comparison testing
144
     * @param elementQualifier Used to determine which elements qualify for
145
     * comparison e.g. when a node has repeated child elements that may occur
146
     * in any sequence and that sequence is not considered important. 
147
     */
148
    public void compare(Node control, Node test, DifferenceListener listener, 
149
                        ElementQualifier elementQualifier) {
150
        DOMDifferenceEngine engine = new DOMDifferenceEngine();
4✔
151
        engine.setNodeFilter(NodeFilters.AcceptAll);
4✔
152

153
        final IsBetweenDocumentNodeAndRootElement checkPrelude =
4✔
154
            new IsBetweenDocumentNodeAndRootElement();
155
        engine.addComparisonListener(checkPrelude);
4✔
156

157
        if (matchTracker != null) {
4✔
158
            engine
4✔
159
                .addMatchListener(new MatchTracker2ComparisonListener(matchTracker));
4✔
160
        }
161

162
        org.xmlunit.diff.ComparisonController mappedController =
4✔
163
            new ComparisonController2ComparisonController(controller);
164
        engine.setComparisonController(mappedController);
4✔
165
        if (listener != null) {
4✔
166
            final DifferenceEvaluator evaluator =
4✔
167
                DifferenceEvaluators.chain(DifferenceEvaluators.Default,
4✔
168
                                           DifferenceEvaluators.ignorePrologDifferencesExceptDoctype(),
4✔
169
                                           new IgnoreDoctypeNotPresentDifferences(),
170
                                           new DifferenceListener2DifferenceEvaluator(listener));
171
            engine.setDifferenceEvaluator(evaluator);
4✔
172
        }
173

174
        NodeMatcher m = new DefaultNodeMatcher();
4✔
175
        if (elementQualifier != null) {
4✔
176
            Class<?> c = elementQualifier.getClass();
4✔
177
            if (KNOWN_SELECTORS.containsKey(c)) {
4✔
178
                m = new DefaultNodeMatcher(KNOWN_SELECTORS.get(c));
4✔
179
            } else {
180
                m = new DefaultNodeMatcher(new ElementQualifier2ElementSelector(elementQualifier));
4✔
181
            }
182
        }
183
        if (!XMLUnit.getCompareUnmatched()) {
4✔
184
            engine.setNodeMatcher(m);
4✔
185
        } else {
186
            engine.setNodeMatcher(new CompareUnmatchedNodeMatcher(m));
4✔
187
        }
188

189
        Input.Builder ctrlBuilder = Input.fromNode(control);
4✔
190
        Input.Builder tstBuilder = Input.fromNode(test);
4✔
191

192
        Source ctrlSource = ctrlBuilder.build();
4✔
193
        Source tstSource = tstBuilder.build();
4✔
194
        if (XMLUnit.getIgnoreComments()) {
4✔
195
            ctrlSource = new CommentLessSource(ctrlSource);
4✔
196
            tstSource = new CommentLessSource(tstSource);
4✔
197
        }
198
        if (XMLUnit.getNormalizeWhitespace()) {
4✔
199
            ctrlSource = new WhitespaceNormalizedSource(ctrlSource);
4✔
200
            tstSource = new WhitespaceNormalizedSource(tstSource);
4✔
201
        } else if (XMLUnit.getIgnoreWhitespace()) {
4✔
202
            ctrlSource = new WhitespaceStrippedSource(ctrlSource);
4✔
203
            tstSource = new WhitespaceStrippedSource(tstSource);
4✔
204
        }
205

206
        engine.compare(ctrlSource, tstSource);
4✔
207
    }
4✔
208

209
    private static Iterable<Difference> toDifference(org.xmlunit.diff.Difference d) {
210
        return toDifference(d.getComparison());
4✔
211
    }
212

213
    public static Iterable<Difference> toDifference(Comparison comp) {
214
        List<Difference> diffs = new LinkedList<Difference>();
4✔
215
        Difference proto = null;
4✔
216
        switch (comp.getType()) {
4✔
217
        case ATTR_VALUE_EXPLICITLY_SPECIFIED:
218
            proto = ATTR_VALUE_EXPLICITLY_SPECIFIED;
×
219
            break;
×
220
        case HAS_DOCTYPE_DECLARATION:
221
            proto = HAS_DOCTYPE_DECLARATION;
4✔
222
            break;
4✔
223
        case DOCTYPE_NAME:
224
            proto = DOCTYPE_NAME;
×
225
            break;
×
226
        case DOCTYPE_PUBLIC_ID:
227
            proto = DOCTYPE_PUBLIC_ID;
4✔
228
            break;
4✔
229
        case DOCTYPE_SYSTEM_ID:
230
            proto = DOCTYPE_SYSTEM_ID;
4✔
231
            break;
4✔
232
        case SCHEMA_LOCATION:
233
            proto = SCHEMA_LOCATION;
4✔
234
            break;
4✔
235
        case NO_NAMESPACE_SCHEMA_LOCATION:
236
            proto = NO_NAMESPACE_SCHEMA_LOCATION;
4✔
237
            break;
4✔
238
        case NODE_TYPE:
239
            proto = NODE_TYPE;
4✔
240
            break;
4✔
241
        case NAMESPACE_PREFIX:
242
            proto = NAMESPACE_PREFIX;
4✔
243
            break;
4✔
244
        case NAMESPACE_URI:
245
            proto = NAMESPACE_URI;
4✔
246
            break;
4✔
247
        case TEXT_VALUE:
248
            if (comp.getControlDetails().getTarget() instanceof CDATASection) {
4✔
249
                proto = CDATA_VALUE;
4✔
250
            } else if (comp.getControlDetails().getTarget()
4✔
251
                       instanceof Comment) {
252
                proto = COMMENT_VALUE;
4✔
253
            } else {
254
                proto = TEXT_VALUE;
4✔
255
            }
256
            break;
4✔
257
        case PROCESSING_INSTRUCTION_TARGET:
258
            proto = PROCESSING_INSTRUCTION_TARGET;
4✔
259
            break;
4✔
260
        case PROCESSING_INSTRUCTION_DATA:
261
            proto = PROCESSING_INSTRUCTION_DATA;
×
262
            break;
×
263
        case ELEMENT_TAG_NAME:
264
            proto = ELEMENT_TAG_NAME;
4✔
265
            break;
4✔
266
        case ELEMENT_NUM_ATTRIBUTES:
267
            proto = ELEMENT_NUM_ATTRIBUTES;
4✔
268
            break;
4✔
269
        case ATTR_VALUE:
270
            proto = ATTR_VALUE;
4✔
271
            break;
4✔
272
        case CHILD_NODELIST_LENGTH:
273
            Comparison.Detail cd = comp.getControlDetails();
4✔
274
            Comparison.Detail td = comp.getTestDetails();
4✔
275
            if (ZERO.equals(cd.getValue())
4✔
276
                || ZERO.equals(td.getValue())) {
4✔
277
                diffs.add(new Difference(HAS_CHILD_NODES,
4✔
278
                                         new NodeDetail(String
279
                                                        .valueOf(!ZERO
4✔
280
                                                                 .equals(cd
4✔
281
                                                                         .getValue())),
4✔
282
                                                        cd.getTarget(),
4✔
283
                                                        cd.getXPath()),
4✔
284
                                         new NodeDetail(String
285
                                                        .valueOf(!ZERO
4✔
286
                                                                 .equals(td
4✔
287
                                                                         .getValue())),
4✔
288
                                                        td.getTarget(),
4✔
289
                                                        td.getXPath())));
4✔
290
            }
291
            proto = CHILD_NODELIST_LENGTH;
4✔
292
            break;
4✔
293
        case CHILD_NODELIST_SEQUENCE:
294
            proto = CHILD_NODELIST_SEQUENCE;
4✔
295
            break;
4✔
296
        case CHILD_LOOKUP:
297
            proto = CHILD_NODE_NOT_FOUND;
4✔
298
            break;
4✔
299
        case ATTR_NAME_LOOKUP:
300
            proto = ATTR_NAME_NOT_FOUND;
4✔
301
            break;
4✔
302
        default:
303
            /* comparison doesn't match one of legacy's built-in differences */
304
            break;
305
        }
306
        if (proto != null) {
4✔
307
            diffs.add(new Difference(proto, toNodeDetail(comp.getControlDetails()),
4✔
308
                                     toNodeDetail(comp.getTestDetails())));
4✔
309
        }
310
        return diffs;
4✔
311
    }
312

313
    public static NodeDetail toNodeDetail(Comparison.Detail detail) {
314
        String value = String.valueOf(detail.getValue());
4✔
315
        if (detail.getValue() instanceof Node) {
4✔
316
            value = ((Node) detail.getValue()).getNodeName();
×
317
        }
318
        return new NodeDetail(value, detail.getTarget(),
4✔
319
                              detail.getXPath());
4✔
320
    }
321

322
    public static class MatchTracker2ComparisonListener
323
        implements ComparisonListener {
324
        private final MatchTracker mt;
325

326
        public MatchTracker2ComparisonListener(MatchTracker m) {
4✔
327
            mt = m;
4✔
328
        }
4✔
329

330
        public void comparisonPerformed(Comparison comparison,
331
                                        ComparisonResult outcome) {
332
            for (Difference diff : toDifference(comparison)) {
4✔
333
                mt.matchFound(diff);
4✔
334
            }
4✔
335
        }
4✔
336
    }
337

338
    public static class ComparisonController2ComparisonController
339
        implements org.xmlunit.diff.ComparisonController {
340
        private final ComparisonController cc;
341
        public ComparisonController2ComparisonController(ComparisonController c) {
4✔
342
            cc = c;
4✔
343
        }
4✔
344

345
        public boolean stopDiffing(org.xmlunit.diff.Difference difference) {
346
            for (Difference diff : toDifference(difference)) {
4✔
347
                if (cc.haltComparison(diff)) {
4✔
348
                    return true;
4✔
349
                }
350
            }
4✔
351
            return false;
4✔
352
        }
353
    }
354

355
    public static class ElementQualifier2ElementSelector
356
        implements ElementSelector {
357
        private final ElementQualifier eq;
358

359
        public ElementQualifier2ElementSelector(ElementQualifier eq) {
4✔
360
            this.eq = eq;
4✔
361
        }
4✔
362

363
        public boolean canBeCompared(Element controlElement,
364
                                     Element testElement) {
365
            return eq.qualifyForComparison(controlElement, testElement);
4✔
366
        }
367

368
    }
369

370
    private static final class IgnoreDoctypeNotPresentDifferences implements DifferenceEvaluator {
371
        public ComparisonResult evaluate(Comparison comparison,
372
                                         ComparisonResult outcome) {
373
            if (outcome == ComparisonResult.EQUAL) {
4✔
374
                return outcome;
4✔
375
            }
376
            if (comparison.getType() == ComparisonType.CHILD_LOOKUP
4✔
377
                && (isDoctype(comparison.getControlDetails())
4✔
378
                    || isDoctype(comparison.getTestDetails()))) {
4✔
379
                return ComparisonResult.EQUAL;
4✔
380
            }
381
            if (comparison.getType() == ComparisonType.CHILD_NODELIST_LENGTH
4✔
382
                && isDocumentWithDocTypeDifference(comparison)
4✔
383
                && Math.abs((Integer) comparison.getControlDetails().getValue()
×
384
                            - (Integer) comparison.getTestDetails().getValue())
×
385
                   == 1) {
386
                return ComparisonResult.EQUAL;
×
387
            }
388
            if (comparison.getType() == ComparisonType.CHILD_NODELIST_SEQUENCE
4✔
389
                && isDocumentWithDocTypeDifference(comparison)) {
4✔
390
                return ComparisonResult.EQUAL;
×
391
            }
392
            return outcome;
4✔
393
        }
394

395
        private boolean isDoctype(Comparison.Detail detail) {
396
            return detail != null && detail.getTarget() instanceof DocumentType;
4✔
397
        }
398

399
        private boolean isDocumentWithDocTypeDifference(Comparison comparison) {
400
            if (!(comparison.getControlDetails().getTarget() instanceof Document)) {
4✔
401
                return false;
4✔
402
            }
403
            return hasDoctypeChild(comparison.getControlDetails().getTarget())
×
404
                || hasDoctypeChild(comparison.getTestDetails().getTarget());
×
405
        }
406

407
        private boolean hasDoctypeChild(Node n) {
408
            return Linqy.any(new IterableNodeList(n.getChildNodes()),
×
409
                             new Predicate<Node>() {
×
410
                                 public boolean test(Node n) {
411
                                     return n instanceof DocumentType;
×
412
                                 }
413
                             });
414
        }
415
    }
416

417
    public static class DifferenceListener2DifferenceEvaluator
418
        implements DifferenceEvaluator {
419
        private final DifferenceListener dl;
420

421
        public DifferenceListener2DifferenceEvaluator(DifferenceListener dl) {
4✔
422
            this.dl = dl;
4✔
423
        }
4✔
424

425
        public ComparisonResult evaluate(Comparison comparison,
426
                                         ComparisonResult outcome) {
427
            if (outcome == ComparisonResult.EQUAL) {
4✔
428
                return outcome;
4✔
429
            }
430
            ComparisonResult max = outcome;
4✔
431
            for (Difference diff : toDifference(comparison)) {
4✔
432
                ComparisonResult curr = null;
4✔
433
                switch (dl.differenceFound(diff)) {
4✔
434
                case DifferenceListener
435
                    .RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL:
436
                    curr = ComparisonResult.EQUAL;
4✔
437
                    break;
4✔
438
                case DifferenceListener
439
                    .RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR:
440
                    curr = ComparisonResult.SIMILAR;
4✔
441
                    break;
4✔
442
                case DifferenceListener
443
                    .RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT:
444
                    curr = ComparisonResult.DIFFERENT;
4✔
445
                    break;
4✔
446
                default:
447
                    // unknown result, ignore it
448
                    break;
449
                }
450
                if (curr != null && curr.compareTo(max) > 0) {
4✔
451
                    max = curr;
4✔
452
                }
453
            }
4✔
454
            return max;
4✔
455
        }
456
    }
457

458
    /**
459
     * Tests whether the DifferenceEngine is currently processing
460
     * comparisons of "things" between the document node and the
461
     * document's root element (comments or PIs, mostly) since these
462
     * must be ignored for backwards compatibility reasons.
463
     *
464
     * <p>Relies on the following assumptions:
465
     * <ul>
466

467
     *   <li>the last comparison DOMDifferenceEngine performs on the
468
     *     document node is an XML_ENCODING comparison.</li>
469
     *   <li>the first comparison DOMDifferenceEngine performs on matching
470
     *     root elements is a NODE_TYPE comparison.  The control Node
471
     *     is an Element Node.</li>
472
     *   <li>the first comparison DOMDifferenceEngine performs if the
473
     *     root elements don't match is a CHILD_LOOKUP comparison.
474
     *     The control Node is an Element Node.</li>
475
     * </ul>
476
     * </p>
477
     */
478
    private static class IsBetweenDocumentNodeAndRootElement
4✔
479
        implements ComparisonListener {
480

481
        private boolean haveSeenXmlEncoding = false;
4✔
482
        private boolean haveSeenElementNodeComparison = false;
4✔
483

484
        public void comparisonPerformed(Comparison comparison,
485
                                        ComparisonResult outcome) {
486
            if (comparison.getType() == ComparisonType.XML_ENCODING) {
4✔
487
                haveSeenXmlEncoding = true;
4✔
488
            } else if (comparison.getControlDetails().getTarget()
4✔
489
                          instanceof Element
490
                       &&
491
                       (comparison.getType() == ComparisonType.NODE_TYPE
4✔
492
                        || comparison.getType() == ComparisonType.CHILD_LOOKUP)
4✔
493
                       ) {
494
                haveSeenElementNodeComparison = true;
4✔
495
            }
496
        }
4✔
497

498
        private boolean shouldSkip() {
499
            return haveSeenXmlEncoding && !haveSeenElementNodeComparison;
×
500
        }
501
    }
502

503
    private static class CompareUnmatchedNodeMatcher
504
        implements NodeMatcher {
505
        private final NodeMatcher nestedMatcher;
506
        private CompareUnmatchedNodeMatcher(NodeMatcher nested) {
4✔
507
            nestedMatcher = nested;
4✔
508
        }
4✔
509

510
        public Iterable<Map.Entry<Node, Node>>
511
            match(Iterable<Node> controlNodes,
512
                  Iterable<Node> testNodes) {
513
            final Map<Node, Node> map = new HashMap<Node, Node>();
4✔
514
            for (Map.Entry<Node, Node> e 
515
                     : nestedMatcher.match(controlNodes, testNodes)) {
4✔
516
                map.put(e.getKey(), e.getValue());
4✔
517
            }
4✔
518

519
            final LinkedList<Map.Entry<Node, Node>> result =
4✔
520
                new LinkedList<Map.Entry<Node, Node>>();
521

522
            for (Node n : controlNodes) {
4✔
523
                if (map.containsKey(n)) {
4✔
524
                    result.add(new Entry(n, map.get(n)));
4✔
525
                } else {
526
                    Iterable<Node> unmatchedTestElements =
4✔
527
                        Linqy.filter(testNodes, new Predicate<Node>() {
4✔
528
                                @Override
529
                                public boolean test(Node t) {
530
                                    return !map.containsValue(t);
4✔
531
                                }
532
                            });
533
                    Iterator<Node> it = unmatchedTestElements.iterator();
4✔
534
                    if (it.hasNext()) {
4✔
535
                        Node t = it.next();
4✔
536
                        map.put(n, t);
4✔
537
                        result.add(new Entry(n, t));
4✔
538
                    }
539
                }
540
            }
4✔
541
            return result;
4✔
542
        }
543

544
        private static class Entry implements Map.Entry<Node, Node> {
545
            private final Node key;
546
            private final Node value;
547
            private Entry(Node k, Node v) {
4✔
548
                key = k;
4✔
549
                value = v;
4✔
550
            }
4✔
551
            public Node getKey() { return key; }
4✔
552
            public Node getValue() { return value; }
4✔
553
            public Node setValue(Node v) {
554
                throw new UnsupportedOperationException();
×
555
            }
556
        }
557
    }
558

559
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc