|
16 | 16 | import java.util.List; |
17 | 17 | import java.util.Map; |
18 | 18 | import java.util.Set; |
| 19 | +import java.util.function.Predicate; |
19 | 20 | import java.util.regex.Matcher; |
20 | 21 | import java.util.regex.Pattern; |
21 | 22 |
|
@@ -100,6 +101,43 @@ public class LicenseTextHelper { |
100 | 101 | NORMALIZE_TOKENS.put("\"", "'"); |
101 | 102 | NORMALIZE_TOKENS.put("merchantability", "merchantability"); |
102 | 103 | } |
| 104 | + |
| 105 | + /** |
| 106 | + * Class to encapsulate Token iterator |
| 107 | + */ |
| 108 | + private static class TokenIterator { |
| 109 | + private final String[] tokens; |
| 110 | + private int position = 0; |
| 111 | + private String current; |
| 112 | + |
| 113 | + TokenIterator(String[] tokens) { |
| 114 | + this.tokens = tokens; |
| 115 | + this.current = getTokenAt(tokens, position++); |
| 116 | + } |
| 117 | + |
| 118 | + String current() { |
| 119 | + return current; |
| 120 | + } |
| 121 | + |
| 122 | + boolean hasNext() { |
| 123 | + return current != null; |
| 124 | + } |
| 125 | + |
| 126 | + void advance() { |
| 127 | + current = getTokenAt(tokens, position++); |
| 128 | + } |
| 129 | + |
| 130 | + void skipWhile(Predicate<String> condition) { |
| 131 | + while (current != null && condition.test(current)) { |
| 132 | + advance(); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + boolean hasOnlySkippableTokensRemaining() { |
| 137 | + skipWhile(LicenseTextHelper::canSkip); |
| 138 | + return current == null; |
| 139 | + } |
| 140 | + } |
103 | 141 |
|
104 | 142 | private LicenseTextHelper() { |
105 | 143 | // static class |
@@ -143,45 +181,41 @@ public static boolean isLicenseTextEquivalent(String licenseTextA, String licens |
143 | 181 | * @return true if the license text is equivalent |
144 | 182 | */ |
145 | 183 | public static boolean isLicenseTextEquivalent(String[] licenseATokens, String[] licenseBTokens) { |
146 | | - int bTokenCounter = 0; |
147 | | - int aTokenCounter = 0; |
148 | | - String nextAToken = getTokenAt(licenseATokens, aTokenCounter++); |
149 | | - String nextBToken = getTokenAt(licenseBTokens, bTokenCounter++); |
150 | | - while (nextAToken != null) { |
151 | | - if (nextBToken == null) { |
152 | | - // end of b stream |
153 | | - while (canSkip(nextAToken)) { |
154 | | - nextAToken = getTokenAt(licenseATokens, aTokenCounter++); |
155 | | - } |
156 | | - if (nextAToken != null) { |
157 | | - return false; // there is more stuff in the license text B, so not equal |
158 | | - } |
159 | | - } else if (tokensEquivalent(nextAToken, nextBToken)) { |
160 | | - // just move onto the next set of tokens |
161 | | - nextAToken = getTokenAt(licenseATokens, aTokenCounter++); |
162 | | - nextBToken = getTokenAt(licenseBTokens, bTokenCounter++); |
163 | | - } else { |
164 | | - // see if we can skip through some B tokens to find a match |
165 | | - while (canSkip(nextBToken)) { |
166 | | - nextBToken = getTokenAt(licenseBTokens, bTokenCounter++); |
167 | | - } |
168 | | - // just to be sure, skip forward on the A license |
169 | | - while (canSkip(nextAToken)) { |
170 | | - nextAToken = getTokenAt(licenseATokens, aTokenCounter++); |
171 | | - } |
172 | | - if (!tokensEquivalent(nextAToken, nextBToken)) { |
173 | | - return false; |
174 | | - } else { |
175 | | - nextAToken = getTokenAt(licenseATokens, aTokenCounter++); |
176 | | - nextBToken = getTokenAt(licenseBTokens, bTokenCounter++); |
177 | | - } |
| 184 | + TokenIterator iterA = new TokenIterator(licenseATokens); |
| 185 | + TokenIterator iterB = new TokenIterator(licenseBTokens); |
| 186 | + |
| 187 | + while (iterA.hasNext()) { |
| 188 | + if (!iterB.hasNext()) { |
| 189 | + return iterA.hasOnlySkippableTokensRemaining(); |
| 190 | + } |
| 191 | + |
| 192 | + if (tokensEquivalent(iterA.current(), iterB.current())) { |
| 193 | + iterA.advance(); |
| 194 | + iterB.advance(); |
| 195 | + } else if (!trySkipToMatch(iterA, iterB)) { |
| 196 | + return false; |
178 | 197 | } |
179 | 198 | } |
180 | | - // need to make sure B is at the end |
181 | | - while (canSkip(nextBToken)) { |
182 | | - nextBToken = getTokenAt(licenseBTokens, bTokenCounter++); |
| 199 | + return iterB.hasOnlySkippableTokensRemaining(); |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * Skips any tokens that can be skipped and attempts to match the remaining tokens |
| 204 | +// * @param iterA Token iterator for comparison |
| 205 | + * @param iterB Token iterator for comparison |
| 206 | + * @return true if the tokens match |
| 207 | + */ |
| 208 | + private static boolean trySkipToMatch(TokenIterator iterA, TokenIterator iterB) { |
| 209 | + iterB.skipWhile(LicenseTextHelper::canSkip); |
| 210 | + iterA.skipWhile(LicenseTextHelper::canSkip); |
| 211 | + |
| 212 | + if (!tokensEquivalent(iterA.current(), iterB.current())) { |
| 213 | + return false; |
183 | 214 | } |
184 | | - return (nextBToken == null); |
| 215 | + |
| 216 | + iterA.advance(); |
| 217 | + iterB.advance(); |
| 218 | + return true; |
185 | 219 | } |
186 | 220 |
|
187 | 221 | /** |
|
0 commit comments