Skip to content

Commit ffaa91f

Browse files
implement subtyping rules for choice item types (#2620)
1 parent 96e0872 commit ffaa91f

11 files changed

Lines changed: 151 additions & 130 deletions

File tree

basex-core/src/main/java/org/basex/io/parse/csv/CsvW3Converter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ private static FuncItem funcItem(final Value rows, final XQMap columnIndex,
121121
final QueryContext qc, final InputInfo ii) {
122122
final VarScope vs = new VarScope();
123123
final SeqType rowType = POSITIVE_INTEGER_O;
124-
final SeqType colType = ChoiceItemType.get(STRING_O, POSITIVE_INTEGER_O).seqType();
124+
final SeqType colType = ChoiceItemType.get(BasicType.STRING,
125+
BasicType.POSITIVE_INTEGER).seqType();
125126
final Var row = vs.addNew(new QNm("row"), rowType, qc, ii);
126127
final Var col = vs.addNew(new QNm("column"), colType, qc, ii);
127128
final Get get = new Get(ii, rows, columnIndex, new VarRef(ii, row), new VarRef(ii, col));

basex-core/src/main/java/org/basex/query/QueryParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3805,7 +3805,7 @@ private EnumType enumerationType() throws QueryException {
38053805
private SeqType choiceItemType() throws QueryException {
38063806
final ChoiceItemType.Builder builder = new ChoiceItemType.Builder();
38073807
do {
3808-
builder.add(itemType());
3808+
builder.add(itemType().type);
38093809
} while(wsConsume("|"));
38103810
check(')');
38113811
return builder.build().seqType();

basex-core/src/main/java/org/basex/query/func/Function.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public enum Function implements AFunction {
238238
params(ELEMENT_ZO, MAP_ZO), ITEM_ZO.mapType(BasicType.STRING).seqType(Occ.ZERO_OR_ONE)),
239239
/** XQuery function. */
240240
ELEMENT_TO_MAP_PLAN(FnElementToMapPlan::new, "element-to-map-plan(input)",
241-
params(ChoiceItemType.get(DOCUMENT_O, ELEMENT_O).seqType(Occ.ZERO_OR_MORE)),
241+
params(ChoiceItemType.get(NodeType.DOCUMENT, NodeType.ELEMENT).seqType(Occ.ZERO_OR_MORE)),
242242
RECORD_O.mapType(BasicType.STRING).seqType()),
243243
/** XQuery function. */
244244
ELEMENT_WITH_ID(FnElementWithId::new, "element-with-id(values[,node])",
@@ -385,7 +385,7 @@ public enum Function implements AFunction {
385385
/** XQuery function. */
386386
IN_SCOPE_NAMESPACES(FnInScopeNamespaces::new, "in-scope-namespaces(element)",
387387
params(ELEMENT_O),
388-
ANY_URI_O.mapType(ChoiceItemType.get(NCNAME_O, EnumType.get("").seqType())).seqType()),
388+
ANY_URI_O.mapType(ChoiceItemType.get(BasicType.NCNAME, EnumType.get(""))).seqType()),
389389
/** XQuery function. */
390390
IN_SCOPE_PREFIXES(FnInScopePrefixes::new, "in-scope-prefixes(element)",
391391
params(ELEMENT_O), STRING_ZM),
@@ -511,7 +511,7 @@ public enum Function implements AFunction {
511511
params(NODE_ZO), QNAME_ZO),
512512
/** XQuery function. */
513513
NODE_TYPE_ANNOTATION(FnNodeTypeAnnotation::new, "node-type-annotation(node)",
514-
params(ChoiceItemType.get(ELEMENT_O, ATTRIBUTE_O).seqType()),
514+
params(ChoiceItemType.get(NodeType.ELEMENT, NodeType.ATTRIBUTE).seqType()),
515515
Records.SCHEMA_TYPE.get().seqType()),
516516
/** XQuery function. */
517517
NORMALIZE_SPACE(FnNormalizeSpace::new, "normalize-space([value])",

basex-core/src/main/java/org/basex/query/func/Records.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public enum Records {
5050
field("column-index", MapType.get(BasicType.STRING, Types.INTEGER_O).seqType(Occ.ZERO_OR_ONE)),
5151
field("rows", ArrayType.get(Types.STRING_O).seqType(Occ.ZERO_OR_MORE)),
5252
field("get", FuncType.get(Types.STRING_O, Types.POSITIVE_INTEGER_O,
53-
ChoiceItemType.get(Types.POSITIVE_INTEGER_O, Types.STRING_O).seqType()).seqType())),
53+
ChoiceItemType.get(BasicType.POSITIVE_INTEGER, BasicType.STRING).seqType()).seqType())),
5454
/** Record definition. */
5555
RANDOM_NUMBER_GENERATOR(FN_URI, "random-number-generator"),
5656
/** Record definition. */

basex-core/src/main/java/org/basex/query/func/fn/FnInvisibleXml.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
*/
3030
public final class FnInvisibleXml extends StandardFunc {
3131
/** The function's argument type. */
32-
public static final SeqType ARG_TYPE = ChoiceItemType.get(Types.STRING_O,
33-
NodeType.get(NameTest.get(new QNm("ixml"))).seqType()).seqType(Occ.ZERO_OR_ONE);
32+
public static final SeqType ARG_TYPE = ChoiceItemType.get(BasicType.STRING,
33+
NodeType.get(NameTest.get(new QNm("ixml")))).seqType(Occ.ZERO_OR_ONE);
3434
/** The invisible XML parser generator. */
3535
private Generator generator;
3636

basex-core/src/main/java/org/basex/query/func/fn/FnNodeTypeAnnotation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
public final class FnNodeTypeAnnotation extends FnSchemaType {
1717
/** The function's argument type. */
1818
private static final SeqType ARG_TYPE =
19-
ChoiceItemType.get(Types.ELEMENT_O, Types.ATTRIBUTE_O).seqType();
19+
ChoiceItemType.get(NodeType.ELEMENT, NodeType.ATTRIBUTE).seqType();
2020

2121
@Override
2222
public Value value(final QueryContext qc) throws QueryException {

basex-core/src/main/java/org/basex/query/value/type/ChoiceItemType.java

Lines changed: 77 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44

55
import java.util.*;
66

7-
import org.basex.io.in.DataInput;
7+
import org.basex.io.in.*;
88
import org.basex.query.*;
9-
import org.basex.query.expr.path.*;
109
import org.basex.query.value.*;
1110
import org.basex.query.value.item.*;
1211
import org.basex.util.*;
@@ -19,7 +18,7 @@
1918
*/
2019
public final class ChoiceItemType implements Type {
2120
/** Alternative item types. */
22-
public final List<SeqType> types;
21+
public final List<Type> types;
2322
/** Common ancestor type. */
2423
private final Type union;
2524

@@ -30,31 +29,31 @@ public final class ChoiceItemType implements Type {
3029
* Constructor.
3130
* @param types alternative item types
3231
*/
33-
private ChoiceItemType(final List<SeqType> types) {
32+
private ChoiceItemType(final List<Type> types) {
3433
this.types = types;
35-
Type tp = null;
36-
for(final SeqType st : this.types) {
37-
tp = tp == null ? st.type : tp.union(st.type);
34+
Type ut = null;
35+
for(final Type tp : this.types) {
36+
ut = ut == null ? tp : ut.union(tp);
3837
}
39-
union = tp;
38+
union = ut;
4039
}
4140

4241
/**
4342
* Creates a choice item type.
4443
* @param types alternative item types
4544
* @return choice item type
4645
*/
47-
public static Type get(final SeqType... types) {
46+
public static Type get(final Type... types) {
4847
final Builder builder = new Builder();
49-
for(final SeqType st : types) builder.add(st);
48+
for(final Type tp : types) builder.add(tp);
5049
return builder.build();
5150
}
5251

5352
@Override
5453
public Value cast(final Item item, final QueryContext qc, final InputInfo info)
5554
throws QueryException {
56-
for(final SeqType st : types) {
57-
final Value val = st.cast(item, false, qc, info);
55+
for(final Type tp : types) {
56+
final Value val = tp.seqType().cast(item, false, qc, info);
5857
if(val != null) return val;
5958
}
6059
throw FUNCCAST_X_X_X.get(info, item.type, this, item);
@@ -63,9 +62,9 @@ public Value cast(final Item item, final QueryContext qc, final InputInfo info)
6362
@Override
6463
public Value cast(final Object value, final QueryContext qc, final InputInfo info)
6564
throws QueryException {
66-
for(final SeqType st : types) {
65+
for(final Type tp : types) {
6766
try {
68-
return st.type.cast(value, qc, info);
67+
return tp.cast(value, qc, info);
6968
} catch(final QueryException ex) {
7069
Util.debug(ex);
7170
}
@@ -90,25 +89,48 @@ public boolean eq(final Type type) {
9089
return this == type || type instanceof final ChoiceItemType cit && types.equals(cit.types);
9190
}
9291

92+
@Override
93+
public boolean instanceOf(final Type type) {
94+
final Type norm = expand(type);
95+
if(norm instanceof final ChoiceItemType ct) {
96+
for(final Type tp : types) {
97+
if(!ct.hasInstance(tp)) return false;
98+
}
99+
} else {
100+
for(final Type tp : types) {
101+
if(!tp.instanceOf(norm)) return false;
102+
}
103+
}
104+
return true;
105+
}
106+
93107
/**
94-
* Checks if this choice item type is an instance of the specified sequence type.
95-
* @param seqType sequence type to check
108+
* Checks if this choice type is a supertype of the given type.
109+
* @param type type to be checked
96110
* @return result of check
97111
*/
98-
public boolean instanceOf(final SeqType seqType) {
99-
if(!seqType.one()) throw Util.notExpected();
100-
for(final SeqType st : types) {
101-
if(!st.instanceOf(seqType)) return false;
112+
boolean hasInstance(final Type type) {
113+
final Type norm = expand(type);
114+
if(norm instanceof final ChoiceItemType ct) return ct.instanceOf(this);
115+
for(final Type tp : types) {
116+
if(norm.instanceOf(tp)) return true;
102117
}
103-
return true;
118+
return false;
104119
}
105120

106-
@Override
107-
public boolean instanceOf(final Type type) {
108-
for(final SeqType st : types) {
109-
if(!st.type.instanceOf(type)) return false;
110-
}
111-
return true;
121+
/**
122+
* Expand the given type for comparison with choice item types, if necessary, as specified in the
123+
* subtyping rules for choice item types.
124+
* @param type type to expand
125+
* @return expanded type
126+
*/
127+
private static Type expand(final Type type) {
128+
if(type == BasicType.NUMERIC) return Types.NUMERIC_EXPANSION;
129+
if(type == BasicType.ANY_ATOMIC_TYPE) return Types.ANY_ATOMIC_TYPE_EXPANSION;
130+
if(type == BasicType.ITEM) return Types.ITEM_EXPANSION;
131+
if(type == NodeType.NODE) return Types.NODE_EXPANSION;
132+
if(type == NodeType.GNODE) return Types.GNODE_EXPANSION;
133+
return type;
112134
}
113135

114136
@Override
@@ -119,9 +141,9 @@ public Type union(final Type type) {
119141
@Override
120142
public Type intersect(final Type type) {
121143
final Builder builder = new Builder();
122-
for(final SeqType st : types) {
123-
final Type tp = type.intersect(st.type);
124-
if(tp != null) builder.add(tp.seqType());
144+
for(final Type tp : types) {
145+
final Type is = type.intersect(tp);
146+
if(is != null) builder.add(is);
125147
}
126148
return builder.types.isEmpty() ? null : builder.build();
127149
}
@@ -133,7 +155,7 @@ public boolean isNumber() {
133155

134156
@Override
135157
public boolean isUntyped() {
136-
return union.isNumberOrUntyped();
158+
return union.isUntyped();
137159
}
138160

139161
@Override
@@ -163,41 +185,8 @@ public ID id() {
163185

164186
@Override
165187
public boolean nsSensitive() {
166-
for(final SeqType st : types) {
167-
if(st.type.nsSensitive()) return true;
168-
}
169-
return false;
170-
}
171-
172-
/**
173-
* Checks if the given type is an instance of this type.
174-
* @param type type to be checked
175-
* @return result of check
176-
*/
177-
boolean hasInstance(final Type type) {
178-
for(final SeqType st : types) {
179-
if(type.instanceOf(st.type)) return true;
180-
}
181-
return false;
182-
}
183-
184-
/**
185-
* Checks if the given sequence type is an instance of this type.
186-
* @param seqType sequence type to be checked
187-
* @return result of check
188-
*/
189-
boolean hasInstance(final SeqType seqType) {
190-
if(!seqType.one()) throw Util.notExpected();
191-
if(seqType.type instanceof final NodeType nt) {
192-
if(nt.test instanceof final UnionTest ut) {
193-
for(final Test t : ut.tests) {
194-
if(!hasInstance(NodeType.get(t))) return false;
195-
}
196-
return true;
197-
}
198-
}
199-
for(final SeqType st : types) {
200-
if(seqType.instanceOf(st)) return true;
188+
for(final Type tp : types) {
189+
if(tp.nsSensitive()) return true;
201190
}
202191
return false;
203192
}
@@ -219,7 +208,7 @@ public String name() {
219208

220209
@Override
221210
public String toString() {
222-
return toString("|", (Object[]) types.toArray(SeqType[]::new));
211+
return toString("|", (Object[]) types.toArray(Type[]::new));
223212
}
224213

225214
/**
@@ -229,66 +218,66 @@ public String toString() {
229218
*/
230219
public static final class Builder {
231220
/** Alternative item types. */
232-
private final ArrayList<SeqType> types = new ArrayList<>();
221+
private final ArrayList<Type> types = new ArrayList<>();
233222

234223
/**
235224
* Builds and returns the resulting choice item type.
236225
* @return choice item type, or the single item type if the list contains only one type
237226
*/
238227
public Type build() {
239-
if(types.size() != 1) types.remove(Types.ERROR_O);
228+
if(types.size() != 1) types.remove(BasicType.ERROR);
240229
return switch(types.size()) {
241230
case 0 -> throw Util.notExpected();
242-
case 1 -> types.get(0).type;
231+
case 1 -> types.get(0);
243232
default -> new ChoiceItemType(types);
244233
};
245234
}
246235

247236
/**
248-
* Adds the specified sequence type to this builder, merging enum types only if consecutive, and
249-
* merging document, element, attribute, and pi types anywhere in the list, ignoring duplicates,
250-
* and flattening nested ChoiceItemTypes.
251-
* @param st sequence type to be added
237+
* Adds the specified type to this builder, merging enum types only if consecutive, and merging
238+
* document, element, attribute, and pi types anywhere in the list, ignoring duplicates, and
239+
* flattening nested ChoiceItemTypes.
240+
* @param type type to be added
252241
* @return this builder
253242
*/
254-
public Builder add(final SeqType st) {
243+
public Builder add(final Type type) {
255244
// flatten nested ChoiceItemTypes
256-
if(st.type instanceof ChoiceItemType ct) {
257-
for(final SeqType s : ct.types) add(s);
245+
if(type instanceof final ChoiceItemType ct) {
246+
for(final Type tp : ct.types) add(tp);
258247
return this;
259248
}
260249
// ignore duplicates
261-
if(types.contains(st)) return this;
250+
if(types.contains(type)) return this;
262251
// non-mergeable types or first entry
263-
final Type.ID id = st.type.id();
252+
final Type.ID id = type.id();
264253
if(types.isEmpty()
265254
|| !id.oneOf(Type.ID.ENM, Type.ID.DOC, Type.ID.ELM, Type.ID.ATT, Type.ID.PI)) {
266-
types.add(st);
255+
types.add(type);
267256
return this;
268257
}
269258
// enums: merge consecutive entries only, to preserve position of alternatives
270259
if(id == Type.ID.ENM) {
271260
final int last = types.size() - 1;
272-
final Type tp = types.get(last).type;
261+
final Type tp = types.get(last);
273262
if(tp.id() == Type.ID.ENM) {
274263
// remove and re-add, for duplicate removal
275264
types.remove(last);
276-
return add(tp.union(st.type).seqType());
265+
return add(tp.union(type));
277266
}
278-
// not consecutive: add as-is
279-
types.add(st);
267+
// not consecutive: add as-is
268+
types.add(type);
280269
return this;
281270
}
282271
// nodes (DOC/ELM/ATT/PI): merge with existing alternative of same kind anywhere, in-place
283272
for(int i = 0; i < types.size(); i++) {
284-
final SeqType existing = types.get(i);
285-
if(existing.type.id() == id) {
286-
types.set(i, existing.type.union(st.type).seqType());
273+
final Type existing = types.get(i);
274+
if(existing.id() == id) {
275+
types.set(i, existing.union(type));
287276
return this;
288277
}
289278
}
290279
// no existing node kind found
291-
types.add(st);
280+
types.add(type);
292281
return this;
293282
}
294283
}

basex-core/src/main/java/org/basex/query/value/type/RecordType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ private boolean instanceOf(final Type type, final Set<Pair> pairs) {
206206
return true;
207207
}
208208
if(type instanceof final ChoiceItemType cit) {
209-
for(final SeqType st : cit.types) {
210-
if(instanceOf(st.type, pairs)) return true;
209+
for(final Type tp : cit.types) {
210+
if(instanceOf(tp, pairs)) return true;
211211
}
212212
return false;
213213
}

0 commit comments

Comments
 (0)