Skip to content

Commit c3ea491

Browse files
committed
Replace QualifiedName with ObjectReference
1 parent 00245b0 commit c3ea491

15 files changed

Lines changed: 1009 additions & 259 deletions

src/main/org/firebirdsql/jaybird/util/CollectionUtils.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.ArrayList;
99
import java.util.List;
1010
import java.util.Vector;
11+
import java.util.stream.Stream;
1112

1213
/**
1314
* Helper class for collections
@@ -87,4 +88,30 @@ public static <T> List<T> concat(List<T> list1, List<? extends T> list2) {
8788
return newList;
8889
}
8990

91+
/**
92+
* Concatenates two or more lists to a new modifiable list.
93+
* <p>
94+
* If there are no lists in {@code otherLists}, it will return a new list, with the contents of {@code list1}.
95+
* </p>
96+
*
97+
* @param list1
98+
* list 1
99+
* @param otherLists
100+
* other lists
101+
* @param <T>
102+
* type parameter of {@code list1}, and parent type parameter of lists in {@code otherLists}
103+
* @return concatenation of {@code list1} and {@code otherLists}
104+
* @see #concat(List, List)
105+
*/
106+
@SafeVarargs
107+
public static <T> List<T> concat(List<T> list1, List<? extends T>... otherLists) {
108+
int listsSize = list1.size() + Stream.of(otherLists).mapToInt(List::size).sum();
109+
var newList = new ArrayList<T>(listsSize);
110+
newList.addAll(list1);
111+
for (var list : otherLists) {
112+
newList.addAll(list);
113+
}
114+
return newList;
115+
}
116+
90117
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-FileCopyrightText: Copyright 2025 Mark Rotteveel
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
package org.firebirdsql.jaybird.util;
4+
5+
import org.firebirdsql.jdbc.QuoteStrategy;
6+
import org.jspecify.annotations.Nullable;
7+
8+
import java.util.List;
9+
import java.util.stream.Stream;
10+
11+
import static org.firebirdsql.jaybird.util.StringUtils.trimToNull;
12+
13+
/**
14+
* An identifier is an object reference consisting of a single name.
15+
*
16+
* @since 7
17+
* @see ObjectReference
18+
*/
19+
public final class Identifier extends ObjectReference {
20+
21+
private final String name;
22+
23+
public Identifier(String name) {
24+
name = trimToNull(name);
25+
if (name == null) {
26+
throw new IllegalArgumentException("name cannot be null, empty, or blank");
27+
}
28+
this.name = name;
29+
}
30+
31+
public String name() {
32+
return name;
33+
}
34+
35+
@Override
36+
public int size() {
37+
return 1;
38+
}
39+
40+
@Override
41+
public Identifier at(int index) {
42+
if (index != 0) {
43+
throw new IndexOutOfBoundsException(index);
44+
}
45+
return this;
46+
}
47+
48+
@Override
49+
public Identifier first() {
50+
return this;
51+
}
52+
53+
@Override
54+
public Identifier last() {
55+
return this;
56+
}
57+
58+
/**
59+
* The name, quoted using {@code quoteStrategy}.
60+
*
61+
* @param quoteStrategy
62+
* quote strategy
63+
* @return name, possibly quoted
64+
*/
65+
public String toString(QuoteStrategy quoteStrategy) {
66+
return quoteStrategy.quoteObjectName(name);
67+
}
68+
69+
/**
70+
* Appends name to {@code sb} using {@code quoteStrategy}.
71+
*
72+
* @param sb
73+
* string builder to append to
74+
* @param quoteStrategy
75+
* quote strategy
76+
* @return {@code sb} for chaining
77+
*/
78+
public StringBuilder append(StringBuilder sb, QuoteStrategy quoteStrategy) {
79+
return quoteStrategy.appendQuoted(name, sb);
80+
}
81+
82+
@Override
83+
public Stream<Identifier> stream() {
84+
return Stream.of(this);
85+
}
86+
87+
@Override
88+
public List<Identifier> toList() {
89+
return List.of(this);
90+
}
91+
92+
@Override
93+
public boolean equals(@Nullable Object obj) {
94+
if (obj instanceof Identifier other) {
95+
return name.equals(other.name);
96+
} else if (obj instanceof ObjectReference otherRef) {
97+
// We're using ObjectReference, not IdentifierChain, so it'll also work for future subclasses, if any
98+
return otherRef.size() == 1 && name.equals(otherRef.at(0).name);
99+
}
100+
return false;
101+
}
102+
103+
@Override
104+
public int hashCode() {
105+
// This needs to be consistent with IdentifierChain.hashCode (as if this is a chain with a single item)
106+
// We're clearing the sign bit, because that is what IdentifierChain does to avoid negative values
107+
return (31 + name.hashCode()) & 0x7FFF_FFFF;
108+
}
109+
110+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// SPDX-FileCopyrightText: Copyright 2025 Mark Rotteveel
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
package org.firebirdsql.jaybird.util;
4+
5+
import org.firebirdsql.jdbc.QuoteStrategy;
6+
7+
import java.util.List;
8+
import java.util.stream.Stream;
9+
10+
/**
11+
* An identifier chain is an object reference consisting of one or more identifiers.
12+
* <p>
13+
* In practice, we'll use {@link Identifier} if there is only one identifier.
14+
* </p>
15+
* <p>
16+
* The recommended way to create this object is through {@link ObjectReference#of(String...)} or
17+
* {@link ObjectReference#of(List)}.
18+
* </p>
19+
*
20+
* @since 7
21+
* @see ObjectReference
22+
*/
23+
final class IdentifierChain extends ObjectReference {
24+
25+
private final List<Identifier> identifiers;
26+
// cached hashcode, -1 signals not yet cached
27+
private int hashCode = -1;
28+
29+
IdentifierChain(List<Identifier> identifiers) {
30+
if (identifiers.isEmpty()) {
31+
throw new IllegalArgumentException("identifier chain cannot be empty");
32+
}
33+
this.identifiers = List.copyOf(identifiers);
34+
}
35+
36+
@Override
37+
public int size() {
38+
return identifiers.size();
39+
}
40+
41+
@Override
42+
public Identifier at(int index) {
43+
return identifiers.get(index);
44+
}
45+
46+
@Override
47+
public String toString(QuoteStrategy quoteStrategy) {
48+
// Estimate 16 characters per element (including quotes and separator)
49+
return append(new StringBuilder(size() * 16), quoteStrategy).toString();
50+
}
51+
52+
@Override
53+
public StringBuilder append(StringBuilder sb, QuoteStrategy quoteStrategy) {
54+
for (Identifier identifier : identifiers) {
55+
identifier.append(sb, quoteStrategy).append('.');
56+
}
57+
// Remove last dot separator
58+
sb.setLength(sb.length() - 1);
59+
return sb;
60+
}
61+
62+
@Override
63+
public Stream<Identifier> stream() {
64+
return identifiers.stream();
65+
}
66+
67+
@Override
68+
public List<Identifier> toList() {
69+
return identifiers;
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
int hashCode = this.hashCode;
75+
return hashCode != -1 ? hashCode : hashCode0();
76+
}
77+
78+
private int hashCode0() {
79+
// This needs to be consistent with Identifier.hashCode for an instance with a single Identifier
80+
int hashCode = 1;
81+
for (Identifier identifier : identifiers) {
82+
hashCode = 31 * hashCode + identifier.name().hashCode();
83+
}
84+
// Clear sign bit to avoid -1 (and any other negative value)
85+
return this.hashCode = hashCode & 0x7FFF_FFFF;
86+
}
87+
88+
}

0 commit comments

Comments
 (0)