1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3 * SPDX-License-Identifier: MIT
4 */
5 package com.qulice.checkstyle;
6
7 import com.google.common.base.Predicate;
8 import com.google.common.collect.FluentIterable;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Iterator;
12
13 /**
14 * Represents a set of LineRange objects. For example, an instance of this class
15 * could represent all the line ranges for methods in a given Java source code
16 * file.
17 *
18 * @since 0.16
19 */
20 public final class LineRanges {
21
22 /**
23 * ArrayList of line ranges.
24 */
25 private final LineRanges.LocalCollection lines =
26 new LineRanges.LocalCollection();
27
28 /**
29 * Adds a line range to the collection.
30 * @param line The line range to add to the collection
31 */
32 public void add(final LineRange line) {
33 this.lines.collection().add(line);
34 }
35
36 /**
37 * Returns an iterator for this collection.
38 * @return Iterator pointing to the internal collections elements.
39 */
40 public Iterator<LineRange> iterator() {
41 return this.lines.collection().iterator();
42 }
43
44 /**
45 * Detects if the given line number is within any of the line ranges.
46 * @param line The given line number to check
47 * @return True if the given line number is within any line range.
48 */
49 public boolean inRange(final int line) {
50 return !this.lines.collection().isEmpty()
51 && FluentIterable.from(this.lines.collection())
52 .anyMatch(new LineRanges.LineWithAny(line));
53 }
54
55 /**
56 * Gets the subset of LineRanges that are within all given ranges. Does
57 * not return null; instead, returns empty range if there are no matches.
58 * @param ranges The ranges to filter on.
59 * @return Returns all LineRange elements that are within range.
60 */
61 public LineRanges within(final LineRanges ranges) {
62 final LineRanges result = new LineRanges();
63 final Iterator<LineRange> iterator = ranges.iterator();
64 while (iterator.hasNext()) {
65 final LineRange next = iterator.next();
66 for (final LineRange line : this.lines.collection()) {
67 if (next.within(line)) {
68 result.add(line);
69 }
70 }
71 }
72 return result;
73 }
74
75 /**
76 * Clears the collection.
77 */
78 public void clear() {
79 this.lines.collection().clear();
80 }
81
82 /**
83 * Predicate to determine if a given line is within range of any of
84 * the line ranges.
85 *
86 * @since 0.1
87 */
88 private static final class LineWithAny implements Predicate<LineRange> {
89
90 /**
91 * The given line.
92 */
93 private final int given;
94
95 /**
96 * Default constructor.
97 * @param line The given line to check against all the line ranges.
98 */
99 private LineWithAny(final int line) {
100 this.given = line;
101 }
102
103 @Override
104 public boolean apply(final LineRange range) {
105 return range != null && range.within(this.given);
106 }
107 }
108
109 /**
110 * Thread-safe collection of line ranges.
111 *
112 * @since 0.1
113 */
114 private static final class LocalCollection
115 extends ThreadLocal<Collection<LineRange>> {
116
117 /**
118 * Internal Collection.
119 */
120 private final transient Collection<LineRange> ranges =
121 new ArrayList<>(20);
122
123 /**
124 * Get the collection specific to the current thread only.
125 * @return The collection for this thread.
126 */
127 public Collection<LineRange> collection() {
128 return this.ranges;
129 }
130 }
131 }