Skip to content

Commit 332fe2b

Browse files
2-pass operation for direct attribute constructors: xmlns first, then non-xmlns
1 parent 0ca71e0 commit 332fe2b

4 files changed

Lines changed: 1209 additions & 85 deletions

File tree

basex-core/.checkstyle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</fileset>
1010
<filter name="FilesFromPackage" enabled="true">
1111
<filter-data value="src/main/resources"/>
12+
<filter-data value="src/main/java/org/basex/query/util/rex/"/>
1213
<filter-data value="src/main/java/org/basex/query/util/regex/parse"/>
1314
<filter-data value="src/test/resources"/>
1415
</filter>

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

Lines changed: 116 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.basex.query.util.hash.*;
3737
import org.basex.query.util.list.*;
3838
import org.basex.query.util.parse.*;
39+
import org.basex.query.util.rex.*;
3940
import org.basex.query.value.array.*;
4041
import org.basex.query.value.item.*;
4142
import org.basex.query.value.seq.*;
@@ -110,6 +111,8 @@ public class QueryParser extends InputParser {
110111
private QueryError alter;
111112
/** Alternative position. */
112113
private int alterPos;
114+
/** Attribute value scanner. */
115+
private AttributeValueScanner attributeValueScanner;
113116

114117
/**
115118
* Constructor.
@@ -3098,102 +3101,121 @@ private Expr dirElement(final boolean root) throws QueryException {
30983101
// parse attributes
30993102
boolean xmlDecl = false; // xml prefix explicitly declared?
31003103
ArrayList<QNm> atts = null;
3101-
while(true) {
3102-
final byte[] atn = qName(null);
3103-
if(atn.length == 0) break;
3104-
3105-
final ExprList attv = new ExprList();
3106-
consumeWS();
3107-
if(root) {
3108-
if(!consume('=')) return null;
3109-
} else {
3110-
check('=');
3111-
}
3112-
consumeWS();
3113-
final int delim = consume();
3114-
if(!quote(delim)) throw error(NOQUOTE_X, found());
3115-
final TokenBuilder tb = new TokenBuilder();
31163104

3117-
boolean simple = true;
3105+
// remember start of attribute list
3106+
final int attrPos = pos;
3107+
for(int pass = 0; pass < 2; pass++) {
3108+
final boolean processXmlns = pass == 0;
3109+
pos = attrPos;
31183110
while(true) {
3119-
while(!consume(delim)) {
3120-
cp = current();
3121-
switch(cp) {
3122-
case '{':
3123-
if(next() == '{') {
3124-
tb.add(consume());
3125-
consume();
3126-
} else {
3127-
final byte[] text = tb.next();
3128-
if(text.length == 0) {
3129-
add(attv, enclosedExpr());
3130-
simple = false;
3111+
final byte[] atn = qName(null);
3112+
if(atn.length == 0) break;
3113+
consumeWS();
3114+
if(root) {
3115+
if(!consume('=')) return null;
3116+
} else {
3117+
check('=');
3118+
}
3119+
consumeWS();
3120+
3121+
final int len = attributeValueScanner().length(pos);
3122+
final boolean hasXmlnsPrefix = startsWith(atn, XMLNS_COLON);
3123+
final boolean isXmlns = hasXmlnsPrefix || eq(atn, XMLNS);
3124+
if(processXmlns != isXmlns) {
3125+
if(len >= 0) {
3126+
pos += len;
3127+
consumeWS();
3128+
continue;
3129+
}
3130+
Util.debugln("Failed to detect attribute value length: "
3131+
+ new String(input, pos, Math.min(20, input.length - pos)) + "...");
3132+
}
3133+
3134+
final ExprList attv = new ExprList();
3135+
final int delim = consume();
3136+
if(!quote(delim)) throw error(NOQUOTE_X, found());
3137+
final TokenBuilder tb = new TokenBuilder();
3138+
3139+
boolean simple = true;
3140+
while(true) {
3141+
while(!consume(delim)) {
3142+
cp = current();
3143+
switch(cp) {
3144+
case '{':
3145+
if(next() == '{') {
3146+
tb.add(consume());
3147+
consume();
31313148
} else {
3132-
add(attv, Str.get(text));
3149+
final byte[] text = tb.next();
3150+
if(text.length == 0) {
3151+
add(attv, enclosedExpr());
3152+
simple = false;
3153+
} else {
3154+
add(attv, Str.get(text));
3155+
}
31333156
}
3134-
}
3135-
break;
3136-
case '}':
3137-
consume();
3138-
check('}');
3139-
tb.add('}');
3140-
break;
3141-
case '<':
3142-
case 0:
3143-
throw error(NOQUOTE_X, found());
3144-
case '\n':
3145-
case '\t':
3146-
tb.add(' ');
3147-
consume();
3148-
break;
3149-
case '\r':
3150-
if(next() != '\n') tb.add(' ');
3151-
consume();
3152-
break;
3153-
default:
3154-
entity(tb);
3155-
break;
3157+
break;
3158+
case '}':
3159+
consume();
3160+
check('}');
3161+
tb.add('}');
3162+
break;
3163+
case '<':
3164+
case 0:
3165+
throw error(NOQUOTE_X, found());
3166+
case '\n':
3167+
case '\t':
3168+
tb.add(' ');
3169+
consume();
3170+
break;
3171+
case '\r':
3172+
if(next() != '\n') tb.add(' ');
3173+
consume();
3174+
break;
3175+
default:
3176+
entity(tb);
3177+
break;
3178+
}
31563179
}
3180+
if(!consume(delim)) break;
3181+
tb.add(delim);
31573182
}
3158-
if(!consume(delim)) break;
3159-
tb.add(delim);
3160-
}
31613183

3162-
if(!tb.isEmpty()) add(attv, Str.get(tb.finish()));
3163-
3164-
// parse namespace declarations
3165-
final boolean pr = startsWith(atn, XMLNS_COLON);
3166-
if(pr || eq(atn, XMLNS)) {
3167-
if(!simple) throw error(NSCONS);
3168-
final byte[] prefix = pr ? local(atn) : EMPTY;
3169-
final byte[] uri = attv.isEmpty() ? EMPTY : ((Str) attv.get(0)).string();
3170-
if(eq(prefix, XML) && eq(uri, XML_URI)) {
3171-
if(xmlDecl) throw error(DUPLNSDEF_X, XML);
3172-
xmlDecl = true;
3173-
} else {
3174-
if(!Uri.get(uri).isValid()) throw error(INVURI_X, uri);
3175-
if(pr) {
3176-
if(uri.length == 0) throw error(NSEMPTYURI);
3177-
if(eq(prefix, XML, XMLNS)) throw error(BINDXML_X, prefix);
3178-
if(eq(uri, XML_URI)) throw error(BINDXMLURI_X_X, uri, XML);
3179-
if(eq(uri, XMLNS_URI)) throw error(BINDXMLURI_X_X, uri, XMLNS);
3180-
qc.ns.add(prefix, uri);
3184+
if(!tb.isEmpty()) add(attv, Str.get(tb.finish()));
3185+
3186+
// parse namespace declarations
3187+
if(isXmlns) {
3188+
if(!simple) throw error(NSCONS);
3189+
final byte[] prefix = hasXmlnsPrefix ? local(atn) : EMPTY;
3190+
final byte[] uri = attv.isEmpty() ? EMPTY : ((Str) attv.get(0)).string();
3191+
if(eq(prefix, XML) && eq(uri, XML_URI)) {
3192+
if(xmlDecl) throw error(DUPLNSDEF_X, XML);
3193+
xmlDecl = true;
31813194
} else {
3182-
if(eq(uri, XML_URI)) throw error(XMLNSDEF_X, uri);
3183-
sc.dirNS = uri;
3184-
if(!sc.elemNsFixed) sc.elemNS = sc.dirNS;
3195+
if(!Uri.get(uri).isValid()) throw error(INVURI_X, uri);
3196+
if(hasXmlnsPrefix) {
3197+
if(uri.length == 0) throw error(NSEMPTYURI);
3198+
if(eq(prefix, XML, XMLNS)) throw error(BINDXML_X, prefix);
3199+
if(eq(uri, XML_URI)) throw error(BINDXMLURI_X_X, uri, XML);
3200+
if(eq(uri, XMLNS_URI)) throw error(BINDXMLURI_X_X, uri, XMLNS);
3201+
qc.ns.add(prefix, uri);
3202+
} else {
3203+
if(eq(uri, XML_URI)) throw error(XMLNSDEF_X, uri);
3204+
sc.dirNS = uri;
3205+
if(!sc.elemNsFixed) sc.elemNS = sc.dirNS;
3206+
}
3207+
if(ns.contains(prefix)) throw error(DUPLNSDEF_X, prefix);
3208+
ns.add(prefix, uri);
31853209
}
3186-
if(ns.contains(prefix)) throw error(DUPLNSDEF_X, prefix);
3187-
ns.add(prefix, uri);
3210+
} else {
3211+
final QNm attn = new QNm(atn);
3212+
if(atts == null) atts = new ArrayList<>(1);
3213+
atts.add(attn);
3214+
qnames.add(attn, false, info());
3215+
add(cont, new CAttr(info(), false, attn, attv.finish()));
31883216
}
3189-
} else {
3190-
final QNm attn = new QNm(atn);
3191-
if(atts == null) atts = new ArrayList<>(1);
3192-
atts.add(attn);
3193-
qnames.add(attn, false, info());
3194-
add(cont, new CAttr(info(), false, attn, attv.finish()));
3217+
if(!consumeWS()) break;
31953218
}
3196-
if(!consumeWS()) break;
31973219
}
31983220

31993221
if(consume('/')) {
@@ -3230,6 +3252,15 @@ private Expr dirElement(final boolean root) throws QueryException {
32303252
return new CElem(info(), false, name, ns, cont.finish());
32313253
}
32323254

3255+
/**
3256+
* Returns the attribute value scanner.
3257+
* @return attribute value scanner
3258+
*/
3259+
private AttributeValueScanner attributeValueScanner() {
3260+
if(attributeValueScanner == null) attributeValueScanner = new AttributeValueScanner(input);
3261+
return attributeValueScanner;
3262+
}
3263+
32333264
/**
32343265
* Parses the "DirElemContent" rule.
32353266
* @param name name of opening element

0 commit comments

Comments
 (0)