Skip to content

Commit 1c106f2

Browse files
authored
Merge pull request #2213 from adamretter/hotfix/map-constructor-duplicate-keys
Map Constructor should not allow duplicate keys
2 parents 72624f4 + 07d47c0 commit 1c106f2

File tree

2 files changed

+28
-18
lines changed

2 files changed

+28
-18
lines changed

src/org/exist/xquery/ErrorCodes.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ public class ErrorCodes {
131131
"Bind the prefix xmlns to any namespace URI.\n" +
132132
"Bind a prefix to the namespace URI http://www.w3.org/2000/xmlns/.\n" +
133133
"Bind any prefix (including the empty prefix) to a zero-length namespace URI.");
134-
public static final ErrorCode XQDY0138 = new W3CErrorCode("XQDY0138", "Position n does not exist in this array");
135134
public static final ErrorCode XQDY0102 = new W3CErrorCode("XQDY0102", "If the name of an element in an element constructor is in no namespace, creating a default namespace for that element using a computed namespace constructor is an error.");
135+
public static final ErrorCode XQDY0137 = new W3CErrorCode("XQDY0137", "No two keys in a map may have the same key value");
136+
public static final ErrorCode XQDY0138 = new W3CErrorCode("XQDY0138", "Position n does not exist in this array");
136137

137138
/* XQuery 1.0 and XPath 2.0 Functions and Operators http://www.w3.org/TR/xpath-functions/#error-summary */
138139
public static final ErrorCode FOER0000 = new W3CErrorCode("FOER0000", "Unidentified error.");

src/org/exist/xquery/functions/map/MapExpr.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,22 @@
1515
*/
1616
public class MapExpr extends AbstractExpression {
1717

18-
protected List<Mapping> mappings = new ArrayList(13);
18+
private final List<Mapping> mappings = new ArrayList<>(13);
1919

20-
public MapExpr(XQueryContext context) {
20+
public MapExpr(final XQueryContext context) {
2121
super(context);
2222
}
2323

24-
public void map(PathExpr key, PathExpr value) {
24+
public void map(final PathExpr key, final PathExpr value) {
2525
final Mapping mapping = new Mapping(key.simplify(), value.simplify());
2626
this.mappings.add(mapping);
2727
}
2828

29-
public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
30-
if(getContext().getXQueryVersion() < 30){
29+
@Override
30+
public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException {
31+
if (getContext().getXQueryVersion() < 30) {
3132
throw new XPathException(this, ErrorCodes.EXXQDY0003,
32-
"Map is not available before XQuery 3.0");
33+
"Map is not available before XQuery 3.0");
3334
}
3435
contextInfo.setParent(this);
3536
for (final Mapping mapping : this.mappings) {
@@ -38,35 +39,43 @@ public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
3839
}
3940
}
4041

41-
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
42-
if (contextItem != null)
43-
{contextSequence = contextItem.toSequence();}
42+
@Override
43+
public Sequence eval(Sequence contextSequence, final Item contextItem) throws XPathException {
44+
if (contextItem != null) {
45+
contextSequence = contextItem.toSequence();
46+
}
4447
final MapType map = new MapType(this.context);
4548
for (final Mapping mapping : this.mappings) {
4649
final Sequence key = mapping.key.eval(contextSequence);
47-
if (key.getItemCount() != 1)
48-
{throw new XPathException(MapErrorCode.EXMPDY001, "Expected single value for key, got " + key.getItemCount());}
50+
if (key.getItemCount() != 1) {
51+
throw new XPathException(this, MapErrorCode.EXMPDY001, "Expected single value for key, got " + key.getItemCount());
52+
}
4953
final AtomicValue atomic = key.itemAt(0).atomize();
5054
final Sequence value = mapping.value.eval(contextSequence);
55+
if (map.contains(atomic)) {
56+
throw new XPathException(this, ErrorCodes.XQDY0137, "Key \"" + atomic.getStringValue() + "\" already exists in map.");
57+
}
5158
map.add(atomic, value);
5259
}
5360
return map;
5461
}
5562

63+
@Override
5664
public int returnsType() {
5765
return Type.MAP;
5866
}
5967

6068
@Override
61-
public void accept(ExpressionVisitor visitor) {
69+
public void accept(final ExpressionVisitor visitor) {
6270
super.accept(visitor);
63-
for (final Mapping mapping: this.mappings) {
71+
for (final Mapping mapping : this.mappings) {
6472
mapping.key.accept(visitor);
6573
mapping.value.accept(visitor);
6674
}
6775
}
6876

69-
public void dump(ExpressionDumper dumper) {
77+
@Override
78+
public void dump(final ExpressionDumper dumper) {
7079
dumper.display("map {");
7180
for (final Mapping mapping : this.mappings) {
7281
mapping.key.dump(dumper);
@@ -77,7 +86,7 @@ public void dump(ExpressionDumper dumper) {
7786
}
7887

7988
@Override
80-
public void resetState(boolean postOptimization) {
89+
public void resetState(final boolean postOptimization) {
8190
super.resetState(postOptimization);
8291
mappings.forEach(m -> m.resetState(postOptimization));
8392
}
@@ -86,12 +95,12 @@ private static class Mapping {
8695
final Expression key;
8796
final Expression value;
8897

89-
public Mapping(Expression key, Expression value) {
98+
public Mapping(final Expression key, final Expression value) {
9099
this.key = key;
91100
this.value = value;
92101
}
93102

94-
private void resetState(boolean postOptimization) {
103+
private void resetState(final boolean postOptimization) {
95104
key.resetState(postOptimization);
96105
value.resetState(postOptimization);
97106
}

0 commit comments

Comments
 (0)