Skip to content

Commit 83b7038

Browse files
committed
add DotName.startsWith()
1 parent 4b0b224 commit 83b7038

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.jboss.jandex;
2+
3+
import org.openjdk.jmh.annotations.Benchmark;
4+
import org.openjdk.jmh.annotations.BenchmarkMode;
5+
import org.openjdk.jmh.annotations.Fork;
6+
import org.openjdk.jmh.annotations.Measurement;
7+
import org.openjdk.jmh.annotations.Mode;
8+
import org.openjdk.jmh.annotations.Scope;
9+
import org.openjdk.jmh.annotations.Setup;
10+
import org.openjdk.jmh.annotations.State;
11+
import org.openjdk.jmh.annotations.Warmup;
12+
13+
@BenchmarkMode(Mode.Throughput)
14+
@Fork(5)
15+
@Warmup(iterations = 5, time = 1, batchSize = 8192)
16+
@Measurement(iterations = 5, time = 1, batchSize = 8192)
17+
@State(Scope.Benchmark)
18+
public class DotNameStartsWithBenchmark {
19+
private DotName simpleCom;
20+
private DotName simpleComExample;
21+
private DotName simpleComExampleFooBar;
22+
private DotName componentizedCom;
23+
private DotName componentizedComExample;
24+
private DotName componentizedComExampleFooBar;
25+
26+
@Setup
27+
public void setup() {
28+
simpleCom = DotName.createSimple("com");
29+
simpleComExample = DotName.createSimple("com.example");
30+
simpleComExampleFooBar = DotName.createSimple("com.example.FooBar");
31+
componentizedCom = DotName.createComponentized(null, "com");
32+
componentizedComExample = DotName.createComponentized(componentizedCom, "example");
33+
componentizedComExampleFooBar = DotName.createComponentized(componentizedComExample, "FooBar");
34+
}
35+
36+
@Benchmark
37+
public boolean simpleSimple() {
38+
return simpleComExample.startsWith(simpleCom)
39+
&& simpleComExampleFooBar.startsWith(simpleComExample);
40+
}
41+
42+
@Benchmark
43+
public boolean simpleComponentized() {
44+
return simpleComExample.startsWith(componentizedCom)
45+
&& simpleComExampleFooBar.startsWith(componentizedComExample);
46+
}
47+
48+
@Benchmark
49+
public boolean componentizedSimple() {
50+
return componentizedComExample.startsWith(simpleCom)
51+
&& componentizedComExampleFooBar.startsWith(simpleComExample);
52+
}
53+
54+
@Benchmark
55+
public boolean componentizedComponentized() {
56+
return componentizedComExample.startsWith(componentizedCom)
57+
&& componentizedComExampleFooBar.startsWith(componentizedComExample);
58+
}
59+
}

core/src/main/java/org/jboss/jandex/DotName.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,76 @@ public boolean isInner() {
314314
return innerClass;
315315
}
316316

317+
/**
318+
* Returns whether the dotted name structure represented by this {@link DotName}
319+
* starts with the dotted name structure represented by given {@code prefix}. For
320+
* example, {@code com.example.FooBar} starts with {@code com}, {@code com.example}
321+
* and {@code com.example.FooBar}, but it <em>does not</em> start with {@code com.exam}
322+
* or {@code com.example.Foo}.
323+
* <p>
324+
* Note that this method <em>only</em> determines package prefixing and does
325+
* <em>not</em> support inner class prefixing. This is because it is impossible
326+
* to know whether the {@code $} char is part of a class name or an inner class
327+
* delimiter. Notably, when either this {@code DotName} or the {@code prefix} are
328+
* {@linkplain #isComponentized() componentized} and have at least one component
329+
* with the {@linkplain #isInner() inner} flag set, the result is not defined.
330+
*
331+
* @param prefix the prefix to search for, must not be {@code null}
332+
* @return whether this {@link DotName} starts with the given prefix
333+
* @since 3.5.2
334+
*/
335+
public boolean startsWith(DotName prefix) {
336+
int thisLength = this.stringLength();
337+
int prefixLength = prefix.stringLength();
338+
339+
if (thisLength < prefixLength) {
340+
return false;
341+
}
342+
343+
if (componentized) {
344+
if (prefix.componentized) {
345+
DotName current = this;
346+
while (current != null) {
347+
if (current.equals(prefix)) {
348+
return true;
349+
}
350+
current = current.prefix;
351+
}
352+
return false;
353+
} else {
354+
return commonPrefixLength(prefix.local, this) == prefixLength + 1;
355+
}
356+
} else {
357+
if (prefix.componentized) {
358+
return commonPrefixLength(this.local, prefix) == prefixLength + 1;
359+
} else {
360+
if (thisLength == prefixLength) {
361+
return this.local.equals(prefix.local);
362+
} else {
363+
return this.local.startsWith(prefix.local) && this.local.charAt(prefixLength) == '.';
364+
}
365+
}
366+
}
367+
}
368+
369+
private int commonPrefixLength(String simple, DotName componentized) {
370+
if (componentized == null) {
371+
return 0;
372+
}
373+
374+
int index = commonPrefixLength(simple, componentized.prefix);
375+
if (index >= simple.length()) {
376+
return index;
377+
}
378+
if (index >= 0 && simple.startsWith(componentized.local, index)) {
379+
index += componentized.local.length();
380+
if (index >= simple.length() || simple.charAt(index) == '.') {
381+
return index + 1;
382+
}
383+
}
384+
return -1;
385+
}
386+
317387
boolean startsWithJava() {
318388
if (componentized) {
319389
DotName name = this;

core/src/test/java/org/jboss/jandex/test/DotNameTestCase.java

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,103 @@ public void componentizedEquals() {
363363
}
364364
}
365365

366+
@Test
367+
public void startsWith() {
368+
DotName componentizedCom = DotName.createComponentized(null, "com");
369+
assertTrue(componentizedCom.startsWith(componentizedCom));
370+
371+
DotName componentizedComExample = DotName.createComponentized(componentizedCom, "example");
372+
assertTrue(componentizedComExample.startsWith(componentizedCom));
373+
assertTrue(componentizedComExample.startsWith(componentizedComExample));
374+
375+
DotName componentizedComExampleFooBar = DotName.createComponentized(componentizedComExample, "FooBar");
376+
assertTrue(componentizedComExampleFooBar.startsWith(componentizedCom));
377+
assertTrue(componentizedComExampleFooBar.startsWith(componentizedComExample));
378+
assertTrue(componentizedComExampleFooBar.startsWith(componentizedComExampleFooBar));
379+
380+
DotName componentizedComEx = DotName.createComponentized(componentizedCom, "ex");
381+
assertFalse(componentizedComExampleFooBar.startsWith(componentizedComEx));
382+
383+
DotName componentizedComExampleFoo = DotName.createComponentized(componentizedComExample, "Foo");
384+
assertFalse(componentizedComExampleFooBar.startsWith(componentizedComExampleFoo));
385+
386+
DotName simpleCom = DotName.createSimple("com");
387+
assertTrue(simpleCom.startsWith(simpleCom));
388+
389+
assertTrue(simpleCom.startsWith(componentizedCom));
390+
assertTrue(componentizedCom.startsWith(simpleCom));
391+
392+
DotName simpleComExample = DotName.createSimple("com.example");
393+
assertTrue(simpleComExample.startsWith(simpleCom));
394+
assertTrue(simpleComExample.startsWith(simpleComExample));
395+
396+
assertTrue(simpleComExample.startsWith(componentizedCom));
397+
assertTrue(simpleComExample.startsWith(componentizedComExample));
398+
assertTrue(componentizedComExample.startsWith(simpleCom));
399+
assertTrue(componentizedComExample.startsWith(simpleComExample));
400+
401+
DotName simpleComExampleFooBar = DotName.createSimple("com.example.FooBar");
402+
assertTrue(simpleComExampleFooBar.startsWith(simpleCom));
403+
assertTrue(simpleComExampleFooBar.startsWith(simpleComExample));
404+
assertTrue(simpleComExampleFooBar.startsWith(simpleComExampleFooBar));
405+
406+
assertTrue(simpleComExampleFooBar.startsWith(componentizedCom));
407+
assertTrue(simpleComExampleFooBar.startsWith(componentizedComExample));
408+
assertTrue(simpleComExampleFooBar.startsWith(componentizedComExampleFooBar));
409+
assertTrue(componentizedComExampleFooBar.startsWith(simpleCom));
410+
assertTrue(componentizedComExampleFooBar.startsWith(simpleComExample));
411+
assertTrue(componentizedComExampleFooBar.startsWith(simpleComExampleFooBar));
412+
413+
DotName simpleComEx = DotName.createSimple("com.ex");
414+
assertFalse(simpleComExampleFooBar.startsWith(simpleComEx));
415+
assertFalse(simpleComExampleFooBar.startsWith(componentizedComEx));
416+
assertFalse(componentizedComExampleFooBar.startsWith(simpleComEx));
417+
418+
DotName simpleComExampleFoo = DotName.createSimple("com.example.Foo");
419+
assertFalse(simpleComExampleFooBar.startsWith(simpleComExampleFoo));
420+
assertFalse(simpleComExampleFooBar.startsWith(componentizedComExampleFoo));
421+
assertFalse(componentizedComExampleFooBar.startsWith(simpleComExampleFoo));
422+
}
423+
424+
@Test
425+
public void startsWith_random() {
426+
for (int i = 0; i < 2_000_000; i++) {
427+
DotName name1 = createRandomDotName();
428+
DotName name2 = createRandomDotName();
429+
430+
// `createRandomComponentised()` only flags the last component as inner
431+
if (name1.isInner() ^ name2.isInner()) {
432+
continue;
433+
}
434+
435+
assertEquals(naiveStartsWith(name1, name2), name1.startsWith(name2));
436+
437+
if (name1.isComponentized()) {
438+
DotName name1Prefix = name1.prefix();
439+
if (name1Prefix != null) {
440+
assertTrue(name1.startsWith(name1Prefix));
441+
}
442+
} else {
443+
if (name1.local().indexOf('.') >= 0) {
444+
DotName name1Prefix = DotName.createSimple(name1.local().substring(0, name1.local().lastIndexOf('.')));
445+
assertTrue(name1.startsWith(name1Prefix));
446+
}
447+
}
448+
}
449+
}
450+
451+
private boolean naiveStartsWith(DotName this_, DotName prefix) {
452+
String thisStr = this_.toString();
453+
String prefixStr = prefix.toString();
454+
if (thisStr.length() < prefixStr.length()) {
455+
return false;
456+
} else if (thisStr.length() == prefixStr.length()) {
457+
return thisStr.equals(prefixStr);
458+
} else {
459+
return thisStr.startsWith(prefixStr) && thisStr.charAt(prefixStr.length()) == '.';
460+
}
461+
}
462+
366463
@Test
367464
public void scalaAnonfunCurriedCase1() {
368465
DotName a;

0 commit comments

Comments
 (0)