Skip to content

Commit 7c6cba4

Browse files
committed
feat read DOCTYPE NOTATION exp
1 parent 7589705 commit 7c6cba4

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

spec/entities_spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ describe("XMLParser Entities", function() {
247247
<!DOCTYPE code [
248248
<!ELEMENT code (#PCDATA)>
249249
<!NOTATION vrml PUBLIC "VRML 1.0">
250+
<!NOTATION vrml2 PUBLIC "VRML 1.0" "system">
250251
<!ATTLIST code lang NOTATION (vrml) #REQUIRED>
251252
]>
252253
<code lang="vrml">Some VRML instructions</code>`;

src/xmlparser/DocTypeReader.js

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ export default function readDocType(xmlData, i){
2929
}
3030
else if( hasBody && isElement(xmlData, i)) i += 8;//Not supported
3131
else if( hasBody && isAttlist(xmlData, i)) i += 8;//Not supported
32-
else if( hasBody && isNotation(xmlData, i)) i += 9;//Not supported
33-
else if( isComment) comment = true;
32+
else if( hasBody && isNotation(xmlData, i)) {
33+
i += 9;//Not supported
34+
const {name, publicIdentifier, systemIdentifier,index} = readNotationExp(xmlData,i+1);
35+
i = index;
36+
}else if( isComment) comment = true;
3437
else throw new Error("Invalid DOCTYPE");
3538

3639
angleBracketsCount++;
@@ -124,6 +127,80 @@ function readEntityExp(xmlData, i) {
124127
return [entityName, entityValue, i ];
125128
}
126129

130+
function readNotationExp(xmlData, i) {
131+
// Skip leading whitespace after <!NOTATION
132+
i = skipWhitespace(xmlData, i);
133+
134+
// Read notation name
135+
let notationName = "";
136+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
137+
notationName += xmlData[i];
138+
i++;
139+
}
140+
141+
// Validate notation name
142+
if (!validateEntityName(notationName)) {
143+
throw new Error(`Invalid notation name: "${notationName}"`);
144+
}
145+
146+
// Skip whitespace after notation name
147+
i = skipWhitespace(xmlData, i);
148+
149+
// Check identifier type (SYSTEM or PUBLIC)
150+
const identifierType = xmlData.substring(i, i + 6).toUpperCase();
151+
if (identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
152+
throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
153+
}
154+
i += identifierType.length;
155+
156+
// Skip whitespace after identifier type
157+
i = skipWhitespace(xmlData, i);
158+
159+
// Read public identifier (if PUBLIC)
160+
let publicIdentifier = null;
161+
let systemIdentifier = null;
162+
163+
if (identifierType === "PUBLIC") {
164+
[i, publicIdentifier ] = readIdentifierVal(xmlData, i);
165+
166+
// Skip whitespace after public identifier
167+
i = skipWhitespace(xmlData, i);
168+
169+
// Optionally read system identifier
170+
if (xmlData[i] === '"' || xmlData[i] === "'") {
171+
[i, systemIdentifier ] = readIdentifierVal(xmlData, i);
172+
}
173+
} else if (identifierType === "SYSTEM") {
174+
// Read system identifier (mandatory for SYSTEM)
175+
[i, systemIdentifier ] = readIdentifierVal(xmlData, i);
176+
177+
if (!systemIdentifier) {
178+
throw new Error("Missing mandatory system identifier for SYSTEM notation");
179+
}
180+
}
181+
182+
return {notationName, publicIdentifier, systemIdentifier, index: --i};
183+
}
184+
185+
function readIdentifierVal(xmlData, i) {
186+
let identifierVal = "";
187+
const startChar = xmlData[i];
188+
if (startChar !== '"' && startChar !== "'") {
189+
throw new Error(`Expected quoted string, found "${startChar}"`);
190+
}
191+
i++;
192+
193+
while (i < xmlData.length && xmlData[i] !== startChar) {
194+
identifierVal += xmlData[i];
195+
i++;
196+
}
197+
198+
if (xmlData[i] !== startChar) {
199+
throw new Error("Unterminated identifier");
200+
}
201+
i++;
202+
return [i, identifierVal];
203+
}
127204

128205
function isComment(xmlData, i){
129206
if(xmlData[i+1] === '!' &&

0 commit comments

Comments
 (0)